Skip to main content
This guide walks through integrating the InkyPump V2 hook into a JavaScript or TypeScript application. Examples use viem. The same calls work with ethers, web3.js, or any other library.

Setup

import { createPublicClient, createWalletClient, http, parseEther } from "viem"
import { privateKeyToAccount } from "viem/accounts"

const INK_MAINNET = {
  id: 57073,
  name: "Ink",
  nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
  rpcUrls: { default: { http: ["https://rpc-gel.inkonchain.com"] } },
}

const HOOK = "0x4cC8F6d5B7cE150CCC0A9B7664532B1283b96AC4"

const publicClient = createPublicClient({ chain: INK_MAINNET, transport: http() })
const account = privateKeyToAccount(process.env.PRIVATE_KEY)
const walletClient = createWalletClient({ account, chain: INK_MAINNET, transport: http() })
The hook ABI is published as part of the InkyPump SDK or can be pulled from the ABI page.

Read launch state

const state = await publicClient.readContract({
  address: HOOK,
  abi: INKY_PUMP_HOOK_ABI,
  functionName: "getLaunchState",
  args: [launchId],
})

console.log("Finalized:", state.finalized)
console.log("Target raise:", state.targetRaise)
console.log("Tokens remaining on curve:", state.tokensRemaining)
If state.finalized is true, the token has bonded. Route trades through the V4 pool instead.

Preview a buy

Previews use the LaunchViewModule at 0xce83E3659251116d114Ec1CA729ffB49B99403c3 with state read from getLaunchState. The hook also exposes wrapper preview functions, but the working path used by the InkyPump frontend is the view module call below.
const LAUNCH_VIEW_MODULE = "0xce83E3659251116d114Ec1CA729ffB49B99403c3"

const state = await publicClient.readContract({
  address: HOOK,
  abi: INKY_PUMP_HOOK_ABI,
  functionName: "getLaunchState",
  args: [launchId],
})

const { tokensOut, cost, refund, tokensRemaining } = await publicClient.readContract({
  address: LAUNCH_VIEW_MODULE,
  abi: LAUNCH_VIEW_MODULE_ABI,
  functionName: "previewBuyLocal",
  args: [
    state.saleSupply,
    state.targetRaise,
    state.gainBps,
    state.sold,
    state.saleSupply - state.sold,   // remaining
    parseEther("0.1"),                // ethIn
  ],
})

const minTokensOut = (tokensOut * 99n) / 100n  // 1 percent slippage

Execute a buy

const captcha = await fetchCaptchaFromBackend({ launchId, account: account.address })

const hash = await walletClient.writeContract({
  address: HOOK,
  abi: INKY_PUMP_HOOK_ABI,
  functionName: "buy",
  args: [launchId, minTokensOut, captcha],
  value: parseEther("0.1"),
})

const receipt = await publicClient.waitForTransactionReceipt({ hash })
For trades after the anti-snipe window has passed, the captcha argument can be empty:
const emptyCaptcha = { deadline: 0n, signature: "0x" }

Listen for events

import { parseAbiItem } from "viem"

const tradeEvent = parseAbiItem(
  "event Trade(uint256 indexed launchId, address indexed trader, uint8 tradeType, (uint256 priceAfter, uint256 marketCap) priceData, (uint256 ethAmount, uint256 tokenAmount, uint256 fee) tradeData, uint256 refund)"
)

const unwatch = publicClient.watchEvent({
  address: HOOK,
  event: tradeEvent,
  onLogs: (logs) => {
    for (const log of logs) {
      console.log("Trade:", log.args)
    }
  },
})
For referral events:
const referralEvent = parseAbiItem(
  "event Referral(uint256 indexed launchId, address indexed trader, string referralCode)"
)

Create a launch

const params = {
  name: "My Token",
  ticker: "MINE",
  description: "...",
  imageUrl: "https://...",
  telegram: "",
  twitter: "",
  website: "",
  creatorFeeSplitBps: 7000,
  gainBps: 50_000,
  targetRaise: parseEther("3"),
  antiSnipeDuration: 40,
  startTime: 0n,
}
// To prebuy, pass `value: parseEther("0.1")` (or any amount) on the writeContract call below.

const hash = await walletClient.writeContract({
  address: HOOK,
  abi: INKY_PUMP_HOOK_ABI,
  functionName: "createLaunch",
  args: [params],
  value: 0n,
})

const receipt = await publicClient.waitForTransactionReceipt({ hash })

// Parse the LaunchCreated event from receipt.logs to get the launchId and token address

Withdraw creator fees

const hash = await walletClient.writeContract({
  address: HOOK,
  abi: INKY_PUMP_HOOK_ABI,
  functionName: "withdrawFees",
})
The hook sends your accrued ETH balance to the caller.

After bonding

Once getLaunchState returns finalized: true, trade through the Uniswap Universal Router at 0x551134e92e537cEAa217c2ef63210Af3CE96a065. The pool fee is 0.1 percent. Use the V4 Quoter for previews. For the pool key, derive it from the token and WETH:
const poolKey = {
  currency0: tokenAddress < WETH ? tokenAddress : WETH,
  currency1: tokenAddress < WETH ? WETH : tokenAddress,
  fee: 1_000,
  tickSpacing: 60,
  hooks: HOOK,
}
The Universal Router takes a path encoded against this pool key. See the Uniswap V4 router docs for the full encoding.

Where to test

For a quick read-only test against the live system, read a launch state:
cast call 0x4cC8F6d5B7cE150CCC0A9B7664532B1283b96AC4 \
  "getLaunchState(uint256)" \
  $LAUNCH_ID \
  --rpc-url https://rpc-gel.inkonchain.com
If the call returns a hex blob (the encoded LaunchConfig) without reverting, your setup is talking to the live hook correctly.

Doing this from your editor

The InkyPump MCP server wraps the hook so you can call these functions through Claude Code or Codex without writing the integration yourself. Useful for one off launches and testing.