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

# Testing with Sandbox

Don't rely on Testnet for integration tests. It's slow, you need a faucet, and other people can mess up your state. `near-kit` includes a built-in **Sandbox** manager that runs a local NEAR node for you.

## Setup

We recommend using a test runner like `bun:test`, `jest`, or `vitest`.

```typescript theme={null}
import { Near } from "near-kit"
import { Sandbox } from "near-kit/sandbox"
import { beforeAll, afterAll, test, expect } from "bun:test"

let sandbox: Sandbox
let near: Near

// 1. Start Sandbox
beforeAll(async () => {
  // This downloads a NEAR binary and starts a node locally
  sandbox = await Sandbox.start()

  // near-kit automatically configures the RPC and
  // loads the root account key for you.
  near = new Near({ network: sandbox })
})

// 2. Stop Sandbox
afterAll(async () => {
  if (sandbox) await sandbox.stop()
})

// 3. Write Tests
test("can create account", async () => {
  const newAccount = `test.${sandbox.rootAccount.id}`

  await near
    .transaction(sandbox.rootAccount.id)
    .createAccount(newAccount)
    .send()

  const exists = await near.accountExists(newAccount)
  expect(exists).toBe(true)
})
```

## Pre-Funded Accounts

The sandbox comes with one "Root Account" (`test.near`) that has a massive balance. Use this account to create sub-accounts for your tests.

```typescript theme={null}
const root = sandbox.rootAccount
console.log(root.id) // "test.near"
console.log(root.secretKey) // "ed25519:..."
```

## Patching State

Use `patchState()` to directly modify blockchain state without sending transactions. This is useful for setting up test scenarios quickly — for example, giving an account a specific balance or adding an access key.

```typescript theme={null}
import { EMPTY_CODE_HASH } from "near-kit/sandbox"
import type { StateRecord } from "near-kit/sandbox"

// Create an account by patching state directly
await sandbox.patchState([
  {
    Account: {
      account_id: "alice.test.near",
      account: {
        amount: "5000000000000000000000000", // 5 NEAR in yoctoNEAR
        locked: "0",
        code_hash: EMPTY_CODE_HASH,
        storage_usage: 100,
      },
    },
  },
  {
    AccessKey: {
      account_id: "alice.test.near",
      public_key: "ed25519:...",
      access_key: {
        nonce: 0,
        permission: "FullAccess",
      },
    },
  },
])
```

Each `StateRecord` can be one of:

| Record type | Description                          |
| ----------- | ------------------------------------ |
| `Account`   | Account balance, locked amount, etc. |
| `AccessKey` | Full-access or function-call key     |
| `Contract`  | Deployed WASM code (base64-encoded)  |
| `Data`      | Contract storage key/value pairs     |

## Fast-Forwarding Blocks

Use `fastForward()` to advance the blockchain by producing empty blocks. This is useful for testing time-dependent contract logic (e.g., lockups, vesting schedules) without waiting for real blocks.

```typescript theme={null}
// Advance 100 blocks
await sandbox.fastForward(100)

// Now check time-dependent state
const result = await near.view("lockup.test.near", "is_unlocked")
```

## State Snapshots

Take a snapshot of the entire blockchain state with `dumpState()`, then restore it later with `restoreState()`. This is great for running multiple tests against the same initial state.

```typescript theme={null}
// Set up your test scenario
await deployContracts()
await seedTestData()

// Capture it
const snapshot = await sandbox.dumpState()

test("scenario A", async () => {
  // ... modify state ...
})

test("scenario B", async () => {
  // Restore to the clean state before running
  await sandbox.restoreState(snapshot)
  // ... modify state differently ...
})
```

### Saving Snapshots to Disk

For expensive setup that you don't want to repeat across test runs, save snapshots to disk:

```typescript theme={null}
// First run: save the snapshot
const snapshotPath = await sandbox.saveSnapshot()
console.log(snapshotPath) // "/tmp/.near-sandbox-xxx/snapshots/snapshot-1234.json"

// Later: load and restore it
const snapshot = await sandbox.loadSnapshot(snapshotPath)
await sandbox.restoreState(snapshot)
```

## Restarting the Sandbox

Use `restart()` to stop the sandbox process, clear all data, and start fresh. Block height resets to 0. Optionally pass a snapshot to bake into the genesis state — accounts from the snapshot will exist from block 0.

```typescript theme={null}
// Clean restart
await sandbox.restart()

// Restart with a known state baked into genesis
const snapshot = await sandbox.dumpState()
await sandbox.restart(snapshot)
```

<Tip>
  `restart()` with a snapshot is the most reliable way to reset state. Unlike `restoreState()` which patches on top of existing state, `restart()` merges snapshot records into the genesis file so the sandbox boots with exactly the state you want.
</Tip>

## Custom Binary

By default, `Sandbox.start()` downloads the near-sandbox binary from NEAR's servers. You can use a local binary instead:

```typescript theme={null}
// Option 1: Pass the path directly
const sandbox = await Sandbox.start({
  binaryPath: "/path/to/near-sandbox"
})

// Option 2: Set environment variable
// NEAR_SANDBOX_BIN_PATH=/path/to/near-sandbox
const sandbox = await Sandbox.start()
```

Priority order:

1. `binaryPath` option (if provided)
2. `NEAR_SANDBOX_BIN_PATH` environment variable
3. Download from NEAR's S3 bucket

This is useful for:

* CI environments with pre-cached binaries
* Testing against a custom-built sandbox
* Offline development
