ZAMM Protocol — Developer Documentation

ZAMM (zenAtomic Market Maker) is a singleton smart‑contract implementing a gas‑minimised Uniswap V2–style constant‑product market. It wraps liquidity shares and custom tokens in the ERC‑6909 multi‑token standard and introduces transient storage credit so routers can chain swaps without redundant external transfers.

Table of Contents

  1. Overview
  2. Design Fundamentals
    1. Constant‑Product Pools
    2. ERC‑6909 LP Tokens
    3. Transient Storage Credit
  3. Contract Addresses & Deployment
  4. Pool Lifecycle
    1. make()
    2. makeLiquid()
    3. addLiquidity()
    4. removeLiquidity()
  5. Swapping
    1. swapExactIn()
    2. swapExactOut()
    3. swap()
  6. Transient Balance API
  7. Batch & Compressed Calls
  8. Fee Control
  9. Security Notes
  10. Example Workflows
  11. Quick ABI Reference
  12. License

1. Design Fundamentals

1.1 Constant‑Product Pools

Each pool obeys x·y = k. Pools are identified by poolId = keccak256(PoolKey) where PoolKey includes:

{
  id0, id1   // ERC‑6909 token IDs (0 = ERC‑20 / native ETH)
  token0, token1 // contract addresses (0 = native ETH)
  swapFee    // basis‑points fee, ≤ 10000
}

Ordering rules guarantee a single canonical ID for any unordered pair.

1.2 ERC‑6909 LP Tokens

Add‑/remove‑liquidity mints/burns a fungible ERC‑6909 token whose tokenContract == address(this) and id == poolId. Supply tracks totalLiquidity and supports transfer()/approve() natively.

1.3 Transient Storage Credit

During a transaction ZAMM can credit balances to the caller in transient storage (EIP‑1153 style). This acts like an in‑contract msg.sender → address(this) transfer costing ≈ 2 gas.

This mechanism lets routers build multihop paths without touch­ing external balances more than once — dramatically reducing gas.

2. Contract Addresses & Deployment

The constructor sets the deployer as feeToSetter. Use Fee Control functions to update feeTo and feeToSetter. No factory contract is required: pools are instantiated lazily on first addLiquidity() or makeLiquid().

3. Pool Lifecycle

3.1 make()

function make(address maker, uint256 supply, string uri) returns (uint256 coinId)

Deploy a new ERC‑6909 token (not paired) and mint supply to maker. The coinId is keccak256(selector, msg.sender, block.timestamp).

3.2 makeLiquid()

function makeLiquid(address maker,address liqTo,uint256 mkrAmt,
                    uint256 liqAmt,uint256 swapFee,string uri)
        payable returns (uint256 coinId, uint256 poolId, uint256 liquidity)

Same as make(), plus immediately creates an ETH ↔ Token pool and seeds initial liquidity with msg.value ⬌ liqAmt. Useful for token launches.

3.3 addLiquidity()

Add assets to an existing pool, receiving LP tokens. Supports transient credit and native ETH. Optimal ratios are calculated on‑chain.

3.4 removeLiquidity()

Burn LP tokens for underlying reserves. amount0Min/amount1Min guard against slippage.

4. Swapping

4.1 swapExactIn()

swapExactIn(PoolKey key, uint256 amountIn,
            uint256 amountOutMin, bool zeroForOne,
            address to, uint256 deadline) payable → uint256 amountOut

4.2 swapExactOut()

swapExactOut(PoolKey key, uint256 amountOut,
             uint256 amountInMax, bool zeroForOne,
             address to, uint256 deadline) payable → uint256 amountIn

Mirror of swapExactIn; refunding excess ETH when provided.

4.3 swap() (low‑level)

swap(PoolKey key, uint256 amount0Out, uint256 amount1Out,
     address to, bytes data)

Flash‑style primitive patterned after Uniswap V2. When to == address(this) the output is credited to transient storage, enabling efficient multihop sequences inside a router or multicall. If data is non‑empty, IZAMMCallee(to).zammCall will be invoked.

5. Transient Balance API

FunctionPurpose
deposit(token,id,amount)Pre‑credit balance. Use before calling swaps in the same tx.
recoverTransientBalance(token,id,to)Withdraw leftover credit at end of tx.
multicall(bytes[])Sequential internal delegatecall; preserves transient credit across calls.

Tip: If you build a router, structure calls as:

  1. deposit() for input tokens once
  2. series of swap() (or swapExactIn) with to=this
  3. final swapX with external to
  4. optional recoverTransientBalance

6. Batch & Compressed Calls

The fallback function implements a compressed calldata codec (Solady LibZip). Routers may send densely‑packed calls directly to ZAMM. For readability during development, prefer multicall.

7. Fee Control

setFeeTo(address) sets the protocol‑fee recipient. setFeeToSetter(address) transfers admin rights. Only the current feeToSetter (initialized to deployer) may call either function.

8. Security Notes & Best Practices

9. Example Workflows

9.1 Single‑hop ETH → DAI

// pseudo router
zamm.swapExactIn{value:1 ether}(
  PoolKey(0, DAI_ID, address(0), DAI, 30), // 0.30 % fee
  1 ether,
  1800e18,       // minimum DAI out
  true,
  user,
  block.timestamp + 5 minutes
);

9.2 Two‑hop WBTC → ETH → USDC with Transient Credit

bytes[] calls = new bytes[](3);
// 1) credit WBTC once
calls[0] = abi.encodeWithSelector(ZAMM.deposit.selector, WBTC, 0, 1 btc);
// 2) WBTC→ETH, output kept as credit
calls[1] = abi.encodeWithSelector(ZAMM.swap.selector,
        keyWBTC_ETH, 0, ethOut, address(this), "");
// 3) ETH→USDC to user
calls[2] = abi.encodeWithSelector(ZAMM.swapExactIn.selector,
        keyETH_USDC, ethOut, minUsdc, true, user, deadline);
zamm.multicall(calls);

9.3 Bootstrap Token Launch

(coinId, poolId, lp) = zamm.makeLiquid{value:100 ether}(
    msg.sender, msg.sender, 1_000_000 * 10**18,   // mint & send tokens
    100_000 * 10**18,                             // paired token amount
    50,                                           // 0.5 % fee
    "ipfs://…"
);

10. Quick ABI Reference

Function (public)Key ArgsReturns
makemaker,supply,uricoinId
makeLiquidmaker,liqTo,mkrAmt,liqAmt,swapFee,uricoinId,poolId,liquidity
addLiquidityPoolKey,amount0Desired,amount1Desired,…amount0,amount1,liquidity
removeLiquidityPoolKey,liquidity,…amount0,amount1
swapExactInPoolKey,amountIn,…amountOut
swapExactOutPoolKey,amountOut,…amountIn
swapPoolKey,amount0Out,amount1Out,to,data
deposittoken,id,amount
recoverTransientBalancetoken,id,toamount
multicallbytes[] databytes[] results
setFeeTo / setFeeToSetteraddress

11. License

Code released under MIT. Documentation © 2025 z0r0z. No warranty.