How to build a token launchpad on NEAR
A launchpad lets anyone create a token that people can trade right away. On NEAR, the only contract you need is a launch factory: it creates each token and puts its whole supply into a Plach launch pool. The Intear DEX Aggregator routes trades through that pool, and the Intear API serves its prices, charts and trades.
This guide builds one on mainnet, step by step. We deployed a factory for it, examplepad.near, and the examples on this page launch, list, chart and trade its tokens. Last week's guide, How to add token swaps to your app, covers trading in more depth.
What you'll use
| Piece | What it does | Where |
|---|---|---|
| Launch factory | Creates tokens and their launch pools. The only contract you deploy | Your fork of INTEARnear/launch |
| Plach | The AMM that holds each launch's pool | slimedragon.near/xyk on dex.intear.near |
| DEX Aggregator | Buy and sell quotes, as transactions to sign | https://router.intear.tech |
| Intear API | Token list, prices, charts, trades, balances, positions | https://api.intea.rs |
| NEAR Connect | Connecting the user's wallet | NPM: @hot-labs/near-connect |
No API keys are needed. Everything runs on NEAR mainnet.
How a launch works
One call to the factory creates the token, puts its whole supply into a launch pool, and can buy some of it for the creator right away. The pool starts with virtual NEAR liquidity that doesn't exist, which sets the starting price: a token's starting market cap equals its virtual liquidity. Buys push the price up and sells pull it down, and nobody can add or remove the pool's liquidity, so it can't be rugged.
Intear's factory starts NEAR launches at 300 NEAR. Ours starts at 10, so the small trades on this page move the chart:
Live example
Why Intear DEX
Launchpads on NEAR can be built on Intear Plach (a DEX on Intear DEX engine), or on one of Rhea's two kinds of pools: classic and CLMM. Here's how they compare for launches:
| Intear DEX / Plach | Rhea classic | Rhea CLMM | |
|---|---|---|---|
| Unique features | Virtual liquidity, revenue and referral tracking dashboard with one-click fee claims, on the Intear DEX website | None | Single-sided / concentrated liquidity (CLMM), native limit orders |
| Fees | From 0% to 90%, can be paid to creator even when LP is burnt, fixed or scheduled to start high and drop over time, auto-conversion to one token | From 0% to 99.9%, paid to liquidity providers | Eight tiers: 0.01%, 0.04%, 0.1%, 0.2%, 1%, 2%, 3% and 4%, paid to liquidity providers |
| DEX fees | 20% of pool fees | 20% of pool fees | 20% of pool fees + 0.5 NEAR first-swap fee per trader |
| Source code | Open source, with the deployed version linked on chain | Used to be public, made private after an exploit in early 2026 | Not public, and has never been |
| Audits | Never audited | Older versions audited in 2025 | Older versions audited in 2025 |
| Live since | December 2025 | August 2021 | January 2023 |
Rhea has been running longer, and its DCL pools let liquidity providers add a single token over a price range. Plach has what a launchpad needs built in, so this guide uses it. Definitely unbiased comparison ðŸ˜
1. Fork the factory
Fork INTEARnear/launch. Its settings are in src/lib.rs: the tokens a launch can pair with, NEAR, USDC and TEAR, and the virtual liquidity of each. Our fork changes one line, the virtual liquidity of NEAR launches:
let quote = match "e_token {
- None => Some((AssetId::Near, NearToken::from_near(300).as_yoctonear())),
+ None => Some((AssetId::Near, NearToken::from_near(10).as_yoctonear())),
Every token's ID ends in the factory's account, like example-1.examplepad.near, so pick a short one, and set it as FACTORY in tests/launch.rs.
2. Deploy it on mainnet
Run the tests, which launch tokens in a local sandbox with the mainnet code of Intear DEX and Plach, and build the contract:
cargo test
cargo near build reproducible-wasm
Create the factory's account and deploy, with near CLI. The code takes about 3.2 NEAR of storage:
near account create-account fund-myself examplepad.near '3.4 NEAR' \
autogenerate-new-keypair save-to-keychain \
sign-as your-account.near network-config mainnet sign-with-keychain send
near contract deploy examplepad.near use-file target/near/intear_launch.wasm \
with-init-call new json-args '{}' prepaid-gas '30.0 Tgas' attached-deposit '0 NEAR' \
network-config mainnet sign-with-keychain send
Then register the factory and its quote tokens in Intear DEX, once:
# Storage for the factory's balances in Intear DEX
near contract call-function as-transaction dex.intear.near storage_deposit \
json-args '{}' prepaid-gas '30.0 Tgas' attached-deposit '0.02 NEAR' \
sign-as examplepad.near network-config mainnet sign-with-keychain send
# NEAR, USDC and TEAR, for the factory and for Plach
ASSETS='["near", "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1", "nep141:token.intear.near"]'
near contract call-function as-transaction dex.intear.near register_assets \
json-args "{\"asset_ids\": $ASSETS}" prepaid-gas '30.0 Tgas' attached-deposit '1 yoctoNEAR' \
sign-as examplepad.near network-config mainnet sign-with-keychain send
near contract call-function as-transaction dex.intear.near register_assets \
json-args "{\"asset_ids\": $ASSETS, \"for\": {\"Dex\": \"slimedragon.near/xyk\"}}" prepaid-gas '30.0 Tgas' attached-deposit '1 yoctoNEAR' \
sign-as examplepad.near network-config mainnet sign-with-keychain send
# The factory on the quote tokens, to take first-buy deposits in them
for TOKEN in 17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1 token.intear.near; do
near contract call-function as-transaction $TOKEN storage_deposit \
json-args '{"account_id": "examplepad.near", "registration_only": true}' \
prepaid-gas '30.0 Tgas' attached-deposit '0.00125 NEAR' \
sign-as examplepad.near network-config mainnet sign-with-keychain send
done
Here's our deployment, read from the chain:
Live example
Reading examplepad.near…
Our transactions: account, deploy, Intear DEX storage, assets, assets for Plach, USDC, TEAR.
3. Launch a token
prepare_launch checks a launch and returns its token ID, the exact deposit to attach, and any errors. Then launch_token launches it with that deposit:
const args = {
name: 'Example',
symbol: 'EXAMPLE',
icon: 'data:image/webp;base64,...',
decimals: 24,
total_supply: (1_000_000_000n * 10n ** 24n).toString(),
short_id: false,
fees: [[{ Account: 'alice.near' }, { Fixed: 10000 }]], // 1% of each trade for the creator
launch_data: { telegram: null, x: null, twitch: null, website: 'https://example.com', description: 'An example' },
first_buy: '100000000000000000000000', // 0.1 NEAR, or null
quote_token: null, // NEAR
};
const { token_account_id, deposit, errors } = await view('examplepad.near', 'prepare_launch', { account_id: 'alice.near', ...args });
const outcome = await wallet.signAndSendTransaction({
receiverId: 'examplepad.near',
actions: [{ type: 'FunctionCall', params: { methodName: 'launch_token', args, gas: '300000000000000', deposit } }],
});
Try it below. Launching sends a real transaction, and costs about 0.04 NEAR plus the first buy.
Live on mainnet
Request
4. List the launches
One Intear API subscription follows every token of your factory, new launches included:
{
"id": 1,
"method": "tokens.subscribe",
"params": {
"fields": ["name", "symbol", "decimals", "icon_hash", "price_usd", "market_cap_usd", "volume_usd_24h", "holders", "created_at_millis"],
"where": { "parent": "examplepad.near" },
"min_change": { "price_usd": "0.1%", "market_cap_usd": "0.1%", "volume_usd_24h": "1%" },
"min_interval_ms": 1000
}
}
The reply has every token, and each push after it has only what changed:
const tokens = new Map();
function onPush({ set = {}, left = [], created = [] }) {
for (const [tokenId, fields] of Object.entries(set)) tokens.set(tokenId, { ...tokens.get(tokenId), ...fields });
for (const tokenId of left) tokens.delete(tokenId);
render(tokens, created);
}
Live example
Loading the launches…
| Token | Market cap | 24h volume | Holders | Launched |
|---|
5. The token page
The chart comes from candles.subscribe. Multiply the prices by the supply to chart the market cap:
{ "id": 2, "method": "candles.subscribe", "params": { "tokens": ["example-1.examplepad.near"], "resolution": "1", "quote": "usd", "count_back": 300 } }
import { createChart, CandlestickSeries } from 'lightweight-charts';
const chart = createChart(container, { autoSize: true });
const series = chart.addSeries(CandlestickSeries);
const toBars = ({ time, usd }) => time.map((openedAt, i) => ({
time: openedAt / 1000, // seconds
open: usd.open[i] * supply, high: usd.high[i] * supply, low: usd.low[i] * supply, close: usd.close[i] * supply,
}));
// reply: series.setData(toBars(result.tokens[tokenId]))
// push: for (const bar of toBars(push.tokens[tokenId])) series.update(bar) // a candle replaces the one with the same time
Or embed https://chart.intear.tech/?token=<token> in an iframe for a ready chart. Trades come from trades.subscribe:
{ "id": 3, "method": "trades.subscribe", "params": { "token": "example-1.examplepad.near", "history": 30, "fields": ["timestamp_millis", "trader", "transaction_id", "counterparty", "counterparty_amount", "side", "usd_value"] } }
The pool and its fees are in dex-backend's https://dex-backend.intear.tech/pools/all, and the description and links in the factory's get_launch_data. Pick a token in the list above to open it here.
Live example
| Side | Amount | Value | Trader | When |
|---|
6. Buy and sell
Buying is a swap from NEAR to the token, and selling the other way. Get a quote from the DEX Aggregator and send its transactions with the wallet, as in the swap guide:
const params = new URLSearchParams({
token_in: 'near',
token_out: 'nep141:example-1.examplepad.near',
amount_in: '100000000000000000000000', // 0.1 NEAR
slippage_type: 'Auto',
min_slippage: '0.001',
max_slippage: '0.1', // fresh launches move a lot
trader_account_id: 'alice.near',
fees: JSON.stringify({ 'examplepad.near': 0.01 }), // your app's fee, step 7
referrer_id: 'examplepad.near',
});
const [quote] = await (await fetch(`https://router.intear.tech/route?${params}`)).json();
This trades real tokens on NEAR mainnet. Tokens launched here are examples, with no value beyond the NEAR in their pools.
Live on mainnet
7. Fees and referrals
Add fees to your route requests to earn a share of every trade your app sends. For a referral program, split it with whoever brought the trader:
const PLATFORM = 'examplepad.near';
// The first referral link a browser opens is kept
const ref = new URLSearchParams(location.search).get('ref');
if (ref && isValidAccountId(ref) && !localStorage.getItem('referrer')) localStorage.setItem('referrer', ref);
// 1% per trade, half of it to the referrer, but not on their own trades
function routeFees(referrerId, traderId) {
if (!referrerId || referrerId === traderId || referrerId === PLATFORM) return { [PLATFORM]: 10_000 / 1e6 };
return { [PLATFORM]: 5_000 / 1e6, [referrerId]: 5_000 / 1e6 };
}
- Pass
trader_account_idonce a wallet is connected: the route then registers your fee accounts for the token being paid, so they get paid. Previews before that can leave it out. - Pass your platform's account as
referrer_idto count the volume you bring.
Open this page with ?ref= and an account to see the split change.
Live example
- This browser's referrer
- Your referral link
8. Claim fees
App and referral fees land in each account's Intear DEX balance, and withdraw sends them to the wallet:
{
receiverId: 'dex.intear.near',
actions: [{ type: 'FunctionCall', params: {
methodName: 'withdraw',
args: { asset_id: 'near', amount: { Full: { at_least: null } } },
gas: '50000000000000',
deposit: '1',
} }],
}
Creator fees wait in Plach until withdraw_fees moves them to the Intear DEX balance, to withdraw as above. https://dex-backend.intear.tech/balances?accountId=<account> shows both.
{
receiverId: 'dex.intear.near',
actions: [{ type: 'FunctionCall', params: {
methodName: 'dex_call',
args: {
dex_id: 'slimedragon.near/xyk',
method: 'withdraw_fees',
args: 'AQAAAAA=', // ["near"], encoded in Borsh
attached_assets: {},
},
gas: '100000000000000',
deposit: '1',
} }],
}
Live on mainnet
9. Positions and PnL
positions.get returns how much of each token an account holds, what it cost, and its realized PnL:
const { columns, rows } = await (await fetch('https://api.intea.rs/positions?account=alice.near')).json();
// columns: token, amount, cost_usd, bought, sold, realized_pnl_usd, buys, sells, ...
Live example
Next steps
- Read the factory's source, and the Plach docs on launch pools and fees.
- See every Intear API method and field in the docs and playground.
- Building with an AI agent? Give it the intear-launchpad skill: everything in this post, plus ready TypeScript clients for launching, routes, the Intear API and wallets. Its other files are next to
SKILL.md, at the paths it links: save them all in anintear-launchpad/folder in the agent's skills folder, like~/.claude/skills/for Claude Code. - Questions? Ask in Intear's Telegram chat.