> ## 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.

# Deposits

> Move sats and Spark tokens onto Execution

`client.deposit` references one or more existing Spark transfers and credits the resulting EVM balance in a single signed intent. The Spark transfer leg is the caller's responsibility. `client.deposit` does not move funds out of the SparkWallet. The single-intent round-trip pattern that does both legs lives on `TradingClient.swap({ useAvailableBalance: true })`; see [Swaps](/products/execution/swaps).

## Native sats

```typescript theme={null}
import { ExecutionClient } from "@flashnet/sdk";

const execClient = new ExecutionClient(sparkWallet, {
  gatewayUrl,
  rpcUrl,
  chainId,
});
await execClient.authenticate();

// 1. Send the Spark transfer to the gateway's deposit address.
const { spark } = await execClient.getNetworkInfo();
const transfer = await sparkWallet.transfer({
  amountSats: 100_000,
  receiverSparkAddress: spark.depositAddress,
});

// 2. Reference its id in the deposit intent. The gateway requires pure
//    hex (no dashes, no 0x prefix), which is what the Spark SDK returns
//    for tokens. For BTC the Spark SDK returns a UUID. Strip the dashes.
const sparkTransferId = transfer.id.replace(/-/g, "");

const result = await execClient.deposit({
  deposits: [
    { sparkTransferId, amount: 100_000n, asset: { type: "btc" } },
  ],
});

console.log(result.intentId, result.status);
```

The intent stays `ACCEPTED` while the deposit oracle confirms the Spark transfer against the operator DB. Once confirmed and the block finalizes, your EVM-side balance reflects the credit.

The `recipient` field is optional. If omitted, the deposit credits the EVM address derived from the same identity key. Pass an explicit address when you want to fund a different account.

## Spark tokens

Tokens follow the same two-step shape, with two name changes: `transferTokens` instead of `transfer`, and the `tokenId` in the deposit payload is the **32-byte hex** form, not bech32m.

```typescript theme={null}
import { decodeSparkHumanReadableTokenIdentifier } from "@flashnet/sdk";

const { tokenIdentifier: bech32m } = { tokenIdentifier: "btknrt1..." };
const { tokenIdentifier: hexTokenId } = decodeSparkHumanReadableTokenIdentifier(
  bech32m,
  "REGTEST"
);

const { spark } = await execClient.getNetworkInfo();
const sparkTransferId = await sparkWallet.transferTokens({
  tokenIdentifier: bech32m,
  tokenAmount: 1_000_000n,
  receiverSparkAddress: spark.depositAddress,
});

await execClient.deposit({
  deposits: [
    {
      sparkTransferId,
      amount: 1_000_000n,
      asset: { type: "token", tokenId: hexTokenId },
    },
  ],
});
```

If this is the first deposit of that token, the gateway deploys a `SparkToken` contract via CREATE2 and credits the balance to your EVM address. Subsequent deposits hit the same contract.

## Multiple deposits in one intent

The `deposits` array carries any number of assets, each with its own `sparkTransferId`. Submit them together when you want them to land atomically:

```typescript theme={null}
await execClient.deposit({
  deposits: [
    { sparkTransferId: btcTransferId,   amount: 50_000n,    asset: { type: "btc" } },
    { sparkTransferId: tokenTransferId, amount: 1_000_000n, asset: { type: "token", tokenId: hexTokenId } },
  ],
});
```

This is the foundation of the swap-and-withdraw flow that [`TradingClient`](/products/execution/swaps) builds on. The pattern is: deposit → execute action → withdraw, all in one signed intent.

## Status lifecycle

The response includes a `submissionId` and an `intentId`. The intent moves through:

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

A deposit-shaped intent stays `ACCEPTED` while the Spark operator-DB confirms every transfer id. Most flows can wait for `FINALIZED` before reading downstream balances. For low-latency UIs, `INCLUDED_PENDING_FINALITY` is enough to update optimistic state. See [Intents → Status lifecycle](/products/execution/intents#status-lifecycle).

## Constraints

* **Minimum deposit.** The gateway advertises an advisory client-side minimum at `getNetworkInfo().minDepositSats`. The gateway enforces its own bounds at admission, so a deposit below the advertised minimum may still be rejected. Treat the value as a UI hint, not a contract.
* **Sub-sat alignment.** Native sats deposits must be whole-sat amounts. The contract rejects sub-sat dust.
* **Replay protection.** Each Spark transfer id can only be applied once. `SparkGateway.usedTransferIds` records the binding on first use; a second deposit referencing the same id reverts with `TransferAlreadyUsed()`.
* **Expiry.** Intents expire after 15 minutes by default. Set `expiresAt` (unix ms) for a longer window, up to the gateway's 24h cap.
