buy many
- contracts
- monad
- nad.fun
buy a whole list of nad.fun tokens in one transaction. you send MON, every token lands in your wallet. no basket, no custody, no partial fills.
it runs on NadMultiBuy, a small contract we deployed on monad mainnet.
use it from the dashboard
- open established cults, every row has a checkbox
- tick the tokens you want, up to 20
- set MON per token and your slippage (0.5%, 1%, 2% or custom)
- connect your wallet, review, confirm
the review screen shows, before you sign:
- each token, its route (nad.fun DEX or nad.fun V2) and the least amount you will receive
- the buy fee and the total MON sent
- the network fee, at most
- your slippage
quotes refresh every few seconds while the review is open. the transaction is valid for 5 minutes, after that it reverts and nothing is spent.
the gas badge compares one buyMany against buying each token one by one. batching is cheaper at every size, see gas.
what can be bought
- legacy nad.fun tokens, on the bonding curve or graduated
- nad.fun V2 tokens quoted in WMON, on the curve or graduated
V2 tokens quoted in anything else (LVMON, BNCB, USDC…) cannot be bought here, the dashboard greys them out with the reason. tokens that did not come from nad.fun are rejected by the contract.
how a buy works
one call, buyMany(legs, to, deadline, maxFeeBps). a leg is one purchase:
struct Leg {
address token; // what to buy
uint256 monIn; // MON to spend on it
uint256 minOut; // least amount of token `to` must receive, non-zero
}
for each leg, in order, the contract:
- picks the nad.fun router that fits the token:
- legacy, graduated →
DEX_ROUTER - legacy, still on the curve →
BONDING_CURVE_ROUTER - V2 quoted in WMON →
NadFunRouter.buyWithNative, which picks curve or pair itself
- legacy, graduated →
- buys with
monIn, the router sends the token straight toto - checks
to's balance went up by at leastminOut
then it pays the fee, refunds every unspent MON to you, and emits BoughtMany.
all or nothing. if any leg fails, the whole transaction reverts. you never end up with half a list.
fee
- charged on the MON spent buying,
sum(monIn), not on what you send on top - capped at 1% (
MAX_FEE_BPS = 100) in the contract, it can never go higher - currently 0%
- you pass
maxFeeBps, the highest fee you accept. if the fee changed above it before your transaction lands, it reverts withFeeAboveMax
msg.value must cover sum(monIn) + fee. anything above comes back in the refund.
safety
- no custody. the contract holds nothing between calls: tokens go router → your wallet, MON is refunded in the same transaction. it only accepts MON while a buy is running
- your minimums, checked twice. each
minOutgoes to the router, and the contract checks the actual balance change oftoitself. a router that ignores its minimum is caught, and tokens you already held never count - deadline. past
deadline, it reverts withExpired - reentrancy. every entry point is guarded, MON is sent last
- owner powers. the owner (Ownable2Step) can set the fee within the 1% cap, set the fee recipient, and pause new buys. the owner cannot touch your funds or tokens
- reviewed internally, not externally audited yet
fresh tokens. nad.fun applies anti-sniping penalties right after launch: legacy tokens fill about 1% under quote for 5 blocks, V2 up to 80% for 7. your minOut bounds the loss, but buying a token younger than ~10 blocks will likely revert.
gas
monad bills the gas limit, not the gas used. the dashboard sends the estimate plus 10%.
full transaction cost, every account cold, 0.5 MON per token:
| route | tokens | one by one | buyMany | saved |
|---|---|---|---|---|
| legacy curve | 3 | 954k | 707k | 26% |
| 10 | 3.18M | 1.90M | 40% | |
| legacy DEX | 3 | 841k | 720k | 14% |
| 10 | 3.05M | 2.28M | 25% | |
| V2 pair | 3 | 1.71M | 1.12M | 35% |
| 10 | 5.72M | 2.81M | 51% | |
| 20 | 11.43M | 5.22M | 54% | |
| mixed | 3 | 1.13M | 1.09M | 4% |
| 10 | 4.25M | 2.61M | 39% |
one by one pays cold access to the router, curve, WMON and fee collectors on every transaction, a batch pays it once.
errors
| error | why |
|---|---|
Expired | the deadline passed before the transaction landed |
NoLegs, TooManyLegs | 0 legs, or more than 20 |
ZeroMinOut | a leg's minOut is 0 |
FeeAboveMax | the current fee is above your maxFeeBps |
MonBudgetExceeded | msg.value does not cover sum(monIn) + fee |
UnsupportedToken | not a nad.fun token, or a V2 token not quoted in WMON |
ComponentLocked | a legacy token is mid-graduation, or nad.fun V2 is halted. retry in a bit |
LegFailed(i, token, reason) | the router reverted on leg i, reason is its revert data. usually slippage |
InsufficientOut(i, got, min) | leg i delivered less than its minOut |
contract
| network | monad mainnet (chain id 143) |
NadMultiBuy | 0xe9546c686A146a398Ce58FB67f5077bE8Ab36fa0 |
RecipeQuoter | 0xB4212eB16F8Aa1550943D24e00D49a112d3d8f80 |
building your own frontend? RecipeQuoter.multiBuyLegs(tokens, monIn, slippageBps) returns ready-made legs from live quotes. read feeBps() and pass it as maxFeeBps.