Skip to content

Swap KAS and kUSDT

KasPool is a KAS⇄kUSDT pool on Kaspa L1. It holds a reserve of KAS and a reserve of kUSDT and prices every swap by the constant-product rule: after a swap, the product of the two reserves may not shrink. Each swap is a single Kaspa transaction.

  1. Get a quote. Your app reads the pool’s current state: the KAS reserve X and the kUSDT reserve Y.
  2. Build the transaction. The app builds a transaction that spends the pool UTXO, the pool’s kUSDT reserve and your own coins, and names the exact amount you receive.
  3. Sign and submit. Your wallet signs your inputs. The pool covenant accepts the transaction only if the reserves after the swap satisfy the constant-product rule with the fee applied.
A KasPool buy transaction (KAS in, kUSDT out)Inputs: #0 Pool (entry buy; KasPool leader); #1 kUSDT reserve (KCC20) (transfer leader; owner = pool ID); #2 Buyer's KAS coins (pay kas_in and the network fee). Outputs: #0 Pool (successor) (X + kas_in − cut; protocol KAS + cut); #1 kUSDT reserve (successor) (amount Y′ + protocol tokens); #2 Buyer's kUSDT state (amount = buyer_state.amount); #3 KAS change (plain output). The pool checks (X + 0.997·kas_in) · Y′ ≥ X · Y.InputsOutputsOne transactionPool.buy(kas_in, buyer_state)spends the exact pool UTXOit was built onchecks, in 128-bit integers:(X + 0.997·kas_in) · Y′≥ X · Y#0 Poolentry buy; KasPool leader#1 kUSDT reserve (KCC20)transfer leader; owner = pool ID#2 Buyer's KAS coinspay kas_in and the network fee#0 Pool (successor)X + kas_in − cut; protocol KAS + cut#1 kUSDT reserve (successor)amount Y′ + protocol tokens#2 Buyer's kUSDT stateamount = buyer_state.amount#3 KAS changeplain outputKasPool familykUSDT familyplain KAS (no covenant)X = KAS reserve, Y = kUSDT reserve, cut = 0.05 % protocol share
A buy: KAS in, kUSDT out. Two covenant families move in one transaction; the pool forces the reserve’s successor.

Every swap pays 0.30 % of its input: 0.25 % stays in the pool for liquidity providers, 0.05 % accrues to the protocol. The best amount a swap can return is:

effective = floor(input × 9 970 / 10 000)
output = floor(reserve_out × effective / (reserve_in + effective))

For a buy, the input is KAS, reserve_in is X and reserve_out is Y. For a sell, the input is kUSDT, reserve_in is Y and reserve_out is X. Asking for one base unit more than this quote is rejected.

Pool You send You receive (quote)
10 000 KAS and 1 000 kUSDT 100 KAS about 9.87 kUSDT

A round trip (KAS to kUSDT and back) returns a little less than you started with, about the 0.6 % paid in fees. You also pay the normal Kaspa network fee in KAS.

A swap transaction spends the exact pool UTXO it was built on and names the exact amount you receive. Two things follow:

  • Nobody can worsen your price after you sign. If another swap lands first, it spends that pool UTXO. Your transaction then becomes invalid, rather than executing at a worse price.
  • You may need to retry. In a busy moment your app may have to re-quote and re-sign against the new pool state.
  • The wallet asks before a worse price. It goes ahead on its own only if the new quote is at least as good as the one you confirmed. Your slippage minimum stays the hard floor. (This is being built in the wallet now.)

A sell is the mirror image. Your kUSDT state is an input, any remaining kUSDT returns to you as change, and the KAS goes to an output you choose.

A KasPool sell transaction (kUSDT in, KAS out)Inputs: #0 Pool (entry sell; KasPool leader); #1 kUSDT reserve (KCC20) (transfer leader; owner = pool ID); #2 Seller's kUSDT state (transfer_delegator; owner signs). Outputs: #0 Pool (successor) (X − kas_out); #1 kUSDT reserve (successor) (amount Y′ + protocol tokens + cut); #2 Seller's kUSDT change (amount = change_state.amount); #3 KAS to the seller (plain output, less the network fee). The pool checks (Y + 0.997·in) · X′ ≥ Y · X.InputsOutputsOne transactionPool.sell(kas_out, change_state)spends the exact pool UTXOit was built onchecks, in 128-bit integers:(Y + 0.997·in) · X′≥ Y · X#0 Poolentry sell; KasPool leader#1 kUSDT reserve (KCC20)transfer leader; owner = pool ID#2 Seller's kUSDT statetransfer_delegator; owner signs#0 Pool (successor)X − kas_out#1 kUSDT reserve (successor)amount Y′ + protocol tokens + cut#2 Seller's kUSDT changeamount = change_state.amount#3 KAS to the sellerplain output, less the network feeKasPool familykUSDT familyplain KAS (no covenant)X = KAS reserve, Y = kUSDT reserve, cut = 0.05 % protocol share
A sell: kUSDT in, KAS out. The seller’s state is a delegator input; the reserve state leads the kUSDT transfer.

To bound a dump of forged kUSDT, the KAS sells take out of the pool, less what buys bring in, may be at most 10 % of its KAS reserve per hour (the circuit breaker). Buys are not limited, and each buy gives sells back the KAS it brought. If your sell is over what is left, the app tells you that the pool lets at most so much KAS out until about a given time. Sell less, or wait for the next window.

The bridge’s guardian pauses the pool on any alarm, with the vaults and the minter. While it is paused:

Stops Keeps working
Buys, sells and adding liquidity Removing liquidity: you can always withdraw your Position

Only the pool admin resumes it.

Anyone can add KAS and kUSDT in the current ratio and receive a Position: a covenant UTXO that records liquidity shares and the key that owns them. Burning the Position returns at most its share of each reserve. The owner can transfer a Position to another key. See KasPool for the exact rules.