npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kashdao/protocol-sdk

v0.1.1

Published

Official non-custodial TypeScript SDK for the Kash prediction-market protocol. Bring your own RPC, signer, and bundler. Native fetch + viem; zero internal dependencies.

Downloads

58

Readme

@kashdao/protocol-sdk

Self-orchestrated, non-custodial TypeScript SDK for the Kash prediction-market protocol.

npm version types license Node 22+ viem

🚀 Mainnet live. Both Base Sepolia (84532, testnet) and Base mainnet (8453) are supported. See Supported chains for the chain registry.

Trust model — non-custodial AND zero Kash dependency. Every Kash SDK and the public API are non-custodial: user funds always live in accounts the user controls, and Kash never holds funds, never moves funds, never holds keys, and never signs anything. What makes this package distinct is that it also has zero Kash dependency: it never reaches Kash-hosted infrastructure. Everything required to use it lives on the consumer's side — their RPC, their signer, (optionally) their bundler, their funded address. See SECURITY.md § Non-custodial design for the formal statement.

Two trading modes — pick one

The SDK supports both wallet models the protocol allows. Pick the one that matches your stack:

| Mode | Factory | Best for | | --------------------------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | EOA (vanilla EIP-1559) | createEoaClient | Market makers with existing tx-signing infra (web3signer, Fireblocks-direct, AWS-KMS, ethers wallet). No bundler. Lowest per-trade overhead. | | Smart Account (ERC-4337 v0.7) | createSmartAccountClient | Self-custody UIs, AI-agent runners, retail-adjacent integrators that benefit from gasless onboarding via paymaster (PaymasterConfig injection), social recovery, or session keys. (Batched UserOps require a custom SimpleAccount variant with executeBatch — not in scope for this SDK.) |

Both modes are first-class peers, share the on-chain read surface, and preserve the non-custodial guarantee (user funds in Privy-managed MPC smart accounts; the consumer controls signing). You can use one or both side-by-side.

// EOA mode
import { createEoaClient, viemAccountEoaSigner } from '@kashdao/protocol-sdk';
const eoa = createEoaClient({ chainId: 84532, rpc, signer: viemAccountEoaSigner(account) });

// Smart-account mode
import { createSmartAccountClient, viemAccountSigner } from '@kashdao/protocol-sdk';
const sa = createSmartAccountClient({
  chainId: 84532,
  rpc,
  signer: viemAccountSigner(account),
  bundler: '...',
});

If you want generic code that handles either, use the TradingClient discriminated union:

import { type TradingClient, isEoaClient } from '@kashdao/protocol-sdk';

function reportClient(client: TradingClient) {
  if (isEoaClient(client)) console.log('EOA at', client.signer.ownerAddress);
  else console.log('SA bundler', client.bundler.entryPointAddress);
}

Supported chains

| Chain | chainId | Status today | Notes | | -------------------------- | --------- | ---------------------------- | ---------------------------------------------- | | Base Sepolia (testnet) | 84532 | ✅ Usable — full surface | Recommended for integration / pre-prod testing | | Base (mainnet) | 8453 | ✅ Usable — full surface | Production deployment |

Programmatic gating:

import {
  isSupportedChainId, // strict: createXxxClient will succeed
  isKnownChainId, // loose: SDK has metadata, may not be deployed yet
  SUPPORTED_CHAIN_IDS, // [84532, 8453]
  KNOWN_CHAIN_IDS, // [84532, 8453]
} from '@kashdao/protocol-sdk';

if (isSupportedChainId(chainId)) renderTradeUI();
else if (isKnownChainId(chainId)) renderComingSoonBanner();
else renderUnsupportedChainError();

Safety guarantee. Any attempt to use a chain that is not in SUPPORTED_CHAIN_IDS (or whose registered factory is the zero address) is a synchronous, typed throw at the factory (both modes), and at the Zod parse layer before that. No funds at risk; no transaction or UserOp can be built, signed, or submitted against an unregistered or undeployed protocol.

Local Anvil / Hardhat / Tenderly forks / sidechains

For chains outside the static registry, pass customChain on the client config — it bypasses the registry guard and uses the addresses you supply verbatim. Mirrors viem's defineChain pattern.

import { defineChain } from 'viem';
import { createSmartAccountClient, viemAccountSigner } from '@kashdao/protocol-sdk';

const client = createSmartAccountClient({
  chainId: 31337,
  rpc: 'http://localhost:8545',
  signer: viemAccountSigner(account),
  customChain: {
    name: 'Anvil',
    viemChain: defineChain({
      id: 31337,
      name: 'Anvil',
      nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
      rpcUrls: { default: { http: ['http://localhost:8545'] } },
    }),
    addresses: { factory: '0x…', usdc: '0x…' /* …rest from your deploy file */ },
    // SA mode only — Anvil's SA factory differs from canonical (see TROUBLESHOOTING.md § AA23)
    smartAccount: {
      factoryAddress: '0x…',
      implementationAddress: '0x…',
      entryPointAddress: '0x…',
    },
  },
});

Read TROUBLESHOOTING.md before running locally — covers the AA23 / EntryPoint deployment / USDC decimals traps that bite every new local-dev integration. Working examples for both modes live in examples/local-anvil/.

When to use this package

| You are… | Mode | Install | | ---------------------------------------------------------------------------------------- | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | A market maker with existing tx-signing infra (HSM, Fireblocks, web3signer, AWS-KMS) | EOA | @kashdao/protocol-sdk | | An AI-agent runner with a viem account or remote signer | EOA or SA | @kashdao/protocol-sdk | | A self-custody UI builder wanting paymaster / batched ops / session keys | SA | @kashdao/protocol-sdk | | A Hummingbot strategy author (or any Python market-making framework) | EOA | kashdao-protocol-sdk (Python — the canonical path; mirrors this SDK byte-for-byte) | | A developer building a retail integration | (use the Kash-orchestrated path instead) | @kashdao/sdk (wraps the public REST API) |

Hummingbot users: install the Python SDK. It exposes the same factories (create_eoa_client, create_smart_account_client), the same trade lifecycle, and the same error hierarchy as this package — same encoding, same chain semantics. See HUMMINGBOT_INTEGRATION.md for a full strategy walkthrough.

Surface

Both client factories return mode-specific types but share the same conceptual surface. The trade lifecycle is exposed at three layers — pick the highest one that fits your control needs:

// Common to both modes
client.mode                    // 'eoa' | 'smart-account' (discriminator)
client.markets.{get, state, quote, watch}              // 100% on-chain via your RPC
client.account.{usdcBalance, usdcAllowance, position, gasBalance}

// trades — ordered shortest-path → most explicit
client.trades.send.{buy, sell, closePosition, approve}     // 1-call: prepare + simulate + sign + submit + wait
client.trades.{prepareBuy, prepareSell, prepareClosePosition, prepareApprove}  // build + estimate gas + estimate fees + recompute hash
client.trades.{buildBuy, buildSell, buildClosePosition, buildApprove}          // unsigned tx/UserOp only
client.trades.{simulate, hashOf, submit}                                       // inspect + raw forward
client.trades.waitForReceipt(hash, { signal?, timeoutMs? })                    // mode-agnostic — works on EOA tx hashes AND UserOp hashes

client.chainId                 // configured chain id (number)
client.signer                  // your signer adapter (mode-specific contract)
client.publicClient            // raw viem PublicClient if you need to escape
client.addresses               // per-chain protocol address registry
client.rpc.health()            // chain-RPC liveness probe (returns { ok, chainId, latencyMs })
client.rpc.estimateFees(opts?) // EIP-1559 fee estimator — symmetric across both modes
                               //   defaultFees / aggressiveFees / replacementFees presets exported separately

// Smart-account mode only
client.bundler                 // ERC-4337 v0.7 RPC client (your bundler)
client.bundler.health()        // bundler liveness probe — symmetric with client.rpc.health()
client.account.computeAddress  // derive deterministic SA address
client.account.isDeployed      // check on-chain deployment status
client.account.ensureDeployed  // returns { deployed, address } — surface "first trade includes setup" hint to UIs

// EOA mode only — no bundler / no SA derivation; the EOA address IS the signer

SA-mode params.account validation. Every trades.{build,prepare,send}.* call validates that params.account === computeAddress(signer.ownerAddress, options.salt ?? 0n). Catches the most common SA-mode footgun (pasting the EOA owner instead of the derived SA). If you run multiple SAs per EOA via different salts, pass the matching options.salt to point the validator at the right derivation; otherwise it throws KashConfigError(SA_ACCOUNT_MISMATCH). EOA mode does the same check synchronously (params.account === signer.ownerAddress) via the *_ACCOUNT_MISMATCH error codes.

Cross-market discovery (markets.list) and historical trade aggregation (markets.predictions) are not in this package — neither is feasible on-chain at scale, and including them would introduce a Kash-API dependency that breaks the zero-Kash-dependency guarantee this package makes (the non-custodial guarantee is preserved either way). If you need them, install @kashdao/sdk alongside; it wraps the public REST API.

Outcome labels ("YES", "NO", candidate names) are off-chain — they live in market metadata and are not on the Market contract. state.outcomes is positional: index 0 is "the first outcome" and consumers either know the position-to-label mapping out-of-band, hard-code it for boolean markets, or fetch it from @kashdao/sdk's markets.get(id).outcomeLabels. Same applies to quote.outcomeIndex and BuildBuyParams.outcome.

Reference signer adapters:

EOA mode — implements signTransaction(tx) → signedSerializedTx:

  • viemAccountEoaSigner(account) — wraps a viem Account (privateKeyToAccount, mnemonicToAccount, etc.).
  • jsonRpcEoaSigner({ rpc, address }) — calls remote eth_signTransaction over consumer-provided RPC. Suitable for web3signer, Frame, MetaMask-style providers, Fireblocks-direct, AWS-KMS via wrappers.

Smart-account mode — implements signUserOpHash(hash) → sig (and optional signTypedDataV4):

  • viemAccountSigner(account) — wraps a viem Account.
  • jsonRpcSigner({ rpc, address }) — calls remote personal_sign / eth_signTypedData_v4.

For HSM / Fireblocks / AWS-KMS / proprietary signers, implement the appropriate EoaSignerAdapter or SmartAccountSignerAdapter interface yourself. Worked examples (copy into your repo and adapt the imports) live at examples/signers/:

  • aws-kms-signer.ts — AWS KMS raw signing
  • fireblocks-signer.ts — Fireblocks for smart-account mode
  • fireblocks-eoa-signer.ts — Fireblocks for EOA mode
  • privy-signer.ts — Privy: server-side adapter + browser-side viemAccountSigner pattern (Privy's embedded wallet exposes a viem Account, so the built-in adapter is enough — no custom code needed for the React/browser path)
  • coinbase-smart-wallet-signer.ts — Coinbase Smart Wallet integration patterns
  • web3signer-signer.ts — Consensys web3signer over JSON-RPC

Bundler clients (smart-account mode only — bundler is required for any trades.* or bundler.* call but optional at construction time, so read-only markets.* / account.* consumers can build the client without one. The first trade attempt without a bundler throws KashConfigError(BUNDLER_REQUIRED). No default URL — bundlers are chain-scoped and a one-size-fits-all default would silently reject UserOps for the chain id you configured):

  • createFlashbotsBundlerClient({ url }) — Ethereum mainnet only as of writing (the canonical Flashbots Protect URL returns chain id 0x1); pass an explicit URL.
  • createPimlicoBundlerClient({ url, apiKey }) — supports Base + Base Sepolia + others.
  • createAlchemyBundlerClient({ url, apiKey }) — supports Base + Base Sepolia + others.
  • createGenericBundlerClient({ url }) — any compliant ERC-4337 v0.7 bundler RPC.

Subpath imports for tree-shaken bundles:

// Smart-account mode
import { createPimlicoBundlerClient } from '@kashdao/protocol-sdk/bundler/pimlico';
import { jsonRpcSigner } from '@kashdao/protocol-sdk/signers/json-rpc';

// EOA mode
import { viemAccountEoaSigner } from '@kashdao/protocol-sdk/eoa/signers/viem-account';
import { jsonRpcEoaSigner } from '@kashdao/protocol-sdk/eoa/signers/json-rpc';

Install

npm install @kashdao/protocol-sdk viem
# or
pnpm add @kashdao/protocol-sdk viem

viem is a peer dependency. zod ships transitively. No internal Kash packages are pulled in.

Quickstart — smart-account mode

The shortest path: client.trades.send.* packs build → simulate → sign → submit → wait into one call. For 95% of consumers this is the right entry point. Power users wanting per-step control drop down to the explicit lifecycle (see "Power users — explicit trade lifecycle" below).

Need testnet funds? Base Sepolia (chain id 84532) faucets: ETH for gas — Coinbase Base Sepolia faucet or Alchemy Sepolia faucet. USDC for trades — Kash on Base Sepolia uses a mock USDC contract at 0xD6F9b17fB20aB2E532ACBAB4C7eeCc0915278913 (see addresses.ts), not Circle's official testnet USDC. Mint or transfer the mock token to your trading address; Circle's official testnet USDC is unrelated and will not work with the protocol contracts.

import { createSmartAccountClient, usdc, viemAccountSigner } from '@kashdao/protocol-sdk';
import { privateKeyToAccount } from 'viem/accounts';

const owner = privateKeyToAccount(process.env.KASH_OWNER_KEY as `0x${string}`);

const client = createSmartAccountClient({
  chainId: 84532, // Base Sepolia
  rpc: 'https://my-base-sepolia-node.example.com',
  signer: viemAccountSigner(owner),
  // Bundler is required and chain-scoped — bring your own. For Base
  // Sepolia, Pimlico, Alchemy, and Stackup are the common picks.
  bundler: { provider: 'pimlico', url: process.env.KASH_BUNDLER_URL! },
});

// Derive the smart-account address (deterministic CREATE2).
const sa = await client.account.computeAddress(owner.address);

// Read on-chain market state.
const state = await client.markets.state(knownMarketAddress);
console.log(state.outcomes.map((o) => o.probability));

// One-line trade. Behind the scenes: build → estimate gas → estimate
// fees → recompute hash → simulate → sign → submit → wait for inclusion.
// Auto-deploys the SA on first use.
const result = await client.trades.send.buy(knownMarketAddress, {
  account: sa,
  outcome: 0,
  amountUsdc: usdc('10'), // 10 USDC, no atomic-math gymnastics
  maxSlippageBps: 50, // 0.5%
});

if ('blockNumber' in result) {
  console.log(`included: ${result.transactionHash} block ${result.blockNumber}`);
}

First-time consumers: the trade above will revert if the SA hasn't approved USDC to the Market yet. See the "First-trade approval" note below or examples/smart-account/09-first-trade-approval.ts for the canonical usdcAllowance → conditional approve → buy flow.

Quickstart — EOA mode

Same one-line pattern as smart-account mode — same params shape, same return type. The signer signs vanilla EIP-1559 transactions; no bundler involved.

import { createEoaClient, usdc, viemAccountEoaSigner } from '@kashdao/protocol-sdk';
import { privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount(process.env.KASH_OWNER_KEY as `0x${string}`);
const client = createEoaClient({
  chainId: 84532,
  rpc: 'https://my-base-sepolia-node.example.com',
  signer: viemAccountEoaSigner(account),
});

const result = await client.trades.send.buy(knownMarketAddress, {
  account: client.signer.ownerAddress, // EOA holds USDC + receives tokens
  outcome: 0,
  amountUsdc: usdc('1'), // 1 USDC
  maxSlippageBps: 50, // 0.5%
});

if ('blockNumber' in result) {
  console.log(`included: ${result.transactionHash} block ${result.blockNumber}`);
}

First-trade approval (already shipped). Both modes need a one-time USDC.approve(market, max) before the first BUY — Market.transferFrom reverts otherwise. The SDK ships the canonical pattern:

import { MAX_UINT256 } from '@kashdao/protocol-sdk';

const allowance = await client.account.usdcAllowance(account, marketAddress);
if (allowance < amountUsdc) {
  await client.trades.send.approve({
    account,
    spender: marketAddress,
    amount: MAX_UINT256, // approve once, trade forever
  });
}
await client.trades.send.buy(marketAddress, {
  account,
  outcome: 0,
  amountUsdc,
  maxSlippageBps: 50,
});

See examples/eoa/03-approve-and-buy.ts and examples/smart-account/09-first-trade-approval.ts for full worked flows.

Power users — explicit trade lifecycle

send.* covers 95% of consumers. For the other 5% — custom fee strategies, replacement-by-fee, batched sponsor sign-offs, mode-specific orchestration — drop down to the explicit lifecycle:

| Step | Method | Purpose | | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | 1 | client.trades.buildBuy/Sell/ClosePosition/Approve | Encode calldata, read nonce, return zeroed-gas UserOp/tx | | 2 | client.bundler.estimateGas (SA) / client.publicClient.estimateGas (EOA) | Populate gas limits | | 3 | client.bundler.estimateFees (SA) / client.estimateFees (EOA) | Populate maxFeePerGas / maxPriorityFeePerGas | | 4 | client.trades.simulate | Pre-flight eth_call; surfaces typed revert reasons | | 5 | client.trades.hashOf | Recompute the canonical hash AFTER population (the build-time hash is stale) | | 6 | client.signer.signUserOpHash (SA) / client.signer.signTransaction (EOA) | Sign | | 7 | client.trades.submit | Submit; pre-flight staleness guard catches stale-hash mistakes | | 8 | client.trades.waitForReceipt (mode-agnostic) — or client.bundler.waitForReceipt (SA) / client.publicClient.waitForTransactionReceipt (EOA) for raw mode-specific receipt fields | Wait for inclusion |

Or use client.trades.prepareBuy/Sell/ClosePosition/Approve to collapse steps 1–5 into one call, returning a fully-populated BuiltUserOp / BuiltTransaction ready to sign.

See examples/smart-account/05-build-sign-submit.ts and examples/eoa/05-build-sign-submit.ts for the full explicit flow.

Real-time market events

const sub = client.markets.watch(marketAddress, {
  onEvent(ev) {
    if (ev.type === 'TRADE') console.log(`${ev.side} #${ev.outcome}: ${ev.tokensWad} tokens`);
    if (ev.type === 'RESOLVED') console.log(`winner: outcome ${ev.winningOutcome}`);
  },
  onReconnect() {
    // Best-effort delivery — viem reconnects but does NOT replay missed events.
    // Reconcile via @kashdao/sdk's kash.markets.predictions if gap-free
    // coverage matters, or your own indexer.
  },
  onError(err) {
    console.error(err.code, err.message);
  },
  onConnectionChange(state) {
    // Edge-triggered on transitions only ('connected' | 'reconnecting' | 'unsubscribed').
    metrics.gauge('kash.watch.connection', state === 'connected' ? 1 : 0);
  },
});

// Introspection — call at any time, no polling required:
sub.isHealthy(); // true if currently connected
sub.connectionState(); // 'connected' | 'reconnecting' | 'unsubscribed'
sub.lastBlockSeen(); // bigint | null — high-water mark for reconciliation queries (null until first event)

// Later: sub.unsubscribe();

Error handling

Every error extends KashProtocolError. Use the cross-realm-safe static is:

import { KashProtocolError } from '@kashdao/protocol-sdk';

try {
  await client.trades.submit(signedUserOp);
} catch (err) {
  if (KashProtocolError.is(err)) {
    console.error(err.code, err.context, err.cause);
    if (err.isRetryable) await sleepThenRetry();
  } else {
    throw err;
  }
}

Each error carries:

  • code — stable machine-readable identifier (e.g., BUNDLER_RPC_ERROR, MARKET_READ_FAILED)
  • context — free-form diagnostic data (URL, market address, etc.)
  • isRetryable — whether the consumer should retry
  • isOperationaltrue for expected operational failures (network blip, slippage revert), false for programmer errors (misconfigured client)
  • cause — the underlying error (network failure, viem revert, etc.)

What this SDK deliberately does NOT do

  • No retries on UserOp submission. Retrying a transaction can cause double-spends. Implement retries at your layer with your own idempotency.
  • No telemetry to Kash. No phone-home; no usage analytics.
  • No paymaster. The smart account pays its own gas in ETH.
  • No relay. Signed UserOps go to the consumer-configured bundler, never to a Kash-hosted service.

Where each method goes — zero Kash dependency

Every operation in this package goes through either the consumer's RPC or the consumer's bundler. Kash-hosted infrastructure is never in the path.

| Method | Goes to | | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | | markets.{get,state,quote,watch} | Consumer's RPC (on-chain readContract / watchContractEvent) | | account.{usdcBalance,usdcAllowance,position,gasBalance} (+ SA-only computeAddress,isDeployed,ensureDeployed) | Consumer's RPC | | trades.{buildBuy,buildSell,buildClosePosition,buildApprove,simulate} | Consumer's RPC (quote + nonce + simulation reads) | | trades.{prepareBuy,prepareSell,prepareClosePosition,prepareApprove} | Consumer's RPC (build + gas + fee estimation) | | trades.submit + bundler.* (SA only) + trades.send.* | Consumer's bundler RPC (SA) / consumer's chain RPC (EOA) | | trades.waitForReceipt(hash) | Consumer's bundler RPC (SA) / consumer's chain RPC (EOA) | | rpc.{health,estimateFees} | Consumer's RPC |

Kash literally cannot see, censor, or front-run anything done through this SDK. If the Kash API goes down, this SDK is unaffected.

Need market discovery or trade history?

Those operations aren't in this package by design — neither is feasible on-chain at scale (enumerating all markets requires scanning factory creation events back to startBlock; per-market history requires unbounded log queries). They live in @kashdao/sdk, which wraps the public REST API:

import { KashClient } from '@kashdao/sdk';
import { createSmartAccountClient } from '@kashdao/protocol-sdk';

const kash = new KashClient({ apiKey: process.env.KASH_API_KEY });
const direct = createSmartAccountClient({ chainId: 84532, rpc, signer, bundler });

// Discovery + history via the Kash-orchestrated sdk (REST)
const markets = await kash.markets.list({ status: 'ACTIVE' });
const recent = await kash.markets.predictions(markets.data[0].id);

// Trade execution via protocol-sdk (zero Kash dependency)
const built = await direct.trades.buildBuy(markets.data[0].contractAddress, params);

You can run the two packages side-by-side, or skip @kashdao/sdk entirely if you're a market maker maintaining your own market registry and indexer.

Architectural choices

  • ABIs are vendored. No internal @kashdao/* runtime deps — every ABI lives under src/contracts/generated/ and is regenerated from the Kash protocol's canonical ABI source. A drift test in CI fails the build if the vendored tree disagrees with the source.
  • Addresses are mirrored. src/contracts/addresses.ts is a static literal of the Kash protocol's per-chain registry. A drift test asserts equality against the canonical source.
  • SmartAccountSignerAdapter is sign-the-hash by default, with an optional signTypedDataV4 path for HSM/Fireblocks/AWS-KMS integrations that don't expose raw hash signing. EOA mode uses EoaSignerAdapter (signs full EIP-1559 transactions).

Long-form integration guide

A more detailed market-maker walkthrough — covering wallet setup, gas funding, signer integration, and reconnection semantics — is published at docs.kash.bot/developer-docs/protocol-sdk/overview.

License

MIT — see LICENSE.