> For the complete documentation index, see [llms.txt](https://svpchain.gitbook.io/svpchain-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://svpchain.gitbook.io/svpchain-docs/chain/validator-setup.md).

# Validator setup

Run a bonded, block-signing svpchaind validator on testnet.

This page is for third-party developers running a `svpchaind` **validator** on an svpchain network (the testnet `svp-2517-1`). It walks from a clean Linux host to a node that is **bonded and signing blocks in the active validator set**.

{% hint style="info" %}
**Validators are currently permissioned — only partners can join the active set.** To become a partner, email <nova@deltapinglabs.com>. Once you're approved you can follow the steps below to bond and sign blocks.
{% endhint %}

Every step is paste-able and most sections end with a **Verify** block. A validator is just a full node that has additionally submitted a `create-validator` transaction — so the first half of this doc (build → init → configure → sync) is the same work any full node does, and the second half (fund → create-validator → confirm) is what makes it a validator.

{% hint style="danger" %}
**svpchain validators MUST run the Slinky oracle sidecar.** Block proposals carry oracle prices in vote extensions; a validator that is bonded but not running Slinky will produce invalid vote extensions and get jailed. Section 6 is not optional.
{% endhint %}

### Before you start: values to obtain

This doc uses `<PLACEHOLDERS>` for everything network operators must hand you. Collect these first — from the svpchain team or the network's published [Networks](/svpchain-docs/chain/networks.md) page — and keep them next to you:

| Placeholder          | What it is                                                                                                                        |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `<GENESIS_URL>`      | URL to download the network `genesis.json` (or a public RPC `/genesis` endpoint).                                                 |
| `<SEEDS>`            | Comma-separated P2P **seed** nodes, each `nodeid@host:26656`.                                                                     |
| `<PERSISTENT_PEERS>` | Comma-separated P2P **persistent** peers, `nodeid@host:26656` (optional if `<SEEDS>` is given).                                   |
| `<RPC_URL>`          | A public CometBFT RPC endpoint, e.g. `https://rpc-testnet.svpchain.org:443` — used for status checks and (optionally) state-sync. |
| `<FAUCET>`           | How to obtain testnet `asvp` for your operator account (faucet URL or process).                                                   |
| `<MIN_GAS_PRICE>`    | Network-recommended minimum gas price. Defaults to `0asvp` below if you don't have one.                                           |
| `<ETH_RPC_ENDPOINT>` | Ethereum (Sepolia) JSON-RPC for the bridge daemon. Defaults to `https://ethereum-sepolia-rpc.publicnode.com`.                     |

Network constants you do **not** need to ask for (used throughout this doc):

| Thing                      | Value                                       |
| -------------------------- | ------------------------------------------- |
| Binary name                | `svpchaind`                                 |
| Cosmos chain-id            | `svp-2517-1`                                |
| EVM chain-id (EIP-155)     | `2517`                                      |
| Native staking / gas denom | `asvp` (18 decimals — `1 SVP = 10^18 asvp`) |
| Bech32 address prefix      | `svp`                                       |
| Node home directory        | `~/.svpchain`                               |
| Slinky sidecar image       | `ghcr.io/skip-mev/slinky-sidecar:v1.1.0`    |

### 1. Hardware and prerequisites

A trading chain with an in-memory orderbook and EVM execution is RAM- and IO-heavy. Recommended baseline for a validator:

| Resource | Recommended                                                             |
| -------- | ----------------------------------------------------------------------- |
| CPU      | 8+ cores                                                                |
| RAM      | 64 GB                                                                   |
| Disk     | NVMe SSD, 1 TB+ (grows with history; use `--pruning default` to cap it) |
| OS       | Ubuntu 22.04 LTS (x86-64) or equivalent                                 |
| Network  | Stable public IP, ports `26656` (P2P) reachable inbound                 |

Software to install on the host:

```bash
sudo apt-get update
sudo apt-get install -y curl jq git build-essential
```

You also need **Docker** (for the Slinky sidecar) and **Go 1.23.8** (to build the binary). Install Docker per the [official guide](https://docs.docker.com/engine/install/ubuntu/); Go is handled in section 2.

### 2. Build and install the binary

The Go toolchain is pinned to **1.23.8** (`mise.toml`). Build from a release tag, not `main`.

```bash
# Install Go 1.23.8 if you don't have it.
ARCH=$(dpkg --print-architecture)   # amd64 or arm64
curl -fsSL "https://go.dev/dl/go1.23.8.linux-${ARCH}.tar.gz" -o /tmp/go.tgz
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf /tmp/go.tgz
export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin
echo 'export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin' >> ~/.bashrc

# Clone and build. `make install` drops svpchaind in $HOME/go/bin.
git clone https://github.com/svpchain/protocol.git
cd protocol
git checkout <RELEASE_TAG>          # the tag the network is running, e.g. protocol/vX.Y.Z
make install
```

**Verify:**

```bash
svpchaind version
# → a version string (e.g. 9.0.0-...). If "command not found", check $HOME/go/bin is on $PATH.
```

{% hint style="warning" %}
The network runs a specific binary version. Building from the wrong commit will desync you at the first software upgrade height (or immediately, if consensus-breaking). Match `<RELEASE_TAG>` to what the network publishes.
{% endhint %}

### 3. Initialize the node

`init` scaffolds `~/.svpchain/config/` (config.toml, app.toml, node\_key.json, priv\_validator\_key.json) and a default genesis we overwrite in section 4.

```bash
export MONIKER="<your-public-node-name>"
svpchaind init "$MONIKER" --chain-id svp-2517-1
```

**Verify:**

```bash
ls ~/.svpchain/config/
# → app.toml  config.toml  genesis.json  node_key.json  priv_validator_key.json
```

{% hint style="danger" %}
`priv_validator_key.json` **is your validator's block-signing identity.** Two nodes signing with the same key = double-sign = permanent tombstone slashing. Never copy this file to a second running node. Back it up somewhere offline before you go further.
{% endhint %}

### 4. Install the genesis file

Replace the scaffolded genesis with the network's published one.

```bash
curl -fsSL "<GENESIS_URL>" -o ~/.svpchain/config/genesis.json
```

If `<GENESIS_URL>` is a CometBFT RPC `/genesis` endpoint instead of a raw file, extract the inner object:

```bash
curl -fsSL "<RPC_URL>/genesis" | jq '.result.genesis' > ~/.svpchain/config/genesis.json
```

**Verify:**

```bash
svpchaind validate-genesis ~/.svpchain/config/genesis.json
jq -r '.chain_id' ~/.svpchain/config/genesis.json   # → svp-2517-1
```

### 5. Configure the node

Edit two files under `~/.svpchain/config/`. Set the keys shown — leave the rest at their defaults.

#### 5.1 `config.toml` — peers and consensus

```toml
[p2p]
seeds = "<SEEDS>"
persistent_peers = "<PERSISTENT_PEERS>"   # optional; leave "" if you only use seeds
pex = true
addr_book_strict = false                  # peers may advertise non-routable LAN addresses

[consensus]
timeout_commit = "1s"                      # match the network's block cadence
```

If your host is behind NAT, also set your reachable address so peers can dial you back:

```toml
[p2p]
external_address = "<your-public-ip>:26656"
```

#### 5.2 `app.toml` — gas, oracle, gRPC/API, EVM

```toml
# Minimum gas price you accept. 0asvp matches the local/dev default; use the
# network-recommended value if one was given to you.
minimum-gas-prices = "<MIN_GAS_PRICE>"     # default: "0asvp"

# Slinky oracle — REQUIRED. Points at the sidecar you start in section 6 (same host).
[oracle]
enabled = true
oracle_address = "localhost:8090"
metrics_enabled = true
prometheus_server_address = "localhost:8001"

# The Slinky sidecar reads the on-chain market map over REST + gRPC, so both
# must be enabled. localhost binds are fine — the sidecar runs on this host.
[api]
enable = true
address = "tcp://127.0.0.1:1317"

[grpc]
enable = true
address = "127.0.0.1:9090"

# EVM replay-protection chain-id. Must match the network or your node will
# reject EVM transactions the rest of the network accepts.
[evm]
evm-chain-id = 2517
```

The EVM JSON-RPC server (`[json-rpc]`, port `8545`) is **optional** for a validator and can stay at its default (local-only). Only bind it publicly if you intend to serve EVM RPC to others.

**Verify:**

```bash
grep -E '^(minimum-gas-prices|enabled|oracle_address|evm-chain-id)' ~/.svpchain/config/app.toml
grep -E '^(seeds|persistent_peers|pex)' ~/.svpchain/config/config.toml
```

### 6. Run the Slinky oracle sidecar (required)

Slinky is a standalone process that fetches prices and serves them to `svpchaind` on `localhost:8090`. Run it as a Docker container with `--network host` so `localhost` resolves to the validator host. For why these prices are trustworthy (consensus, multi-source feeds, cross-checks), see [DEX oracle (Slinky)](/svpchain-docs/chain/oracle.md).

First, drop the oracle config in place. Use the one from the repo (`testing/testnet-remote/oracle.json`) or write it:

```bash
sudo mkdir -p /etc/slinky
sudo tee /etc/slinky/oracle.json >/dev/null <<'EOF'
{
  "providers": {
    "dydx_migration_api": {
      "name": "dydx_migration_api",
      "api": {
        "enabled": true,
        "timeout": 20000000000,
        "interval": 10000000000,
        "reconnectTimeout": 2000000000,
        "maxQueries": 1,
        "atomic": true,
        "endpoints": [
          { "url": "http://localhost:1317" },
          { "url": "localhost:9090" }
        ],
        "batchSize": 0,
        "name": "dydx_migration_api"
      },
      "type": "market_map_provider"
    }
  }
}
EOF
```

Start the sidecar:

```bash
docker run -d --name slinky --restart unless-stopped --network host \
  -v /etc/slinky/oracle.json:/etc/slinky/oracle.json:ro \
  ghcr.io/skip-mev/slinky-sidecar:v1.1.0 \
  --port 8090 \
  --marketmap-provider dydx_migration_api \
  --oracle-config /etc/slinky/oracle.json \
  --log-std-out-level error
```

**Verify:**

```bash
docker ps --filter name=slinky --format '{{.Status}}'   # → Up ...
```

{% hint style="info" %}
Slinky reads the market map from `svpchaind`'s REST endpoint (`:1317`). On a fresh node that endpoint isn't ready until the chain has committed its first block, so Slinky may log `marketmap not found` until then — restart it with `docker restart slinky` once the node is producing/replaying blocks if it doesn't recover on its own.
{% endhint %}

### 7. Start the node and sync

Run `svpchaind` under systemd so it restarts on crash and on reboot.

```bash
sudo tee /etc/systemd/system/svpchaind.service >/dev/null <<EOF
[Unit]
Description=svpchaind validator
After=network-online.target
Wants=network-online.target

[Service]
User=$USER
ExecStart=$HOME/go/bin/svpchaind start \\
  --home $HOME/.svpchain \\
  --bridge-daemon-eth-rpc-endpoint <ETH_RPC_ENDPOINT> \\
  --max-daemon-unhealthy-seconds 4294967295
Restart=always
RestartSec=5
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now svpchaind
```

Notes on the flags:

* `--bridge-daemon-eth-rpc-endpoint` — the bridge daemon polls Ethereum (Sepolia for testnet) for deposit events. Default: `https://ethereum-sepolia-rpc.publicnode.com`. Point it at your own ETH RPC if you have one.
* `--max-daemon-unhealthy-seconds 4294967295` — by default an unhealthy daemon shuts the node down; this effectively disables that so a transient daemon hiccup doesn't halt block signing.

The node now syncs from genesis by replaying every block. **Watch the logs:**

```bash
sudo journalctl -u svpchaind -f
# → steady "executed block" / "committed state" lines, increasing heights.
```

**Verify sync progress** (run until `catching_up` is `false`):

```bash
curl -s http://localhost:26657/status | jq '.result.sync_info | {height: .latest_block_height, catching_up}'
# → caught up when "catching_up": false and height matches the network tip.
```

Compare your height against the network: `curl -s <RPC_URL>/status | jq -r '.result.sync_info.latest_block_height'`.

{% hint style="warning" %}
**Do not run section 9 (create-validator) until `catching_up` is `false`.** A bonded validator that is still catching up misses blocks and risks downtime jailing.

Syncing from genesis can take a while on an established chain. If the network publishes **state-sync** RPC servers or a **snapshot**, using one is much faster — set `[statesync] enable = true` with two `rpc_servers` (`<RPC_URL>`) plus a recent trust height/hash in `config.toml`, or restore a snapshot into `~/.svpchain/data/`. Ask the network operators which they provide.
{% endhint %}

### 8. Fund your operator account

The operator (delegator) account holds the `asvp` you self-delegate and pays tx fees. It is **separate** from the block-signing key in section 3.

```bash
# Create (or import) your operator key in the test keyring.
svpchaind keys add operator --keyring-backend test
# → prints a svp1... address and a mnemonic. SAVE THE MNEMONIC SECURELY.
```

Fund that `svp1...` address with testnet `asvp` via `<FAUCET>`. You need enough to cover your self-delegation plus tx fees.

**Verify funds arrived:**

```bash
ADDR=$(svpchaind keys show operator -a --keyring-backend test)
svpchaind query bank balances "$ADDR" --node <RPC_URL>
# → a non-zero asvp balance.
```

{% hint style="info" %}
`--keyring-backend test` stores keys unencrypted on disk — fine for a testnet operator account. Use the `os` or `file` backend for anything you care about.
{% endhint %}

### 9. Create the validator

#### 9.1 Get your validator consensus pubkey

This reads `priv_validator_key.json` from section 3:

```bash
svpchaind tendermint show-validator
# → {"@type":"/cosmos.crypto.ed25519.PubKey","key":"...."}
```

#### 9.2 Write `validator.json`

Paste the pubkey from 9.1 into the `pubkey` field below.

```bash
cat > ~/validator.json <<EOF
{
  "pubkey": $(svpchaind tendermint show-validator),
  "amount": "500000000000000000000000asvp",
  "moniker": "$MONIKER",
  "identity": "",
  "website": "",
  "security": "",
  "details": "",
  "commission-rate": "0.10",
  "commission-max-rate": "0.20",
  "commission-max-change-rate": "0.01",
  "min-self-delegation": "1"
}
EOF
```

* `amount` — your initial self-delegation. `500000000000000000000000asvp` is **500,000 SVP** (`500000 × 10^18`), the amount genesis validators self-bond. The real amount you need to *enter* the active set depends on the current set size and the smallest bonded validator's stake — bond enough to outrank the bottom validator if the set is full.
* `min-self-delegation` — the floor (in `asvp`) below which you get jailed. `"1"` is effectively no floor.
* Commission rates are examples; set your own policy.

#### 9.3 Submit the transaction

```bash
svpchaind tx staking create-validator ~/validator.json \
  --from operator \
  --keyring-backend test \
  --chain-id svp-2517-1 \
  --node <RPC_URL> \
  --gas auto --gas-adjustment 1.4 \
  --gas-prices <MIN_GAS_PRICE> \
  --yes
```

(Default `--gas-prices` to `0asvp` if the network uses zero-fee gas.)

**Verify the tx committed:**

```bash
# Use the txhash printed above:
svpchaind query tx <TXHASH> --node <RPC_URL> | jq '{code: .code, raw_log: .raw_log}'
# → "code": 0 means success. Non-zero → read raw_log (common: insufficient funds, bad pubkey).
```

### 10. Confirm you are in the active set

```bash
# Your validator operator address (valoper) is derived from the operator account:
VALOPER=$(svpchaind keys show operator --bech val -a --keyring-backend test)

svpchaind query staking validator "$VALOPER" --node <RPC_URL> \
  | jq '{moniker: .description.moniker, status, jailed, tokens}'
# → status: BOND_STATUS_BONDED, jailed: false once you're in the active set.
```

Cross-check that CometBFT sees you in the set:

```bash
# Your node's CometBFT consensus address:
svpchaind comet show-address                       # → svpvalcons...

# Once bonded, your validator appears with non-zero voting power in the set:
svpchaind query comet-validator-set --node <RPC_URL> | jq '.validators | length'
```

You are a validator once `status` is `BOND_STATUS_BONDED` and `jailed` is `false`, and the node logs show your address proposing/signing blocks. Watch the logs for a few minutes to confirm steady participation:

```bash
sudo journalctl -u svpchaind -f
# → no repeated ERR/WRN; blocks committing every ~1s.
```

That completes the core validator setup. Keep `priv_validator_key.json` and your operator mnemonic backed up offline, and track the network's announced binary upgrade heights so you can upgrade `svpchaind` in lock-step with the rest of the set.

### Next steps

<table data-view="cards"><thead><tr><th>Operate</th><th data-card-target data-type="content-ref">Link</th></tr></thead><tbody><tr><td>Set up metrics and alerting</td><td><a href="/svpchain-docs/chain/monitoring.md">Monitoring</a></td></tr><tr><td>Check chain parameters</td><td><a href="/svpchain-docs/reference/chain-parameters.md">Chain parameters</a></td></tr></tbody></table>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://svpchain.gitbook.io/svpchain-docs/chain/validator-setup.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
