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

# Intents

> The signed unit of work

An intent is the canonical message you sign to do anything on Flashnet Execution. It carries the Spark transfers funding the action, the action itself, and an expiry. The sequencer admits an intent, validators sign the finality certificate, and settlement dispatches results back to Spark. All bound to one signature.

## Shape

```typescript theme={null}
interface CanonicalIntentMessage {
  chainId: number;
  transfers: CanonicalTransferEntry[];
  action:
    | { type: "deposit"; recipient: string }
    | { type: "execute"; signedTxHash: string };
  nonce: string;
  expiresAt: number; // unix milliseconds
}
```

| Field            | What it is                                                                                                                      |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `chainId`        | Execution-chain id, fetched from `getNetworkInfo()`. Pinning this in the signature prevents cross-chain replay.                 |
| `transfers`      | The Spark transfer ids and amounts to credit before the action runs. Empty for execute-only intents.                            |
| `action.deposit` | Mint deposits to `recipient` (an EVM address) and stop.                                                                         |
| `action.execute` | Run a pre-signed EVM transaction. The signed-tx hash is part of the preimage so swapping the tx body invalidates the signature. |
| `nonce`          | Random hex string for uniqueness. The SDK generates one.                                                                        |
| `expiresAt`      | Past this unix-ms timestamp the gateway refuses admission, inclusion, and settlement.                                           |

## How signing works

The SDK serializes the message in canonical camelCase JSON, takes a SHA-256 hash, and signs it with the identity key (DER-encoded secp256k1 ECDSA). The validator's signature check uses the same preimage, so any deviation between client and gateway invalidates the intent.

You don't construct this message by hand. `ExecutionClient.deposit / withdraw / withdrawToken / execute` all build and sign it for you.

## Expiry

The default TTL is 15 minutes. Override per-call with `expiresAt`:

```typescript theme={null}
await execClient.deposit({
  deposits: [...],
  expiresAt: Date.now() + 60 * 60 * 1000, // 1 hour
});
```

The cap is 24 hours. The gateway rejects anything beyond. Choose based on how long you can tolerate the intent sitting between admission and finality:

* **Short TTL** (1-5 min) for interactive UIs where the user should retry if something stalls.
* **Default** (15 min) for batch flows that need to survive transient gateway hiccups.
* **Long** (1-24h) for offline-signed intents you plan to submit later.

## Composition

A single intent can carry many deposits and one execute action. The pattern most flows follow:

```
1. transfers:  Spark sats + Spark token  (multi-asset deposit)
2. action:     execute  (a Conductor swap that consumes both)
3. result:     SparkWithdrawal events  (settlement dispatches to Spark)
```

Atomicity is the point. Either the deposits land and the action runs, or neither does. The output dispatches back to Spark in the same finality round.

The SDK exposes this composition through `TradingClient.swap` (deposit + swap + withdraw) and the lower-level `execClient.execute(...)` (any deposits + any pre-signed tx).

## Status lifecycle

```
ACCEPTED -> INCLUDED_PENDING_FINALITY -> FINALIZED
        \-> EXPIRED
```

Status values are wire-format: uppercase, `SCREAMING_SNAKE_CASE`.

| Status                      | Meaning                                                                                                                                                                                                                                       |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ACCEPTED`                  | Gateway took the intent: signature, expiry, and replay checks passed, and it is queued for sequencer admission. A deposit-shaped intent stays here while the deposit oracle confirms every referenced transfer against the Spark operator DB. |
| `INCLUDED_PENDING_FINALITY` | The sequencer included the intent in a block. The EVM transaction ran successfully. Validators are signing the finality certificate.                                                                                                          |
| `FINALIZED`                 | Validators reached quorum. The action ran and settlement dispatched.                                                                                                                                                                          |
| `EXPIRED`                   | Terminal failure. The `expiresAt` deadline passed, gateway admission failed, the deposit oracle could not verify a transfer, or the EVM transaction reverted. `statusMessage` carries the reason.                                             |

For most flows, wait for `FINALIZED` before reading downstream balances. For optimistic UIs, `INCLUDED_PENDING_FINALITY` is enough.

## Replay protection

Two layers:

1. **Spark transfer ids.** Each Spark transfer can only fund one intent. A second intent referencing the same id is rejected at admission.
2. **EVM nonce.** The signed transaction inside an execute intent has the standard EVM nonce, so the runtime rejects a replayed transaction even if the intent itself reaches the chain twice.

You don't manage either of these directly. The SDK derives the EVM nonce, and Spark transfer ids are unique per transfer.

## Why intents instead of transactions

Three reasons the SDK doesn't expose raw EVM transactions as the unit of work:

1. **Cross-chain atomicity.** Spark deposits and the EVM action commit together. Plain transactions can't reference Spark transfer state.
2. **Settlement dispatch.** Withdrawals need to translate `SparkWithdrawal` events back into Spark transfers. The intent boundary is what the settlement layer reads.
3. **Replay protection across Spark and EVM.** The intent's nonce binds the deposit ids, the EVM transaction, and the expiry into one preimage so a partial replay isn't possible.

If you need raw EVM access (read-only, or for tooling that already speaks JSON-RPC), the gateway exposes a standard RPC endpoint at `rpcUrl`. Writes go through intents.
