Skip to main content
@hoodstar/sdk is the official TypeScript SDK for the hoodstar.fun protocol. It’s a thin, fully-typed layer over viem — its only peer dependency — covering the entire protocol surface: launching tokens, trading the curve, quoting, migration, fee claims, and event streaming.
npm install @hoodstar/sdk viem
The package is ESM-only. All amounts are bigint wei values — use viem’s parseEther / formatEther helpers.

Setup

Create a viem client pair and pass it to createHoodstar. The SDK exports ready-made chain definitions for Robinhood Chain mainnet and testnet.
import { createPublicClient, createWalletClient, http, parseEther } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { createHoodstar, robinhoodMainnet } from "@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 = createHoodstar({
  publicClient,
  walletClient, // optional — omit for read-only usage
  network: "mainnet", // or "testnet"
});
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
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:
const hood = createHoodstar({
  publicClient,
  walletClient,
  network: { chain, launchpad: "0x…", deployBlock: 0n },
});

Reading the protocol

// 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 }

// Protocol-wide fee config (live, in basis points)
const { protocolFeeBps, creatorFeeBps, totalFeeBps } = await hood.read.getFees();

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:
import { quoteBuy, quoteSell, currentPrice, curveProgressBps, ethToGraduate } from "@hoodstar/sdk";

const state = await hood.read.getCurveState(token);
const { totalFeeBps } = await hood.read.getFees();

// 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. Recover the token address from the TokenCreated event in the receipt:
import { parseEventLogs } from "viem";
import { hoodLaunchpadAbi } from "@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: hoodLaunchpadAbi,
  logs: receipt.logs,
  eventName: "TokenCreated",
});
const token = created.args.token;
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.

Trading

Buying

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:
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

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 — 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

// Accrued but unclaimed creator fees for a token (in wei)
const owed = await hood.read.creatorFeesOwed(token);

// Claims are permissionless — funds always go to the registered recipient
await hood.write.claimCreatorFees(token);
await hood.write.claimProtocolFees(); // pays the treasury

// Harvest locked-LP swap fees for a graduated token (50/50 creator/treasury)
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:
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 — 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:
const lastBlock = await hood.backfill({
  onTokenCreated: indexToken,
  onTrade: indexTrade,
});
Make handlers idempotent (delivery is at-least-once), and key trades on transactionHash + logIndex in your database.

Error handling

Writes simulate first, so contract reverts surface as decoded errors before gas is spent. The custom errors you’ll most commonly handle:
ErrorMeaning
SlippageExceededCurve moved past your slippageBps — re-quote and retry
ExpiredThe deadline passed before inclusion
CurveClosedToken already graduated — trade it on Uniswap instead
CurveStillOpenmigrate called before graduation
AlreadyMigratedmigrate called twice
UnknownTokenAddress is not a hoodstar.fun launch
ZeroAmountTrade amount was zero
NotFeeRecipientsetFeeRecipient from a non-recipient address

Full API surface

tokenCount(), getTokens(offset, limit), getAllTokens(), getCurveState(token), getCurveStates(tokens[]), getFees(), 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()
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), claimProtocolFees(), setFeeRecipient(token, newRecipient), collectFees(locker, token) — trade opts: { slippageBps?: number; deadline?: bigint }
quoteBuy(state, totalFeeBps, grossIn), quoteBuyExact(state, totalFeeBps, tokensOut), quoteSell(state, totalFeeBps, amount), currentPrice(state), curveProgressBps(state), ethToGraduate(state, totalFeeBps), minAfterSlippage(quoted, slippageBps) — plus constants TOTAL_SUPPLY, SALE_SUPPLY, POOL_SUPPLY, VIRTUAL_TOKEN_0
hoodLaunchpadAbi, hoodTokenAbi, feeLockerAbi — exported for direct viem/wagmi use and event parsing.

Contract reference

Prefer to integrate at the contract level? Full function, event, and error reference.