Architecture

Architecture

Current contract stack (v4, deployed)

                 ┌──────────────────────┐
                 │   User wallet (SEI)   │
                 └─────────┬────────────┘

                           │ buy() / sell()

              ┌────────────────────────────┐
              │  JellyPadToken            │  ← curve phase
              │  (one per launched memecoin)│
              └─┬────────────┬────────────┬─┘
                │            │            │
   1% protocol  │   99% SEI  │            │  ERC-20 mint
   fee          │            │            │
                ▼            ▼            ▼
        ┌─────────────┐  ┌──────────────┐  ┌────────────┐
        │ Factory     │  │ JellyPadStaker│  │ User wallet│
        │ (splits     │  │ (mints jpSEI, │  │ (gets MEME)│
        │  50/50)     │  │  delegates,   │  └────────────┘
        └─┬───────────┘  │  buffers)     │
          │              └───────────────┘
   50% to operator, 50% donateToBuffer
   (via factory.receive())

At graduation (mcap × seiUsdPrice ≥ graduationMcapUsd):
  ┌──────────────────────────────────────────────┐
  │  JellyPadToken._migrateToDex():             │
  │    1. take 1% graduation fee in jpSEI         │
  │       → factory                               │
  │    2. approve Saphyre router for the rest     │
  │    3. addLiquidity → creates (jpSEI, TOKEN)   │
  │    4. LP shares minted directly to 0xdEaD     │
  │       (mathematically unrecoverable)          │
  └──────────────────────────────────────────────┘


              ┌────────────────────┐
              │  Saphyre/DragonSwap │
              │  (jpSEI, MEME) pool │  ← post-grad trading
              │  permanent liquidity│
              │  LP burned at 0xdEaD│
              └────────────────────┘

Phase 1: The bonding curve

Each token is its own contract (JellyPadToken). On buy():

  1. Take 1% protocol fee. Forward to factory's receive().
  2. Stake the remaining 99% via JellyPadStaker.stake(false). The staker routes to its buffer if below target, otherwise delegates to the active validator. Either way, the contract receives jpSEI back.
  3. Compute tokens out from a Uniswap V2-style constant-product curve.
  4. Mint tokens to the buyer.
  5. Check graduation: if realSeiReserves × seiUsdPrice ≥ graduationMcapUsd, fire _migrateToDex() in the same tx.

On sell(tokenAmount):

  1. Compute the curve's gross SEI output from the constant-product formula.
  2. Call staker.instantUnstake(jpSeiAmount) for enough jpSEI to cover the gross. The redemption math sizes against the worst-case 4% tier so sells succeed even when the buffer is stressed; any positive dust stays in the contract as bonus reserves.
  3. Take 1% protocol fee off the gross. Forward to factory receive().
  4. Pay seller the remaining 99% in native SEI.
  5. Burn the tokens back into the curve (returned to the contract for resale).

The curve formula:

k         = virtualSeiReserves × virtualTokenReserves   (constant)
tokensOut = virtualTokenReserves - (k / (virtualSeiReserves + seiIn))

Initial reserves: 30 SEI virtual, 800M tokens. The virtual depth makes the curve start at a fair price even with zero real liquidity.

Phase 2: Graduation

When realSeiReserves × seiUsdPrice crosses the per-token graduationMcapUsd threshold, _migrateToDex() runs in the same transaction as the buy that triggered it.

  1. Take 1% graduation fee in jpSEI off the contract's balance. Send to the factory address. (Fee is in jpSEI not native SEI to avoid paying the staker's instant-unstake fee at the migration step. Factory accumulates yield-bearing principal as protocol revenue, then converts to SEI via the 21-day slow-unstake path.)
  2. Approve the Saphyre router for the remaining 99% jpSEI plus all curve-side TOKEN.
  3. Call router.addLiquidity(TOKEN, jpSEI, ..., to: 0xdEaD). Pair created if it doesn't already exist. The LP receipt is minted directly to 0xdEaD.
  4. Set graduated = true. Subsequent buy() / sell() on the curve revert. Trading happens on the Saphyre pair.

Phase 3: Post-graduation trading

The token is a clean ERC-20. The Saphyre pair (jpSEI, MEME) is its only liquidity. Users have three swap paths:

  • JellyPadRouter (buy(token,...) / sell(token,...)). The frontend default. Auto-stakes SEI to jpSEI on the buy side, auto-instantUnstakes on the sell side, applies the 1% protocol fee at each end. Most users go through this.
  • Saphyre directly. The pair contract is public. Anyone can swap against it via the standard V2 router.
  • Aggregators. 1inch, Squid, etc. route into the Saphyre pair like any other V2 pool. The protocol fee is bypassed on these paths since they don't call our router.
        ┌──────────────────────┐
        │  JellyPad frontend   │
        └─────────┬────────────┘
                  │ buy(token, minOut) payable

        ┌──────────────────────┐
        │  JellyPadRouter       │
        │  - 1% protocol fee    │
        │  - stakes SEI → jpSEI │
        │  - swaps on Saphyre   │
        └─┬─────────────┬──────┘
          │             │
          ▼             ▼
   ┌────────────┐  ┌─────────────┐
   │ JellyPad-  │  │ Saphyre pair│
   │ Staker     │  │ (jpSEI,MEME)│
   └────────────┘  └─────────────┘

Buys via the router auto-stake fresh SEI into jpSEI. Buys via Saphyre directly do not. A Uniswap V2 pool is a passive math contract; it doesn't call our token.

Fee routing

USER ACTION              FEE              ROUTING
────────────────────────────────────────────────────────────
factory.createToken()    0.01 SEI         100% → staker buffer
token.buy()              1% of msg.value  factory.receive() →
                                            50% operator / 50% buffer
token.sell()             1% of grossOut   factory.receive() →
                                            50% operator / 50% buffer
token._migrateToDex()    1% of jpSEI      → factory address (held as
                                            jpSEI; converted to native SEI
                                            via 21-day slow-unstake to
                                            treasury — buffer untouched)
router.buy()             1% of msg.value  factory.receive() (same split)
router.sell()            1% of grossOut   factory.receive() (same split)
staker.instantUnstake()  0.5–4% tiered    100% stays in buffer (lifts rate)

The MAX_OPERATOR_FEE_BPS = 7000 constant in the factory caps the operator share at 70%. Owner cannot raise it above that even by mistake.

Why the token isn't fee-on-transfer

A common alternative is a transfer hook on the token itself: tax every transfer 1% and route to the factory. JellyPad deliberately doesn't do this because:

  • Aggregators (1inch, Squid, etc.) blacklist fee-on-transfer ERC-20s
  • DEXes show "honeypot" warnings on the pair, deterring buyers
  • The token loses portability. Every wallet, lending app, and bridge has to special-case it

The router-only approach keeps the token a clean ERC-20.

Files at a glance

FilePurpose
contracts/src/JellyPadStaker.solIn-house liquid staker (jpSEI). Validator delegation, dynamic buffer, tiered fees
contracts/src/JellyPadToken.solThe launched token (curve + graduation)
contracts/src/JellyPadFactory.solDeploys tokens, collects creation fees, splits protocol fees 50/50
contracts/src/JellyPadRouter.solPost-graduation routing layer (one-tx user buys/sells)
contracts/src/interfaces/IStaking.solSei staking precompile ABI
contracts/src/interfaces/IDistribution.solSei distribution precompile ABI
contracts/src/interfaces/IDragonSwapRouter.solSaphyre/DragonSwap V2 ABI
contracts/test/JellyPadStaker.t.sol37 unit tests for staker logic
contracts/test/StakerIntegration.t.sol6 end-to-end tests with the real staker
contracts/test/JellyPadToken.t.sol19 tests covering token curve + graduation + LP burn
contracts/script/seiBash CLI wrapper for deploy + interaction