# Quickstart

This guide swaps 0.0005 ETH for USD on Base with a short script on your server. The script requests a firm quote from Circuitry. Then it sends the quote's transactions from your account with [viem](https://viem.sh), and checks the swap until it completes.

Your account signs and pays for each transaction. Circuitry never holds your keys or your funds.

## Before you start

You need these items:

* An app, and an API key for that app. Create both in the [dashboard](https://app.circuitry.company/apps).
* An account on Base that holds more than 0.0005 ETH. The account uses the extra ETH to pay the network fee.
* The account's private key.
* Node.js 22.18 or later, or Bun.

:::warning
Keep the API key and the private key on your server. Never put them in a browser app or in source control.
:::

## 1. Install viem

Make a new folder for the script. Run these commands in the folder:

:::code-group
```bash [npm]
npm init -y
npm pkg set type=module
npm install viem
```

```bash [pnpm]
pnpm init
pnpm pkg set type=module
pnpm add viem
```

```bash [bun]
bun init -y
bun add viem
```
:::

## 2. Set the keys

Put the API key and the private key in two environment variables:

```bash
export CIRCUITRY_API_KEY="ck_live_..."
export PRIVATE_KEY="0x..."
```

## 3. Write the script

Make a file with the name `swap.ts`. Put the four parts below in the file, in this order.

### Connect to the API and to Base

The `circuitry` function sends a request to the Circuitry API with your API key. It returns the response body. If the request fails, the function throws an error with the API's error code and message.

The wallet signs with your account. It sends transactions to Base through Base's public RPC endpoint. For production, give `http` your own RPC URL.

```ts
import type { Hex } from "viem";
import { createWalletClient, http, publicActions } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";

async function circuitry(path: string, body?: object) {
  const response = await fetch(`https://api.circuitry.company/v1${path}`, {
    method: body ? "POST" : "GET",
    headers: {
      Authorization: `Bearer ${process.env.CIRCUITRY_API_KEY}`,
      ...(body ? { "Content-Type": "application/json" } : {}),
    },
    body: body ? JSON.stringify(body) : undefined,
  });
  const result: any = await response.json();
  if (!response.ok) {
    throw new Error(`${result.error.code}: ${result.error.message}`);
  }
  return result;
}

const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
const wallet = createWalletClient({
  account,
  chain: base,
  transport: http(),
}).extend(publicActions);
```

### Create the swap

This part requests a firm quote for your account. The swap spends exactly 0.0005 ETH and receives USD. Circuitry uses the USD token that gives the best result after the network fee. On Base, that token is USDC.

Before Circuitry returns the swap, it simulates the swap's transactions against the latest block. The quote expires 60 seconds after Circuitry creates it, so send the transactions immediately.

```ts
const swap = await circuitry("/swaps", {
  account: account.address,
  chain: "base",
  input: { asset: { type: "crypto", symbol: "ETH" }, amount: "0.0005" },
  output: { asset: { type: "fx", currency: "USD" } },
});

const output = swap.output.asset.token.symbol;
console.log(
  `Swap ${swap.id} spends ${swap.input.amount.value} ETH for at least ${swap.output.minimum.value} ${output}.`,
);
for (const warning of swap.warnings) {
  console.log(`Warning: ${warning.message}`);
}
```

In an app, show each warning to the user before the user signs. For example, `high_price_impact` tells the user that the size of the swap gives a worse rate.

### Send the steps

This part sends the transaction of each step, in order. It waits for the receipt of each transaction before it sends the next one.

This swap has one step, because a swap that spends ETH needs no approval. A swap that spends a token can also have approval steps before the swap step.

```ts
for (const step of swap.steps) {
  const hash = await wallet.sendTransaction({
    to: step.transaction.to,
    data: step.transaction.data,
    value: BigInt(step.transaction.value),
    gas: BigInt(step.transaction.gas),
  });
  const receipt = await wallet.waitForTransactionReceipt({ hash });
  if (receipt.status !== "success") {
    throw new Error(`The ${step.id} step reverted.`);
  }
}
```

### Check the swap

Circuitry finds your account's transactions on the chain every 2 seconds while the quote is live. This part retrieves the swap every 2 seconds until its status is `completed`, `failed`, or `expired`.

```ts
let current = swap;
while (current.status === "quoted" || current.status === "pending") {
  await new Promise((resolve) => setTimeout(resolve, 2000));
  current = await circuitry(`/swaps/${swap.id}`);
}

console.log(`The swap is ${current.status}.`);
if (current.status === "completed") {
  console.log(
    `It spent ${current.input.spent.value} ETH and received ${current.output.received.value} ${output}.`,
  );
}
```

## 4. Run the script

:::code-group
```bash [Node.js]
node swap.ts
```

```bash [Bun]
bun swap.ts
```
:::

The script prints the swap and its result:

```txt
Swap k17aervc9b9sq4ja60esj941ps8gb1r2 spends 0.0005 ETH for at least 1.203611 USDC.
The swap is completed.
It spent 0.0005 ETH and received 1.209641 USDC.
```

If the request fails, the script prints the error code. For example, `insufficient_balance` tells you that the account doesn't hold enough ETH. [Errors](/errors) lists the codes.

## Next steps

* [Price a swap](/reference/swap-price) while the user chooses an amount. A price holds nothing, and prices have a higher rate limit than swaps.
* [Report each transaction](/reference/swap-steps) when you send it. Then the status of the swap changes immediately.
* Set `accountCanBatch` to `true` for a smart account. Then all the calls come back in one step. See [Batched steps](/reference/swaps#batched-steps).
* Make a typed client from the [OpenAPI document](/openapi).
