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

@tidepool-launchpad/sdk

v0.2.0

Published

TypeScript SDK for the Tidepool launchpad: launch tokens, trade the bonding curve, and follow the migration into Uniswap v4.

Downloads

440

Readme

@tidepool-launchpad/sdk

TypeScript SDK for the Tidepool launchpad. Launch tokens, trade the bonding curve, and follow the migration into a Uniswap v4 pool.

Built on viem. Works in Node and the browser.

pnpm add @tidepool-launchpad/sdk viem

Quick start

import { createPublicClient, createWalletClient, http, parseEther } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { ink } from "viem/chains";
import { TidepoolClient } from "@tidepool-launchpad/sdk";

const publicClient = createPublicClient({ chain: ink, transport: http() });
const walletClient = createWalletClient({
  chain: ink,
  account: privateKeyToAccount(PRIVATE_KEY),
  transport: http(),
});

const tidepool = new TidepoolClient({
  factory: FACTORY,   // the factory PROXY for your chain — see below
  publicClient,
  walletClient,
});

Pass the chain when you build the clients — writes need it to assemble a transaction, and the client checks both clients agree on it. Reads need no wallet at all:

const readonly = new TidepoolClient({ factory: FACTORY, publicClient });

Where FACTORY comes from. The launchpad is proxied and may be redeployed, so the SDK does not bake addresses in. The current deployment for each chain lives in deployments/<chainId>.json at the repo root — read factory from the one matching your chain (57073 mainnet, 763373 Ink Sepolia), or from your own app config. Always the proxy, never the implementation. For a local anvil stack, use the address your deploy script prints.

Launching

const { token, curve } = await tidepool.launchToken({
  name: "Example",
  symbol: "EX",
  uri: "ipfs://…",
  targetRaise: parseEther("85"),
  quoteToken: "0x0000000000000000000000000000000000000000", // native ETH
});

A random salt is generated unless you supply one. It matters more than it looks: the token address is CREATE2-derived from (sender, salt), and its pool key follows. If a launch fails with EPoolAlreadyExists, someone initialized that key first — retry with a different salt, not the same one.

launchTokenAndBuy launches and buys in a single transaction, so nobody can buy between the two.

Trading

const quote = await tidepool.quoteBuy(curve, parseEther("1"));
// → { baseAmountOut, quoteAmountIn, feeAmount, refund, completesRaise }

await tidepool.buy(curve, parseEther("1"));               // 1% slippage default
await tidepool.buy(curve, parseEther("1"), { slippageBps: 50 });

await tidepool.sell(curve, quote.baseAmountOut);

buy and sell handle the parts that are easy to get wrong: they read whether the curve is native- or ERC20-quoted, attach value or set an allowance accordingly, quote first, and derive minAmountOut from your slippage tolerance.

Two things worth knowing about quoteBuy:

  • quoteAmountIn can be less than what you offered. A buy that would overshoot the raise is capped at what the curve can still absorb, and the rest is refunded. refund tells you how much.
  • completesRaise means this buy triggers migration. That transaction also creates and seeds the Uniswap v4 pool, so it costs considerably more gas. Budget for it, or the buy reverts.

Wallets the SDK cannot drive

Privy and other embedded or MPC signers never hand over a private key, so there is no viem WalletClient to give the SDK. The prepare* methods build the transactions and stop there — you broadcast them however your signer works.

const tidepool = new TidepoolClient({ factory, publicClient }); // no wallet

const txs = await tidepool.prepareBuy(curve, parseEther("1"), {
  account: userAddress,
});

for (const tx of txs) {
  await privyWallet.sendTransaction({ to: tx.to, data: tx.data, value: tx.value });
}

prepareBuy and prepareSell return PreparedTransaction[].

Send them in order and wait for each to confirm. An ERC20 trade is two transactions — approve, then trade — and the second reverts if the first has not landed. A native-quote trade is a single entry with the ETH in value.

account is required: the allowance is read rather than assumed, so an approval already covered by a standing allowance is left out instead of costing the user a redundant signature. Approvals are for the exact amount, never unlimited.

These do not simulate, because the second step of a pair reads state the first has not written yet — simulating up front would fail on a trade that is going to succeed. Simulate each step after its predecessor confirms if you want a dry run.

Launches

prepareLaunchToken and prepareLaunchTokenAndBuy return { transactions, salt } instead. The SDK generates the salt when you do not supply one, and it fixes the token's address — keep it, or you cannot reproduce or correlate your own launch, and a retry past a squatted pool key needs a different one.

const { transactions, salt } = await tidepool.prepareLaunchToken(args, {
  account: userAddress,
});

let receipt;
for (const tx of transactions) {
  const hash = await privyWallet.sendTransaction(tx);
  receipt = await publicClient.waitForTransactionReceipt({ hash });
}

// You broadcast it, so read the result back yourself:
const { token, curve } = await tidepool.launchedFrom(receipt);

launchedFrom accepts a receipt or a hash. Pass the receipt when you already have one — it decodes the logs you are holding rather than making another round trip.

Reading state

await tidepool.getConfig();                 // fees, owner, pool manager, paused
await tidepool.getLaunchedTokens();
await tidepool.getCurveAddress(token);
await tidepool.getCurve(curve);             // full state + progressBps
await tidepool.getPoolKey(token);           // v4 pool identity
await tidepool.getMaxRaise(quoteToken);     // cap for a quote asset
await tidepool.isCurve(address);

getCurve returns progressBps (0–10000) measured by base sold, not quote raised. The quote side moves non-linearly along the curve, so a quote-based percentage badly misreports early progress.

Verifying a curve is real

if (!(await tidepool.isCurve(address))) throw new Error("not a Tidepool curve");

Anyone can deploy a proxy off the public beacon and get genuine curve bytecode that was never a real launch. Bytecode is not identity. Check isCurve before showing a curve to users or indexing it.

After migration

Once migrated is true the curve is closed and the token trades on Uniswap v4. poolSeeded confirms the pool was funded — the contracts make migrated && !poolSeeded unreachable, so in practice they move together.

Note that after migration a curve's reserves describe history, not holdings: the funds are in the pool. Don't render realQuoteReserves as a balance.

Errors

Reverts come back as TidepoolContractError with the contract's error name and a plain-language reason where one exists — on reads and writes alike. A quote or buy against a migrated curve raises ETokenAlreadyMigrated rather than returning zeros, and a buy the account cannot fund fails with a message naming the account and both amounts instead of a generic estimation error.

import { TidepoolContractError } from "@tidepool-launchpad/sdk";

try {
  await tidepool.buy(curve, parseEther("1"));
} catch (e) {
  if (e instanceof TidepoolContractError) {
    console.error(e.errorName, e.message);
  }
}

Writes are simulated before sending, so a revert surfaces as a decoded error rather than a failed on-chain transaction.

Regenerating ABIs

forge build && pnpm gen:abis

License

MIT. This covers the SDK only — the launchpad contracts in this repository are separately licensed.