Developers

Integrate with Pinarc

Launch, batch, trade, redeem and lock from your own code with viem.

Everything the app does is a normal contract call. ABIs live in pinarc-dapp/app/lib/chain/abi; addresses come from deployments/4663.json (see Contracts).

Open-source packages

Three MIT-licensed repos under github.com/pinarc-labs wrap all of this:

RepoWhat it gives you
robinhood-chain-kitviem chain definitions for Robinhood Chain (4663 / 46630), verified addresses and token list (USDG, WETH, Uniswap V2), an RPC health ranker with a fallback transport, a rate-limit-aware eth_getLogs chunker, Uniswap V2 quote helpers
pinarc-sdk@pinarc-labs/sdk: contract ABIs + mainnet addresses, the exact curve maths (quoteBuy, settleBatch, graduationPlan, …) as pure functions, a client with launch / commitBatch / buy / sell / lock / redeemFloor (approvals and slippage handled), an event decoder, and a typed client for the public API
pinarc-indexerthe reference indexer: chain → SQLite with an event audit log, projections for tokens, trades, balances, locks and vesting, reorg rewind, candles and a JSON API — verify every number the app shows on your own machine
import { createPublicClient } from "viem";
import { createFallbackTransport, robinhoodChain } from "@pinarc-labs/robinhood-chain-kit";
import { createPinarcClient, MAINNET, initialState, quoteBuy, DEFAULT_LAUNCH_PARAMS } from "@pinarc-labs/sdk";

const publicClient = createPublicClient({ chain: robinhoodChain, transport: createFallbackTransport() });
const pinarc = createPinarcClient({ publicClient, addresses: MAINNET });
const snap = await pinarc.readCurve(await pinarc.getCurveOf(token)); // one multicall
quoteBuy(snap, 100_000000n);                                           // same integers as the contract
quoteBuy(initialState(DEFAULT_LAUNCH_PARAMS), 100_000000n);           // a fresh curve, no RPC

The rest of this page shows the raw viem calls the SDK makes.

Client

import { createPublicClient, createWalletClient, http, parseUnits, defineChain } from "viem";

export const robinhoodChain = defineChain({
  id: 4663, name: "Robinhood Chain",
  nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
  rpcUrls: { default: { http: ["https://robinhood-rpc.publicnode.com"] } },
  blockExplorers: { default: { name: "Blockscout", url: "https://robinhoodchain.blockscout.com" } },
  contracts: { multicall3: { address: "0xcA11bde05977b3631167028862bE2a173976CA11" } },
});
const USDG = "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168";
const pub = createPublicClient({ chain: robinhoodChain, transport: http() });

Launch

const launchFee = 5n * 10n ** 6n, bond = 500n * 10n ** 6n, devBuy = 100n * 10n ** 6n;
await wallet.writeContract({ address: USDG, abi: erc20Abi, functionName: "approve", args: [FACTORY, launchFee + bond + devBuy] });
const hash = await wallet.writeContract({
  address: FACTORY, abi: PinarcFactoryAbi, functionName: "createToken",
  args: [{ name: "Hood Doge", symbol: "HDOGE", metadataURI: "https://dapp.pinarc.io/api/v1/metadata/<id>.json",
           floorBps: 1000, lpLockSeconds: 365 * 86400, teamBps: 800, teamCliff: 30n * 86400n, teamDuration: 210n * 86400n,
           bond, devBuyUsdc: devBuy }],
});
// token + curve addresses are in the TokenCreated log of the receipt

Batch

await wallet.writeContract({ address: USDG, abi: erc20Abi, functionName: "approve", args: [curve, parseUnits("250", 6)] });
await wallet.writeContract({ address: curve, abi: BondingCurveAbi, functionName: "commitBatch", args: [parseUnits("250", 6)] });
// after batchEndsAt:
await wallet.writeContract({ address: curve, abi: BondingCurveAbi, functionName: "claimBatch" }); // settles implicitly

Buy and sell with slippage

const usdgIn = parseUnits("100", 6);
const [tokensOut] = await pub.readContract({ address: curve, abi: BondingCurveAbi, functionName: "quoteBuy", args: [usdgIn] });
const minOut = (tokensOut * 9700n) / 10000n;                   // 3% slippage
await wallet.writeContract({ address: curve, abi: BondingCurveAbi, functionName: "buy", args: [usdgIn, minOut, me] });

const [usdgOut] = await pub.readContract({ address: curve, abi: BondingCurveAbi, functionName: "quoteSell", args: [tokensIn] });
await wallet.writeContract({ address: curve, abi: BondingCurveAbi, functionName: "sell", args: [tokensIn, (usdgOut * 9700n) / 10000n, me] });

Catch ContractFunctionRevertedError and read data.errorName: BatchOpen, MaxTx, MaxWallet, Cooldown, Slippage, CurveGraduated.

After graduation

const ROUTER = "0x89e5DB8B5aA49aA85AC63f691524311AEB649eba";
await wallet.writeContract({ address: ROUTER, abi: uniswapV2RouterAbi, functionName: "swapExactTokensForTokens",
  args: [usdgIn, minOut, [USDG, token], me, BigInt(Math.floor(Date.now() / 1000) + 600)] });

Detect graduation from isGraduated() or the Graduated event; pair() gives the pool.

Floor, vesting, locks, bond

await wallet.writeContract({ address: FLOOR,  abi: FloorReserveAbi, functionName: "redeem",   args: [token, amount] });   // approve token to FLOOR first
await wallet.writeContract({ address: VAULT,  abi: VestingVaultAbi, functionName: "release",  args: [scheduleId] });
await wallet.writeContract({ address: LOCKER, abi: LPLockerAbi,     functionName: "withdraw", args: [lockId, me] });
await wallet.writeContract({ address: BOND,   abi: CreatorBondAbi,  functionName: "release",  args: [token] });

Reading without the API

PinarcFactory.allTokensLength() / allTokens(i) enumerate launches; curveOf(token) finds the curve; getLogs on the events listed in Curve lifecycle rebuilds the whole history — that is exactly what the indexer does.