TypeScript
Setup
Install viem, define X Layer, and point a client at the launchpad. Every other page in this section starts from this file.
Everything here uses viem and nothing else. The launchpad has no SDK to install — it is a handful of contracts, and viem is enough to call all of them.
npm install viem
You will want TypeScript 5.0 or later, and Node 20 or later if you are running this
outside a browser. The samples use top-level await, so run them as ESM —
"type": "module" in your package.json, or a .mts
file.
The shared module#
Every other page imports from this file: the chain, the clients, the addresses and the ABIs. Save it once and the rest is a handful of lines each time.
import { createPublicClient, createWalletClient, defineChain, http, parseAbi } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import type { Address } from 'viem'
/** X Layer. Chain 196, native OKB, one-second blocks. */
export const xLayer = defineChain({
id: 196,
name: 'X Layer',
nativeCurrency: { name: 'OKB', symbol: 'OKB', decimals: 18 },
rpcUrls: { default: { http: ['https://xlayerrpc.okx.com'] } },
blockExplorers: { default: { name: 'OKLink', url: 'https://www.oklink.com/xlayer' } },
})
export const publicClient = createPublicClient({ chain: xLayer, transport: http() })
/** A server-side signer. In a browser use custom(provider) instead — see below. */
export const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
export const walletClient = createWalletClient({ account, chain: xLayer, transport: http() })
export const size = {
factory: '0xfdfCaaF9e744D2C43365a710628469bCAf365491',
launchAndBuy: '0x24274B859C2B99358A7c9ed1da33EbAb832CadB4',
feeEscrow: '0x401100747741364AD16a96D6Ba5724Bf817Cc25F',
} as const satisfies Record<string, Address>
/** Coins quote against native OKB, which is addressed as the zero address. */
export const OKB = '0x0000000000000000000000000000000000000000' as const
/** The only launch config on the factory today: 1B supply, 1% fee, 30/75 OKB. */
export const LAUNCH_CONFIG_ID = 0n
export const factoryAbi = parseAbi([
'struct Socials { string twitter; string telegram; string discord; string website; string farcaster; }',
'struct TokenParams { string name; string symbol; string logo; string description; Socials socials; address creatorFeeRecipient; uint16 creatorTaxBps; bool buybackEnabled; bytes32 expectedEconomics; bytes32 salt; }',
'struct LaunchedToken { address token; address curve; address deployer; address creatorFeeRecipient; address pairToken; uint256 graduationThreshold; uint24 poolFee; int24 tickSpacing; uint16 creatorTaxBps; bool buybackEnabled; uint8 phase; uint256 sweptQuote; uint256 sweptTokens; uint256 sweptAt; bool exists; }',
'function launchFee() view returns (uint256)',
'function launchEnabled() view returns (bool)',
'function maxCreatorTaxBps() view returns (uint256)',
'function previewLaunchEconomics(uint256 launchConfigId, address pairToken) view returns (bytes32)',
'function launchToken(TokenParams params, uint256 launchConfigId, address pairToken) payable returns (address token, address curve)',
'function getLaunchedToken(address token) view returns (LaunchedToken)',
'function createGraduatedPool(address token) returns (uint256 positionId)',
'event TokenLaunched(address indexed token, address indexed curve, address indexed deployer, address pairToken, uint256 launchConfigId, uint256 graduationThreshold)',
'event PoolGraduated(address indexed token, uint256 positionId, uint256 tokenAmount, uint256 pairTokenAmount)',
])
export const launchAndBuyAbi = parseAbi([
'struct Socials { string twitter; string telegram; string discord; string website; string farcaster; }',
'struct TokenParams { string name; string symbol; string logo; string description; Socials socials; address creatorFeeRecipient; uint16 creatorTaxBps; bool buybackEnabled; bytes32 expectedEconomics; bytes32 salt; }',
'function launchAndBuy(TokenParams params, uint256 launchConfigId, address pairToken, uint256 quoteIn, uint256 minTokensOut, address recipient, address[] snipeTaxExemptions) payable returns (address token, address curve, uint256 tokensOut)',
])
export const curveAbi = parseAbi([
'function buy(uint256 quoteIn, uint256 minTokensOut, address recipient) payable returns (uint256 tokensOut)',
'function sell(uint256 tokensIn, uint256 minQuoteOut, address recipient) returns (uint256 quoteOut)',
'function getReserves() view returns (uint256 quoteReserve, uint256 tokenReserve)',
'function realQuoteReserve() view returns (uint256)',
'function sellableTokens() view returns (uint256)',
'function graduationThreshold() view returns (uint256)',
'function currentSnipeTaxBps(address recipient) view returns (uint256)',
'function graduated() view returns (bool)',
'function readyToGraduate() view returns (bool)',
'function token() view returns (address)',
'function feeBps() view returns (uint256)',
'function creatorTaxBps() view returns (uint256)',
'function quoteFeeBalance() view returns (uint256)',
'function sweepFees(uint256 minBuybackTokensOut)',
'event CurveBuy(address indexed buyer, address indexed recipient, uint256 quoteIn, uint256 tokensOut, uint256 fee, uint256 tax)',
'event CurveSell(address indexed seller, address indexed recipient, uint256 tokensIn, uint256 quoteOut, uint256 fee, uint256 tax)',
])
export const escrowAbi = parseAbi([
'function claim() returns (uint256)',
'function balanceOf(address recipient) view returns (uint256)',
])
The ABIs are written as human-readable signatures rather than pasted JSON. viem parses them into the same thing, and they stay legible — which matters here, because these signatures are the reference for what you can call.
Only the parts you need
These are trimmed to what the guides use. The contracts are verified on OKLink, so the full ABI of any of them is one click away if you need something that is not here.
Which RPC#
The public endpoint above works and is free. Two things to know about it:
- It caps
eth_getLogsat 100 blocks per call. Fine for watching, painful for walking history — page it, or use a provider without the cap. - It is rate limited. If you are polling reads for more than a few coins, get your own endpoint. Everything on these pages is standard JSON-RPC, so any X Layer provider works.
In a browser#
The only thing that changes is where the signature comes from. Reads still go through a public client; writes go through the user's wallet instead of a private key.
import { createWalletClient, custom } from 'viem'
import { xLayer } from './size.js'
/**
* In a browser the signer is the user's wallet, so the private key never
* exists in your code. Everything else on these pages is unchanged: the same
* ABIs, the same simulate-then-write pattern, the same public client for reads.
*/
export async function connect() {
const provider = (window as any).ethereum
if (!provider) throw new Error('No wallet found')
const [address] = await provider.request({ method: 'eth_requestAccounts' })
// Offer X Layer if the wallet has never been there. 0xc4 is 196.
await provider.request({
method: 'wallet_addEthereumChain',
params: [{
chainId: '0xc4',
chainName: 'X Layer',
nativeCurrency: { name: 'OKB', symbol: 'OKB', decimals: 18 },
rpcUrls: ['https://xlayerrpc.okx.com'],
blockExplorerUrls: ['https://www.oklink.com/xlayer'],
}],
}).catch(() => {})
const walletClient = createWalletClient({
account: address,
chain: xLayer,
transport: custom(provider),
})
return { address: address as `0x${string}`, walletClient }
}
Keys
The server-side client in size.ts reads a private key from the
environment. Never ship that to a browser, and never commit it. If you are building a
UI, the wallet is the signer and your code never sees a key at all.
What you can call#
| You want to | Call | On |
|---|---|---|
| Launch a coin | launchToken | LaunchFactory |
| Launch and take the first position | launchAndBuy | LaunchAndBuy |
| Buy on the curve | buy | the coin's curve |
| Sell on the curve | sell | the coin's curve |
| Read price and progress | getReserves | the coin's curve |
| Find a coin's curve | getLaunchedToken | LaunchFactory |
| Settle a stalled graduation | createGraduatedPool | LaunchFactory |
| Move fees to the escrow | sweepFees | the coin's curve |
| Collect your fees | claim | FeeEscrow |
Curve addresses are per coin. Given a token address, getLaunchedToken on
the factory returns its curve along with the terms it launched under.