Nexwest uses typed structured data for signatures. The domain separates mainnet from testnet and prevents a signature from being replayed on another chain.
Domain
| Field | Value |
|---|---|
name | Nexwest |
version | 1 |
chainId | 7777 (mainnet), 7778 (testnet) |
verifyingContract | 0x0000000000000000000000000000000000000000 |
Action
The action is serialized with sorted keys and no whitespace, then hashed with keccak256. The signature is over the typed data:
domain = { name, version, chainId, verifyingContract }
types = { Action: [{ name: "action", type: "string" }] }
message = { action: canonicalJson(action) }Use the SDK
The serialization rules are easy to get subtly wrong. The TypeScript, Python, and Rust SDKs implement them and are tested against the exchange on every release.
Example
import { signAction } from "@nexwest/sdk";
const signature = await signAction({
action: {
type: "order",
orders: [
{
asset: 0,
isBuy: true,
limitPx: "104000",
sz: "0.01",
reduceOnly: false,
tif: "Gtc",
},
],
grouping: "na",
},
nonce: Date.now(),
wallet: apiWallet,
});The result is { r, s, v } where v is 27 or 28.
Phantom agents
Actions signed for a sub-account or vault use a phantom agent address derived from the master and the target. The SDK handles this when you pass vaultAddress. Do not derive the phantom address manually in production.
Verify a signature
from nexwest.signing import recover_signer
signer = recover_signer(action, nonce, signature)
assert signer == expected_api_wallet