payScript, the script that receives the token or the payout. Anyone may sweep that output. The sweep asks for no signature.
The pool is one output, so one spend settles it. One sweep spends the pool once and settles up to four standing outputs. Output i settles input i. The next sweep spends the pool that sweep created. The batch matches Lending.
The pool holds a base asset and a quote asset. Its sat value stays 330. The reserves are the asset amounts.
A sale of base pays (quote * dxFee) / (base + dxFee) quote. dxFee is (sold * (10000 - feeBps)) / 10000. The pool adds the whole sale to the base reserve and removes only that quote. When feeBps is zero, dxFee equals the sale, and the product of the reserves stays the same. The fee stays in the reserves, and a fee above zero makes the product rise. Division rounds down. A sale of quote pays (base * dxFee) / (quote + dxFee) base, and the quote reserve grows by the whole sale. A sale whose dxFee or whose payout rounds to zero fails, and that output stays. feeBps is at least zero and below 10000.
Swaps in one batch run in input order. Each output replays the sales before it and prices from the reserves after those sales. The pool output carries the reserves after the last output. The party that builds the batch sets the input order, and earlier inputs get better prices. A swap output does not choose its position, so its price can be worse than the price it saw when it was funded.
A deposit carries both assets. open creates the pool from 660 sats, one carrier for the pool and one for the liquidity token. The first liquidity token amount equals the base amount deposited. The quote sets the first price. The ledger amount equals that base amount, so the pool script stays fixed.
A later deposit batch prices every output from the reserves on input 0. The quote on that output equals (dx * quote) / base. The tokens minted equal (supply * dx) / base. An output whose share rounds to zero fails, and the deposit stays.
To leave, the holder sends the liquidity token to a staging output. That send is the authorization. A later sweep burns the token and pays payScript both assets in the same spend. The base paid is (base * burned) / supply. The quote paid is (quote * burned) / supply. Both amounts must be at least one. One output that burns the whole supply receives both reserves, because that division is exact. Outputs that together burn the whole supply can leave a remainder on the pool. The ledger is gone, so a later deposit or swap fails, and the remainder stays on the pool. The same truncation applies to every exit, not only a full burn: each payout rounds down, and the dust stays in the reserves for the remaining holders.
A pool spend cannot share a transaction with an oracle read. The pool’s continuation and the attestation both require output 0 to carry their own script, and one output 0 cannot carry two scripts. Read the oracle in a separate transaction (Oracle Output).
If any check fails, the emulator does not cosign, so the standing output and the pool stay as they were. refund pays a standing output back to payScript and does not spend the pool.
The exit leaf on the pool is the NUMS point (Exit).
payScript is the receiver’s witness program. The asset ids and feeBps on a standing output match the pool, or new ProductPool does not name this pool. serverExitDelay is not a parameter. The client binds it (CSV delays).
pool_math.ark
product_pool.ark
product_deposit.ark
product_swap.ark
product_exit.ark
Shared parameters
The three standing contracts add
payScript. That is the witness program that receives the minted token, the asset the swap pays, or the payout.
ProductPool
deposit spends the pool and one to four deposits. It checks that each quote equals (dx * quote) / base, and that each output mints (supply * dx) / base liquidity tokens.
swap spends the pool and one to four swap outputs, in input order. An output that sells base is paid (quote * dxFee) / (base + dxFee) quote, and the base reserve grows by the whole sale. An output that sells quote is paid base by the same rule.
withdraw spends the pool and one to four staging outputs. It burns the liquidity tokens and checks each payout of both assets.
unilateral is the NUMS exit leaf. Nobody can spend it.
Standing outputs
ProductDeposit.open creates the pool from the first deposit. The liquidity token amount equals the base amount. sweep joins a later batch and requires output i to pay this output’s payScript.
ProductSwap.swap replays the earlier swap inputs, then requires output i to pay this output’s payScript the asset that output bought.
ProductExit.exit requires output i to pay this output’s payScript both assets for the tokens on this output.
refund on each standing output pays that output back to payScript. The pool is not an input.