Engineering notes

Porting a bonding-curve launchpad from Sui to Monad

Same product, same economics, two execution models that disagree about almost everything. These are the decisions that mattered and the four bugs that only appeared once real money moved on a real chain.

MemeHedge engineering Sui Move → Monad EVM

MemeHedge is a meme-coin launchpad: a token is created, priced by a constant-product bonding curve from its first trade, and graduates to a DEX once it raises a fixed amount. It has been running on Sui. We ported it to Monad without forking the product in two.

The interesting part was not translating Move to Solidity. It was that a bonding curve is a different kind of object on each chain, and almost every assumption baked into the Sui implementation turned out to be an assumption about Sui rather than about bonding curves.

01A curve is an object on one chain and a contract on the other

On Sui, each token's curve is a shared object. It holds its own reserves and its own graduation target, it has a stable identity, and anyone can read its full state in a single call. Ownership is a property of the object.

On an EVM chain there is no such thing. Each curve is a separate contract deployed as a minimal-proxy clone of one implementation, with its state in that contract's own storage. Cheap to deploy, but it changes what "read the curve" means: instead of fetching one object you are calling a function on an address you must already know.

That single difference drove the indexer design. On Sui you can ask the chain about an object. On EVM you have to have been listening when the contract was created, or you do not know it exists.

02Custody does not port, because the curves do not match

MemeHedge is custodial by design: a platform-managed internal wallet signs every trade, and a user's own wallet is used for identity and funding only. This is the "lightning wallet" model — people trade meme coins in bursts, and a wallet confirmation on every buy kills that.

The naive assumption was that the internal wallet would simply carry over. It cannot. Sui uses Ed25519; EVM uses secp256k1. An Ed25519 key cannot produce a signature an EVM chain will accept — not a library limitation, a different elliptic curve.

So a user holds one internal wallet per chain family, generated on first use and encrypted at rest the same way. The external wallet's role is unchanged: it proves who you are, it funds the internal wallet, and it never signs a trade.

Cost us a rebuild

We first built the Monad trading path non-custodially — the server returned an unsigned transaction for the user's wallet to sign. It worked, and it was the wrong architecture. It had to be rebuilt server-side to match Sui. Worth settling the custody model before writing the first line, not after the tests pass.

03Economic parity was a deliberate constraint

It would have been easier to let each chain drift. We did the opposite: identical curve mathematics, identical supply split, identical graduation split, denominated in each chain's own native asset. A token behaves the same way whichever chain it launched on.

What is shared and what necessarily differs
PropertySuiMonad
Curve mathsConstant product with virtual reserves — identical
Supply split65% on the curve, 25% to the pool, 10% retained — identical
Graduation split30% platform / 3% creator / 67% to liquidity — identical
Trading fee1% — identical
Signing curveEd25519secp256k1
Token decimals918
Graduation venueNative DEXUniswap V4

Decimals deserve a mention because they are the quietest way to be wrong. Sui tokens are 9 decimals, EVM tokens are conventionally 18. Any value that crosses the boundary — a column default, a shared config, a hardcoded scale factor — is a factor of a billion waiting to happen.

04Uniswap V4 is not a drop-in for "a DEX"

Graduation on Monad seeds a pool on Uniswap V4, which we picked over V2 on measured liquidity rather than preference. V4 changes enough to matter for an integration:

The failure mode we designed around

Seeding touches third-party code. If that call reverts — a pool already opened at a hostile price, the DEX paused, anything — and graduation reverts with it, the token is stuck one wei below its threshold forever. That is a cheap, permanent denial of service against any token an attacker dislikes. Graduation therefore escrows the liquidity and flags it instead of failing, so a DEX-side problem can never brick a launch.

05Four bugs that only appeared against a live chain

Everything above passed tests. These did not surface until the stack ran against a real deployment, and they are the most transferable part of the exercise.

The indexer had never actually run

It requested log ranges wider than the public RPC would serve, so every call was rejected and nothing was ever indexed. Providers disagree about this limit by two orders of magnitude, and the cap is not discoverable in advance. The fix was to treat a refusal as information — narrow the range and retry the same starting point — rather than to hardcode a number that is wrong somewhere.

Launches were indexed but never became tokens

The projection only updated a row the UI had already created. But the factory is permissionless: anyone can call it directly, and any launch that did not come through our own interface had nothing to update. Those tokens were recorded as events and stayed invisible forever. A permissionless contract needs an indexer that inserts, not one that assumes it already knows.

Trades were indexed but never projected

Sixty trade events sat in storage while every token showed a price of zero. The fix was also a lesson: we sync the curve's own reported state rather than accumulating deltas from events. Accumulating double-counts on any reorg or replay, and re-deriving the curve maths off-chain means maintaining a second implementation free to drift from the first.

Financial amounts routed through floating point

An 18-decimal base unit needs more significant digits than a double carries. Amounts were silently losing their low digits — small enough to look like rounding, large enough to be wrong. Financial values are formatted to exact decimal strings end to end now, never converted through a float.

All four are the same shape: the tests encoded what we believed, and the chain disagreed. None were caught by more unit tests. They were caught by running the real stack against a real deployment and reading what came back.

06Privileged functions behind a delay

The factory owner can change where future launches send their money. That is worth protecting even though it cannot touch a live curve — each curve caches its payout addresses at initialisation, so existing tokens keep the terms they launched under.

Ownership moved to a timelock. Changing wallets, fees, supply parameters or the DEX adapter now has to be queued publicly and survive a delay before it can execute, which turns a silent compromise into a visible one someone can cancel. Transferring ownership — the only irreversible action — is behind the same delay.

One exception was deliberate. The graduation target is retuned as traffic changes and should not need a two-day wait, so it has a second caller confined to a band the owner sets slowly. The rest of the economics stays behind the delay: the same call that sets the target also sets the trading fee, and opening that up would have handed a fast path to something that should never be fast.

A timelock alone protects nothing

A delay only helps if someone is watching during it. The alerting that fires when a privileged call is queued matters more than the alert when one executes — by then the window to cancel has closed.

07What the port actually produced

2chains, one product
42contract tests, incl. live mainnet-fork
0changes to the existing Sui path
1%fee, identical on both

The constraint that shaped everything was that the running Sui platform could not regress. The chain-aware surface was added alongside the existing one rather than replacing it, so the Sui code path is byte-for-byte what it was. Multi-chain support that breaks the chain you already have is not a port, it is a migration with extra steps.

MemeHedge runs on Sui today, with Monad support built and running on testnet. If you are doing a similar port: settle the custody model first, keep the economics identical unless you have a reason not to, and do not trust anything until it has run against a real deployment. The tests will agree with you right up until the chain does not.

Meme Hedge — launch and trade meme coins on Sui. Browse live tokens.