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

# Executing transactions

> Call your own contracts through the gateway

`ExecutionClient.execute` runs an arbitrary pre-signed EVM transaction inside an intent. Use this when `TradingClient` doesn't cover what you're doing: calling your own contract, deploying, or interacting with a non-AMM Flashnet contract.

## Sign and submit

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

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

const account = await execClient.getEvmAccount();
const { execution } = await execClient.getNetworkInfo();
const nonce = await fetchNonce(rpcUrl, account.address);

const calldata = encodeFunctionData({
  abi: myContractAbi,
  functionName: "doThing",
  args: [42n],
});

const signedTx = await account.signTransaction({
  chainId: execution.chainId,
  nonce,
  to: myContractAddress,
  value: 0n,
  data: calldata,
  gas: 500_000n,
  maxFeePerGas: 0n,         // zero-gas, see below
  maxPriorityFeePerGas: 0n,
  type: "eip1559",
});

const result = await execClient.execute({ signedTx });
```

`signedTx` is the RLP-encoded EIP-1559 transaction, 0x-prefixed.

## Bundle deposits with execution

Pass `deposits` alongside the signed tx to credit Spark transfers in the same intent that runs the call. Each deposit references an existing Spark transfer by id; the caller is responsible for issuing the Spark-side transfer first (`execute` does not touch the SparkWallet).

```typescript theme={null}
const { spark } = await execClient.getNetworkInfo();
const transfer = await sparkWallet.transfer({
  amountSats: 100_000,
  receiverSparkAddress: spark.depositAddress,
});
const sparkTransferId = transfer.id.replace(/-/g, "");

await execClient.execute({
  deposits: [
    { sparkTransferId, amount: 100_000n, asset: { type: "btc" } },
  ],
  signedTx,
});
```

The intent stays `ACCEPTED` until the deposit oracle confirms every transfer id against the Spark operator DB. Deposits land before the EVM transaction runs, so `doThing` can spend the just-credited balance.

For the single-intent round-trip (Spark → EVM → Spark) without managing the transfer leg yourself, use `TradingClient.swap({ useAvailableBalance: true })`.

## Zero gas

Flashnet runs `baseFeePerGas = 0` and `maxPriorityFeePerGas = 0` for every block. There is no base fee to charge and no priority fee to bid. Set both fee fields on your transaction to `0n`. Viem's `estimateFeesPerGas` defaults will inject non-zero floors that fight you; pass zero explicitly.

`fetchEip1559Fees(rpcUrl)` queries the node and returns the values it currently reports, so a future fee-policy change surfaces here without breaking clients. Note: it ships under the `/execution` subpath of the SDK barrel, not the root.

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

const fees = await fetchEip1559Fees(rpcUrl);
// { maxFeePerGas: 0n, maxPriorityFeePerGas: 0n } on Flashnet today
```

Native value (the `value` field on a transaction) is denominated in 18-decimal wei. One sat is `10_000_000_000` wei (`WEI_PER_SAT`). Setting `value: 100_000n` sends 100,000 wei (a tiny fraction of a sat), not 100,000 sats. Multiply by `WEI_PER_SAT` when you mean sats.

## Decoding reverts

When an execute intent's transaction reverts, the gateway sets status to `EXPIRED` and packs the revert output into `statusMessage`:

```
payload_flagged:stage=Execute outcome=Reverted intent_index=0 \
  tx_hash=0x... reason=reverted:gas_used=189179 output=0x1f2a2005
```

`decodeRevertReason` translates the selector back to a name:

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

const decoded = decodeRevertReason(intent.statusMessage);
console.log(decoded?.name); // "ZeroAmount()"
```

The default lookup table covers `SparkGateway` and `Conductor` errors plus Solidity built-ins. For your own contracts, pass `extraErrors`:

```typescript theme={null}
const decoded = decodeRevertReason(intent.statusMessage, {
  extraErrors: { "0xabcd1234": "MyContractError(uint256)" },
});
```

For a richer view, `traceInnermostRevert` walks the call trace and returns the deepest revert frame:

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

const frame = await traceInnermostRevert(directRpcUrl, txHash);
if (frame) {
  console.log(frame.address, frame.output, frame.revertReason);
}
```

The frame returns `address`, `output` (ABI-encoded revert bytes), `depth`, and optionally `error` and `revertReason`. There is no `input` field.

`traceInnermostRevert` uses `debug_traceTransaction`, which the gateway's RPC proxy explicitly blocks. Point `directRpcUrl` at a full-node RPC that exposes the debug namespace (the localnet sequencer does; production typically uses an internal node). Calling against the gateway URL returns `null`.

## Status lifecycle

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

| Stage                       | What happened                                                                                                                                                                                                                                 |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `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.                                             |

A reverted transaction does not roll back deposits. The Spark transfers funding the intent are still credited to the EVM address; they just don't get consumed by the call. Sweep them back to Spark with `withdraw` or `withdrawToken` if needed.

## When to use this vs TradingClient

| Use `TradingClient`                  | Use `execute`               |
| ------------------------------------ | --------------------------- |
| Trading on Flashnet pools            | Calling your own contract   |
| Deposit + swap + withdraw atomically | Deposit + arbitrary call    |
| Permit2 / EIP-2612 plumbing          | Custom interaction patterns |

The two compose. Most apps run `TradingClient` for trading and `execute` for everything else.
