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

# TypeScript SDK

> Build bots, indexers, and frontends on veto.fun with @hoodstudio/hoodstar-sdk: typed reads and writes, exact quote math, and event streaming.

`@hoodstudio/hoodstar-sdk` is the official TypeScript SDK for the veto.fun protocol. It's a thin, fully-typed layer over [viem](https://viem.sh) (its only peer dependency) covering the entire protocol surface: launching tokens, trading the curve, quoting, migration, fee claims, and event streaming.

```bash theme={null}
npm install @hoodstudio/hoodstar-sdk viem
```

<Info>
  The package ships both ESM and CommonJS builds (`import` and `require` both work). All amounts are `bigint` wei values. Use viem's `parseEther` / `formatEther` helpers.
</Info>

## Setup

Create a viem client pair and pass it to `createVeto`. The SDK exports ready-made chain definitions for Robinhood Chain mainnet and testnet.

```ts theme={null}
import { createPublicClient, createWalletClient, http, parseEther } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { createVeto, robinhoodMainnet } from "@hoodstudio/hoodstar-sdk";

const publicClient = createPublicClient({
  chain: robinhoodMainnet,
  transport: http(),
});

const walletClient = createWalletClient({
  chain: robinhoodMainnet,
  transport: http(),
  account: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
});

const hood = createVeto({
  publicClient,
  walletClient, // optional; omit for read-only usage
  network: "mainnet",
});
```

<Warning>
  Only `"mainnet"` is deployed. The `"testnet"` entry is a guarded placeholder (Uniswap v3 doesn't exist on Robinhood Chain testnet); the SDK throws a descriptive error if you select it. For development, fork mainnet with Anvil and pass a custom deployment object (below).
</Warning>

Three namespaces do all the work:

* `hood.read`: typed views over the launchpad (curve state, quotes, fees, registry, pool info)
* `hood.write`: transactions (throws if you didn't pass a `walletClient`)
* `hood.watch` / `hood.backfill`: event streaming and historical indexing

<Note>
  **Local development:** you can point the SDK at any deployment (e.g. an Anvil fork) by passing a deployment object instead of a network name:

  ```ts theme={null}
  const hood = createVeto({
    publicClient,
    walletClient,
    network: { chain, launchpad: "0x…", deployBlock: 0n },
  });
  ```
</Note>

## Reading the protocol

```ts theme={null}
// Enumerate tokens (newest last), then batch-fetch curve state in one RPC call
const tokens = await hood.read.getTokens(0n, 50n);
const states = await hood.read.getCurveStates(tokens);

// Per-token views
const info = await hood.read.getTokenInfo(tokens[0]);
// → { address, name, symbol, metadataURI }
const state = await hood.read.getCurveState(tokens[0]);
// → { virtualEth, virtualToken, realTokensLeft, realEth, creator, graduated, migrated,
//     protocolFeeBps, creatorFeeBps, creatorLpShareBps }  ← the token's frozen fee snapshot

// The GLOBAL fee config (owner-tunable; what FUTURE launches will snapshot).
// For a specific token's fees, use its CurveState snapshot; those never change.
const { protocolFeeBps, creatorFeeBps, totalFeeBps, creatorLpShareBps, creationFee } =
  await hood.read.getFees();

// A token's snapshotted LP fee share (fixed at its creation)
const share = await hood.read.creatorLpShareBpsOf(token);

// Backend wallet allowed to trigger fee payouts (zero address = disabled)
const authority = await hood.read.claimAuthority();
```

## Quoting

You can quote **on-chain** (source of truth) or **client-side**. The SDK's `math` module is a bit-exact mirror of the contract's rounding, so quotes match to the wei with zero RPC round-trips:

```ts theme={null}
import { quoteBuy, quoteSell, currentPrice, curveProgressBps, ethToGraduate, totalFeeBpsOf } from "@hoodstudio/hoodstar-sdk";

const state = await hood.read.getCurveState(token);
const totalFeeBps = totalFeeBpsOf(state); // from the token's own snapshot; no extra RPC call

// Instant, local, exact
const q = quoteBuy(state, totalFeeBps, parseEther("1"));
// → { tokensOut, fee, ethUsed, graduates }   graduates=true → this buy finishes the curve

const price = currentPrice(state);            // wei per whole token
const progress = curveProgressBps(state);     // 0–10000 (10000 = graduated)
const remaining = ethToGraduate(state, totalFeeBps); // gross ETH to buy out the curve

// Or ask the contract directly, identical results
const onchain = await hood.read.quoteBuy(token, parseEther("1"));
```

## Launching a token

`createToken` deploys the ERC-20 and opens its curve. The flat creation fee (0.001 ETH under the current config) is read live and added to the transaction value automatically, so `devBuyEth` is exactly the dev buy. Recover the token address from the `TokenCreated` event in the receipt; the event also carries the token's frozen fee snapshot:

```ts theme={null}
import { parseEventLogs } from "viem";
import { vetoDotFunAbi } from "@hoodstudio/hoodstar-sdk";

const hash = await hood.write.createToken({
  name: "My Star",
  symbol: "MSTAR",
  metadataURI: "ipfs://Qm…",          // image + description, pinned to IPFS
  devBuyEth: parseEther("0.5"),       // optional first buy at the public price
});

const receipt = await publicClient.waitForTransactionReceipt({ hash });
const [created] = parseEventLogs({
  abi: vetoDotFunAbi,
  logs: receipt.logs,
  eventName: "TokenCreated",
});
const token = created.args.token; // args also include protocolFeeBps / creatorFeeBps / creatorLpShareBps
```

<Note>
  Every `hood.write.*` method **simulates first** (so reverts are decoded before you spend gas) and returns the transaction `Hash`. Await the receipt yourself with `publicClient.waitForTransactionReceipt`.
</Note>

## Trading

### Buying

```ts theme={null}
const ethIn = parseEther("1");
const q = quoteBuy(await hood.read.getCurveState(token), totalFeeBps, ethIn);

// Exact ETH in; slippage (default 1%) and deadline (default 5 min) built in
const hash = await hood.write.buy(token, ethIn, q.tokensOut, { slippageBps: 100 });
await publicClient.waitForTransactionReceipt({ hash });

// Or exact tokens out, refunding any excess ETH
await hood.write.buyExactTokens(token, tokensWanted, maxEthIn);
```

### Selling

Tokens support EIP-2612, so the one-transaction permit path is the default choice:

```ts theme={null}
const balance = await hood.read.balanceOf(token, account.address);
const sq = quoteSell(await hood.read.getCurveState(token), totalFeeBps, balance / 2n);

// One transaction: signs a permit, no separate approve needed
await hood.write.sellWithPermit(token, balance / 2n, sq.ethOut);

// Classic two-step path also available:
await hood.write.approve(token, amount);
await hood.write.sell(token, amount, sq.ethOut, { slippageBps: 50 });
```

## Graduation & migration

```ts theme={null}
const q = quoteBuy(state, totalFeeBps, parseEther("8"));
if (q.graduates) {
  // This buy will finish the curve; ethUsed < input and the remainder is refunded on-chain
}

// After graduation, anyone can migrate. It's permissionless and free.
await publicClient.waitForTransactionReceipt({ hash: await hood.write.migrate(token) });

// Inspect the resulting Uniswap v3 pool
const pool = await hood.read.getPool(token);              // zero address until pool exists
const { sqrtPriceX96, liquidity } = await hood.read.getPoolState(pool);
const positionId = await hood.read.positionOf(token);     // locked LP NFT id (0 = not migrated)
```

## Claiming fees

Claims are gated but never redirectable: funds always go to the registered recipient, and only the recipient itself (or the platform's claim-authority backend) can trigger the payout.

```ts theme={null}
// Accrued but unclaimed creator fees for a token (in wei)
const owed = await hood.read.creatorFeesOwed(token);

// Fee recipient or claim authority only; pays the recipient
await hood.write.claimCreatorFees(token);

// Airdrop mode (claim authority): pay many tokens' recipients in one tx
await hood.write.claimCreatorFeesBatch([tokenA, tokenB, tokenC]);

// Treasury only
await hood.write.claimProtocolFees();

// Harvest locked-LP swap fees for a graduated token; recipient, treasury,
// or claim authority; split at the token's snapshotted ratio
const locker = await hood.read.locker();
await hood.write.collectFees(locker, token);

// Redirect your creator fee stream (only the current recipient can call this)
await hood.write.setFeeRecipient(token, "0xNewRecipient…");
```

## Streaming events

The watch layer polls `eth_getLogs` over HTTP (Robinhood Chain's public RPC has no websocket), delivers events **at least once** in `(blockNumber, logIndex)` order, and gives you a cursor for crash-safe resumption:

```ts theme={null}
const stop = hood.watch(
  {
    onTokenCreated: (e) => console.log("new token", e.token, e.symbol, "by", e.creator),
    onTrade: (e) => console.log(e.token, e.isBuy ? "buy" : "sell", e.ethGross),
    onGraduated: (e) => console.log("graduated", e.token, "raised", e.ethRaised),
    onMigrated: (e) => console.log("pool live", e.pool, "position", e.tokenId),
    onBlockProcessed: (n) => saveCursor(n), // persist this; it's your resume point after restarts
    onError: (err) => console.error(err),
  },
  { fromBlock: await loadCursor(), pollIntervalMs: 1000 },
);

// later: stop()
```

For historical indexing, `backfill` replays the full event history in chunked `eth_getLogs` calls and resolves with the last processed block:

```ts theme={null}
const lastBlock = await hood.backfill({
  onTokenCreated: indexToken,
  onTrade: indexTrade,
});
```

<Info>
  Make handlers **idempotent** (delivery is at-least-once), and key trades on `transactionHash + logIndex` in your database.
</Info>

## Error handling

Writes simulate first, so contract reverts surface as decoded errors before gas is spent. The custom errors you'll most commonly handle:

| Error              | Meaning                                                 |
| ------------------ | ------------------------------------------------------- |
| `SlippageExceeded` | Curve moved past your `slippageBps`; re-quote and retry |
| `Expired`          | The `deadline` passed before inclusion                  |
| `CurveClosed`      | Token already graduated; trade it on Uniswap instead    |
| `CurveStillOpen`   | `migrate` called before graduation                      |
| `AlreadyMigrated`  | `migrate` called twice                                  |
| `UnknownToken`     | Address is not a veto.fun launch                        |
| `ZeroAmount`       | Trade amount was zero                                   |
| `NotFeeRecipient`  | `setFeeRecipient` from a non-recipient address          |

## Full API surface

<AccordionGroup>
  <Accordion title="hood.read: all methods">
    `tokenCount()`, `getTokens(offset, limit)`, `getAllTokens()`, `getCurveState(token)`, `getCurveStates(tokens[])`, `getFees()`, `claimAuthority()`, `creatorLpShareBpsOf(token)`, `quoteBuy(token, ethIn)`, `quoteBuyExact(token, tokensOut)`, `quoteSell(token, amount)`, `creatorFeesOwed(token)`, `protocolFeesOwed()`, `feeRecipientOf(token)`, `getTokenInfo(token)`, `balanceOf(token, account)`, `getPool(token)`, `positionOf(token)`, `getPoolState(pool)`, `locker()`, `treasury()`, `weth()`, `positionManager()`
  </Accordion>

  <Accordion title="hood.write: all methods">
    `createToken({ name, symbol, metadataURI, devBuyEth?, minTokensOut? })`, `buy(token, ethIn, quotedTokensOut, opts?)`, `buyExactTokens(token, tokensOut, maxEthIn, opts?)`, `sell(token, amount, quotedEthOut, opts?)`, `sellWithPermit(token, amount, quotedEthOut, opts?)`, `approve(token, amount)`, `migrate(token)`, `claimCreatorFees(token)`, `claimCreatorFeesBatch(tokens[])`, `claimProtocolFees()`, `setFeeRecipient(token, newRecipient)`, `collectFees(locker, token)`. Trade `opts`: `{ slippageBps?: number; deadline?: bigint }`
  </Accordion>

  <Accordion title="Standalone math exports">
    `quoteBuy(state, totalFeeBps, grossIn)`, `quoteBuyExact(state, totalFeeBps, tokensOut)`, `quoteSell(state, totalFeeBps, amount)`, `totalFeeBpsOf(state)`, `currentPrice(state)`, `curveProgressBps(state)`, `ethToGraduate(state, totalFeeBps)`, `minAfterSlippage(quoted, slippageBps)`. Plus constants `TOTAL_SUPPLY`, `SALE_SUPPLY`, `POOL_SUPPLY`, `VIRTUAL_TOKEN_0`
  </Accordion>

  <Accordion title="ABIs">
    `vetoDotFunAbi`, `hoodTokenAbi`, `feeLockerAbi`, exported for direct viem/wagmi use and event parsing. (`hoodLaunchpadAbi` remains as a deprecated alias of `vetoDotFunAbi`.)
  </Accordion>
</AccordionGroup>

<Card title="Contract reference" icon="file-contract" href="/developers/contracts">
  Prefer to integrate at the contract level? Full function, event, and error reference.
</Card>
