Integrate with Bonker using viem to deploy tokens and interact with the factory programmatically. Bonker runs on Base (chain 8453, the default) and Robinhood Chain (chain 4663) — each has its own factory, hooks, and locker deployed at different addresses, and a deployment is only ever valid on the chain it names via originatingChainId. Pick a chain below for its addresses; everything else about deploymentConfig is identical either way.
Deploy a token
import { encodeAbiParameters, parseAbiParameters } from 'viem';
const FACTORY = '0xD850DACe6c3E3B3cf09ABb92342Fab681013c8cB';
const DYNAMIC_HOOK = '0x963E91A45148b39737b9DF10c5b897B55cA9e8cC';
const LP_LOCKER = '0xBf05b1d5E356f3219D0086A4e09c969ADbe2e7d0';
const MEV_MODULE = '0x6a04057180F8cc02E18DabEE3f3437E438BE657A';
const FEE_LOCKER = '0x473e52D89bE6ea78f94d1b5c62Bd1f01b1E32e21';
const WETH = '0x4200000000000000000000000000000000000006';
const ZERO_ADDR = '0x0000000000000000000000000000000000000000';
const CHAIN_ID = 8453n; // Base
const feeDataBytes = encodeAbiParameters(
parseAbiParameters('uint24, uint24, uint256, uint256, int24, uint256, uint24'),
[30000, 100000, 30n, 120n, 200, 500000000n, 7500]
);
const poolDataBytes = encodeAbiParameters(
parseAbiParameters('(address extension, bytes extensionData, bytes feeData)'),
[{ extension: ZERO_ADDR, extensionData: '0x', feeData: feeDataBytes }]
);
const deploymentConfig = {
tokenConfig: {
tokenAdmin: myAddress,
name: 'My Token',
symbol: 'MTK',
salt: '0x' + '00'.repeat(32),
image: '',
metadata: '',
context: '',
originatingChainId: CHAIN_ID,
},
poolConfig: {
hook: DYNAMIC_HOOK,
pairedToken: WETH,
tickIfToken0IsBonker: -230400,
tickSpacing: 200,
poolData: poolDataBytes,
},
lockerConfig: {
locker: LP_LOCKER,
rewardAdmins: [myAddress],
rewardRecipients: [myAddress],
rewardBps: [10000], // 100% to creator
tickLower: [-887200],
tickUpper: [887200],
positionBps: [10000],
lockerData: encodeAbiParameters([{ type: 'tuple', components: [{ type: 'uint8[]', name: 'feePreference' }] }], [{ feePreference: [1] }]),
},
mevModuleConfig: {
mevModule: MEV_MODULE,
mevModuleData: encodeAbiParameters(parseAbiParameters('(uint24 startingFee, uint24 endingFee, uint256 secondsToDecay)'), [{ startingFee: 666777, endingFee: 41673, secondsToDecay: 15n }]),
},
extensionConfigs: [], // no extensions
};
// The chain you broadcast this on must match originatingChainId above — walletClient's
// own chain, e.g. via viem's `chain` param or wagmi's connected chain.
const tx = await walletClient.writeContract({
address: FACTORY,
abi: factoryAbi,
functionName: 'deployToken',
args: [deploymentConfig],
});import { encodeAbiParameters, parseAbiParameters } from 'viem';
const FACTORY = '0x00615D913372aD8c87a070cD7bb18e1acf9d95f3';
const DYNAMIC_HOOK = '0x79e394e79C54936C7582452736d18890cE1368cC';
const LP_LOCKER = '0xae2a15309cd4401AF710CE014ec61246a7706B08';
const MEV_MODULE = '0xC3E89329777183Ebf4fBE02769c98799B9Ff93b4';
const FEE_LOCKER = '0x04f034649b72e7f4F167BeE683797C0C35067528';
const WETH = '0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73';
const ZERO_ADDR = '0x0000000000000000000000000000000000000000';
const CHAIN_ID = 4663n; // Robinhood Chain
const feeDataBytes = encodeAbiParameters(
parseAbiParameters('uint24, uint24, uint256, uint256, int24, uint256, uint24'),
[30000, 100000, 30n, 120n, 200, 500000000n, 7500]
);
const poolDataBytes = encodeAbiParameters(
parseAbiParameters('(address extension, bytes extensionData, bytes feeData)'),
[{ extension: ZERO_ADDR, extensionData: '0x', feeData: feeDataBytes }]
);
const deploymentConfig = {
tokenConfig: {
tokenAdmin: myAddress,
name: 'My Token',
symbol: 'MTK',
salt: '0x' + '00'.repeat(32),
image: '',
metadata: '',
context: '',
originatingChainId: CHAIN_ID,
},
poolConfig: {
hook: DYNAMIC_HOOK,
pairedToken: WETH,
tickIfToken0IsBonker: -230400,
tickSpacing: 200,
poolData: poolDataBytes,
},
lockerConfig: {
locker: LP_LOCKER,
rewardAdmins: [myAddress],
rewardRecipients: [myAddress],
rewardBps: [10000], // 100% to creator
tickLower: [-887200],
tickUpper: [887200],
positionBps: [10000],
lockerData: encodeAbiParameters([{ type: 'tuple', components: [{ type: 'uint8[]', name: 'feePreference' }] }], [{ feePreference: [1] }]),
},
mevModuleConfig: {
mevModule: MEV_MODULE,
mevModuleData: encodeAbiParameters(parseAbiParameters('(uint24 startingFee, uint24 endingFee, uint256 secondsToDecay)'), [{ startingFee: 666777, endingFee: 41673, secondsToDecay: 15n }]),
},
extensionConfigs: [], // no extensions
};
// The chain you broadcast this on must match originatingChainId above — walletClient's
// own chain, e.g. via viem's `chain` param or wagmi's connected chain.
const tx = await walletClient.writeContract({
address: FACTORY,
abi: factoryAbi,
functionName: 'deployToken',
args: [deploymentConfig],
});Robinhood Chain's UniversalRouter is a fork with an extra field in its swap structs — only relevant if you're building swaps yourself rather than using the deployed pool's default routing. See Architecture and the Deployed Contracts page for the full address list on both chains.
Claiming fees
// Check available fees — FEE_LOCKER and WETH from whichever chain's constants you're
// using above (Base or Robinhood Chain both work the same way here).
const available = await publicClient.readContract({
address: FEE_LOCKER,
abi: feeLockerAbi,
functionName: 'availableFees',
args: [myAddress, WETH],
});
// Claim fees (called by the reward recipient)
await walletClient.writeContract({
address: FEE_LOCKER,
abi: feeLockerAbi,
functionName: 'claim',
args: [myAddress, WETH],
});