arkade:<asset> → arkade:<asset> route: BTC against an asset such
as USDT. Exactly one side is BTC; the client refuses an asset-for-asset swap.
- an initialized Arkade
Walleton mutinynet: Create Your Wallet, with{ isMainnet: false }and the mutinynet operator,https://mutinynet.arkade.sh. The mainnet registry lists no asset market today, and the mutinynet registry lists BTC/USDT.
EventSource setup from
Swap Client instead.
Discover and Price
The mutinynet BTC/USDT card lists a price feed andfee_bps and no relay, so
quote calculates the terms from them and contacts no solver. The card
reserves no inventory: only a fill commits the solver. To limit which
registries and markets can price a swap, set policy.allowedRegistries and
policy.selectMarket.
amount is a bigint in the asset’s smallest unit: sats
for BTC, hundredths for USDT (2 decimals). amountOn says which side it pins.
Convert what a user types with Amount.parse
(Swap Client).
amountOn: 'take' works when you buy an asset with BTC. Selling an asset
for BTC through a card that names a relay is exact-in only, so set the amount
on the give side there.
Keep what you receive within the card’s bounds. The solver checks its
bounds against what it pays out, which is what you receive. With
amountOn: 'give', the client does not check them: a swap outside them is
funded, the solver does not fill it, and you must cancel it to get the deposit
back. With amountOn: 'take', the client checks them and throws
UnsupportedRoute before anything is funded. A card that names a relay
accepts only amountOn: 'give' when you sell an asset for BTC.
Show the deposit, receive amount, and fee before funding. accept does not
check a fee ceiling: compare quote.fee.amount with your limit yourself, or
use exchange with maxFee. A quote expires. After that, accept throws
QuoteExpired, and it never gets a new quote for you. Ask again.
An open quote can block your next one. Until a quote is funded or
expires, the reference solver can refuse another quote from the same wallet.
quote then throws SwapRefusal with the reason quote_conflict. The
reference solver’s quotes last about 30 seconds by default. If you show a
price and the user does not fund it, wait until quote.expiresAt (unix
seconds) before you ask again.
Fund
accept registers the covenant with your wallet’s contract manager, saves the
record, and funds the swap. Registration marks the contract
genericallySpendable: false, so send, settle, and background renewal do
not spend a live swap.
accept saves before it funds. If you retry accept with the same quote, it
resumes the stored swap and does not fund twice.
To get a quote, check the fee, and accept in one call, use exchange. The fee
is charged in the asset you receive, so maxFee must name that asset. A
ceiling in any other asset throws. Nothing is funded until the fee passes:
- A card that names a relay: the reference solver keeps the carrier, and
quote.feecounts it. For example, a sale of 4.00 of an asset showed a fee of 345 sats, and only 15 of those were the spread. SetmaxFeein BTC and above 330 sats. - A card without a relay: the fill pays the carrier back to you with the
BTC, and
quote.feeleaves it out.
Track and Cancel
client.onUpdate reports each swap’s
status as an outcome. The client
watches the wallet’s contract events and persists before it notifies:
open means funded and waiting for a solver. filled means done: the asset is
in your Arkade wallet. After a wallet restore, client.ready rebuilds the records
by scanning the wallet’s history for offer deposits.
An unfilled swap has no fill deadline, but the virtual output that holds
your deposit still expires with its batch. If the deposit stays unfilled past
that, the operator sweeps it, and the swap reports needs_recovery.
Recover a Swept Swap
brings it back. To take back an unfilled deposit, cancel the swap:
cancel submits the cancel transaction to Arkade without the solver’s
signature, and writes the cancelling → cancelled transition itself.
Cancellation races a fill, and losing that race is not an error. If the
deposit was already spent, the client checks which contract path spent it and
reports filled: the swap completed. If it cannot tell, cancel returns
needs_recovery, and the swap stays at cancelling. Nothing was swept, so
recover changes nothing and throws SwapDriveRefusedError (nothing-swept).
Show the swap to a person, and let the next pass check it again.
Swap One Asset for Another
The swap client and the reference solver serve only pairs with BTC on one side. To exchange one asset for another, make two swaps through BTC: sell the first asset for BTC, then spend exactly that BTC on the second asset. Each asset needs its own market with BTC.- You pay the spread twice.
- Ask for the second quote after the first swap fills. While a quote from your wallet is open, the reference solver can refuse another one.
- If the second swap does not fill, the BTC from the first stays in your wallet. Nothing swaps it back.
Want to learn more?
Markets and Quotes
The model underneath: how quotes are requested, priced, and committed.
Asset Swaps
The underlying contract: pricing, funding, lifecycle, and cancellation.
RFQ Protocol
Quote wire family, transport, settlement, and recovery requirements.