How it works
Every coin launches through the same factory into its own Uniswap v4 pool. Creators do not provide liquidity; the curve starts with 2.5 ETH of virtual liquidity and the full token supply.
Choose a name, ticker, metadata, social links, and an optional first buy.
Buy and sell against the token's curve through a compatible Uniswap v4 router.
Trading and creator volume can earn keys in the two active pot rounds.
How to win a pot
Half of one percent from every trade goes to each reward pot. A key makes its holder the current leader, and the last eligible keyholder when the round settles receives that pot as claimable ETH. Each round runs for four hours. The separate 1% project share accrues in the hook for treasury to withdraw.
Every full 0.1 ETH of trade notional earns a trader key. A 0.35 ETH trade earns three keys and makes that wallet the current trader leader.
Volume from every trade on a creator's coins accumulates during the round. Each full 1 ETH earns that creator a key, even when it comes from many smaller trades by different users.
Near the end, qualifying keys can extend the round up to its fixed cap. If that cap is reached, the hash of a future Robinhood L2 block selects a cutoff inside the closing window before the winner is finalized.
Graduation
Graduation happens once a token reaches a 30 ETH fully diluted market cap. The hook emits the migration event and permanently marks the token as graduated. The interface shows the live USD equivalent of that 30 ETH target.
Contract addresses
Addresses below are from the live RUSHPOT deployment on Robinhood Chain.
UnavailableUnavailableUnavailableLaunch a token
Tokens must be created through FairLaunchDeployer. The factory creates the ERC-20, initializes its pool with the approved hook, and performs the optional first buy atomically.
import { parseEther } from "viem";
import { fairLaunchDeployerAbi } from "./abis";
const initialDevBuy = parseEther("0.1");
const hash = await walletClient.writeContract({
address: "DEPLOYER_ADDRESS",
abi: fairLaunchDeployerAbi,
functionName: "launch",
args: [{
name: "Internet Money",
symbol: "JPEG",
twitter: "https://x.com/yourcoin",
website: "https://yourcoin.xyz",
telegram: "https://t.me/yourcoin",
metadataUri: "ipfs://bafy...",
initialDevBuy,
}],
value: initialDevBuy,
});Listen for live events
Connect to the RUSHPOT event stream to refresh your interface when a launch, trade, graduation, key, round, or payout event is confirmed.
const socket = new WebSocket("wss://api.rushpot.fun/v1/ws");
socket.addEventListener("message", (message) => {
const event = JSON.parse(message.data);
if (event.type === "TradeExecuted") {
refreshToken(event.token);
}
if (event.type === "RoundFinalized") {
refreshPots();
}
});Buy and sell
ETH is currency0, the launch token is currency1, and the launchpad hook must be included in the pool key. Encode the address that should receive trader keys in hookData; this lets bots and smart routers credit the user they are trading for. Set a real quoted minimum output in production. Robinhood trades use the Uniswap Universal Router's V4_SWAP command.
import { encodeAbiParameters, maxUint256, parseEther, zeroAddress } from "viem";
const amountIn = parseEther("0.1");
const keyRecipientData = encodeAbiParameters(
[{ type: "address" }],
[account],
);
const poolKey = {
currency0: zeroAddress,
currency1: tokenAddress,
fee: 3000,
tickSpacing: 60,
hooks: "HOOK_ADDRESS",
};
const swap = encodeAbiParameters([{
type: "tuple",
components: [
{ name: "poolKey", type: "tuple", components: [
{ name: "currency0", type: "address" },
{ name: "currency1", type: "address" },
{ name: "fee", type: "uint24" },
{ name: "tickSpacing", type: "int24" },
{ name: "hooks", type: "address" },
]},
{ name: "zeroForOne", type: "bool" },
{ name: "amountIn", type: "uint128" },
{ name: "amountOutMinimum", type: "uint128" },
{ name: "minHopPriceX36", type: "uint256" },
{ name: "hookData", type: "bytes" },
],
}], [{
poolKey,
zeroForOne: true,
amountIn,
amountOutMinimum: quotedMinimumOut,
minHopPriceX36: 0n,
hookData: keyRecipientData,
}]);
const settle = encodeAbiParameters(
[{ type: "address" }, { type: "uint256" }],
[zeroAddress, maxUint256],
);
const take = encodeAbiParameters(
[{ type: "address" }, { type: "uint256" }],
[tokenAddress, quotedMinimumOut],
);
const inputs = [encodeAbiParameters(
[{ type: "bytes" }, { type: "bytes[]" }],
["0x060c0f", [swap, settle, take]],
)];
await walletClient.writeContract({
address: "ROUTER_ADDRESS",
abi: universalRouterAbi,
functionName: "execute",
args: ["0x10", inputs, deadline],
value: amountIn,
});import { encodeAbiParameters, maxUint256, parseEther, zeroAddress } from "viem";
const amountIn = parseEther("1000000");
const permit2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
await walletClient.writeContract({
address: tokenAddress,
abi: erc20Abi,
functionName: "approve",
args: [permit2, maxUint256],
});
await walletClient.writeContract({
address: permit2,
abi: permit2Abi,
functionName: "approve",
args: [tokenAddress, "ROUTER_ADDRESS", (1n << 160n) - 1n, permitExpiry],
});
const swap = encodeAbiParameters([{
type: "tuple",
components: [
{ name: "poolKey", type: "tuple", components: [
{ name: "currency0", type: "address" },
{ name: "currency1", type: "address" },
{ name: "fee", type: "uint24" },
{ name: "tickSpacing", type: "int24" },
{ name: "hooks", type: "address" },
]},
{ name: "zeroForOne", type: "bool" },
{ name: "amountIn", type: "uint128" },
{ name: "amountOutMinimum", type: "uint128" },
{ name: "minHopPriceX36", type: "uint256" },
{ name: "hookData", type: "bytes" },
],
}], [{
poolKey,
zeroForOne: false,
amountIn,
amountOutMinimum: quotedMinimumEthOut,
minHopPriceX36: 0n,
hookData: keyRecipientData,
}]);
const settle = encodeAbiParameters(
[{ type: "address" }, { type: "uint256" }],
[tokenAddress, maxUint256],
);
const take = encodeAbiParameters(
[{ type: "address" }, { type: "uint256" }],
[zeroAddress, quotedMinimumEthOut],
);
const inputs = [encodeAbiParameters(
[{ type: "bytes" }, { type: "bytes[]" }],
["0x060c0f", [swap, settle, take]],
)];
await walletClient.writeContract({
address: "ROUTER_ADDRESS",
abi: universalRouterAbi,
functionName: "execute",
args: ["0x10", inputs, deadline],
});Read and claim winnings
Read both live rounds directly from the hook. Winnings are pull-based, so the winner claims them from their own wallet after settlement.
const traderRound = await publicClient.readContract({
address: "HOOK_ADDRESS",
abi: rewardLaunchpadHookAbi,
functionName: "getCurrentRound",
args: [0],
});
const creatorRound = await publicClient.readContract({
address: "HOOK_ADDRESS",
abi: rewardLaunchpadHookAbi,
functionName: "getCurrentRound",
args: [1],
});
const claimable = await publicClient.readContract({
address: "HOOK_ADDRESS",
abi: rewardLaunchpadHookAbi,
functionName: "pendingWinnings",
args: [account],
});
await walletClient.writeContract({
address: "HOOK_ADDRESS",
abi: rewardLaunchpadHookAbi,
functionName: "claimWinnings",
});