Skip to main content

launkr

Launch and trade restricted SIP-010 tokens on Launkr — a protected token launcher and XYK AMM on Stacks. Deploy a token, open a bonding or direct pool, and trade STX for tokens via the singleton contract. Works on both mainnet and testnet.

Source facts

Repository
aibtcdev/skills
Last source activity
October 8, 2026 at 09:06
Detected SKILL.md language
English
Stars
11
Forks
52

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
4 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
launkr
description
Launch and trade restricted SIP-010 tokens on Launkr — a protected token launcher and XYK AMM on Stacks. Deploy a token, open a bonding or direct pool, and trade STX for tokens via the singleton contract. Works on both mainnet and testnet.
metadata
{"author":"rather-labs","author-agent":"Launkr by Rather Labs","user-invocable":"false","arguments":"launch | create-pool | get-pool | quote-buy | quote-sell | swap-buy | swap-sell | swap-and-burn | quote-swap-and-burn | is-paused | set-fee-receiver | accept-fee-receiver","entry":"launkr/launkr.ts","mcp-tools":"deploy_contract, call_contract, call_read_only_function","requires":"wallet","tags":"l2, defi, write, requires-funds"}
# Launkr Skill Launch and trade restricted SIP-010 tokens on Launkr — a protected token launcher and AMM on the Stacks blockchain. Contracts are public and permissionless — any agent with a Stacks wallet can call them directly. No intermediary, no custody, no fee to any third party beyond Launkr's own protocol fee (see below). **What Launkr is:** A singleton XYK AMM that hosts N pools. Each pool trades STX against a *restricted* SIP-010 token. "Restricted" means composability, not custody: transfers to an ordinary wallet always succeed; only sending to a *contract* is gated (allowlisted principals/code-hashes, initially just the singleton). This guarantees fee capture on every *swap* — trades must go through the singleton — without freezing the token in your wallet. See `AGENT.md`'s "What NOT to do" for the precise on-chain logic and why an earlier draft of these docs described this restriction incorrectly. **Two pool modes:** - **Bonding** (`create-pool-bonding`) — Starts with virtual reserves. No STX seed required. Fee: 1%. Automatically graduates to direct mode when real STX collected crosses the graduation threshold. - **Direct** (`create-pool-direct`) — Starts with a real STX seed (≥ 100 STX). Fee: 5%. **Hash gate — critical:** The singleton verifies that each token's source is byte-identical to the on-chain template. Always fetch the template source verbatim from the Launkr API or the Hiro API — never modify it, even whitespace. Any change breaks the singleton's hash-based allowlist check (`ERR_TOKEN_NOT_OURS u201`). --- ## Protocol Info Get contract IDs, floors, and fee schedule: ``` GET https://launkr.io/api/protocol?network=mainnet GET https://launkr.io/api/protocol?network=testnet ``` **Mainnet contracts** (redeployed 2026-07-16, dropping an earlier `-mn-demo` staging suffix — verified live on-chain, see worked examples below): - Singleton: `SP2ABWV7JE5SFV1A1BDS8HARP2QY7QRPGC9Z367PM.lp-singleton-v6` - Template: `SP2ABWV7JE5SFV1A1BDS8HARP2QY7QRPGC9Z367PM.restricted-token-template-v6` - Trait: `SP2ABWV7JE5SFV1A1BDS8HARP2QY7QRPGC9Z367PM.restricted-ft-trait-v6` **Testnet contracts:** - Singleton: `ST2ABWV7JE5SFV1A1BDS8HARP2QY7QRPGC9KJJYWE.lp-singleton-v6` - Template: `ST2ABWV7JE5SFV1A1BDS8HARP2QY7QRPGC9KJJYWE.restricted-token-template-v6` - Trait: `ST2ABWV7JE5SFV1A1BDS8HARP2QY7QRPGC9KJJYWE.restricted-ft-trait-v6` **How `launkr.ts` resolves these — read this before assuming "always fetch fresh" means "always trust what comes back."** Every subcommand fetches `GET /api/protocol` at runtime *and* compares the result against the two addresses pinned in `NET_CONFIG` at the top of `launkr.ts`. If they agree, the live values are used. If they differ, the command **refuses to run** and prints both. That refusal is deliberate, and it exists because the obvious version of this — "fetch live, fall back to the pinned copy" — quietly destroys the deploy verification described in section 4. `launch` byte-compares the API's `clarityCode` against the on-chain source of the *template address*; if that address also comes from the API, both sides of the comparison have the same origin, and the check can only catch launkr.io disagreeing with itself rather than a compromised or hijacked launkr.io, which is the threat it was written for. The same applies to the singleton: a substituted address collects a `--mode direct` STX seed under an `eq` post-condition that authorizes the transfer perfectly happily. So the two sources check each other. A genuine redeploy shows up as a clear refusal naming both addresses — verify the new ones independently on the explorer and update `NET_CONFIG`. `--allow-config-change` overrides the refusal for a single invocation and is available on every subcommand, but it puts full trust in the API response for that run: use it only after confirming the redeploy on-chain yourself. `get-pool`, `quote-buy`, and `quote-sell` are pure reads and take an explicit `--network mainnet|testnet` flag. `launch`, `swap-buy`, and `swap-sell` do **not** — they always follow whichever wallet is currently loaded (set by the `NETWORK` env var at wallet-creation time), because that's what actually determines the broadcast target no matter what a flag says. An earlier version of this skill had a `--network` flag on every command, including the three that sign transactions — it silently had no effect on where the transaction was actually sent, since `callContract`/ `deployContract` read the network from the account, not from any parameter. Confirm which wallet/network is active before running a write command rather than expecting a flag to control it. ## Fees | Mode | Treasury | Protocol | Total | |---|---|---|---| | Bonding | 0.90% | 0.10% | 1.00% | | Direct / Graduated | 4.50% | 0.50% | 5.00% | | swap-and-burn | — | — | 0% (graduated pools only) | ## Protocol Floors | Parameter | Minimum | Maximum | |-----------|---------|---------| | supply (atomic units) | `100000000000000` | `1000000000000000000000` | | decimals | — | `18` | | stxSeed (direct mode) | `100000000` (100 STX) | — | | virtualStx (bonding mode) | `500000000` (500 STX) | — | | graduationThreshold | `2000000000` (2000 STX) | `10000000000000` (10M STX) | | graduationThreshold | — | 10× virtualStx | --- ## 1. Launch a token Two sequential on-chain transactions. `POST /api/launch` returns unsigned Clarity source + call params for both — you sign and broadcast each yourself with your own key. **Do not send step 2 until step 1 confirms on-chain.** ``` POST https://launkr.io/api/launch { "network": "mainnet", "deployerAddress": "<your STX address>", "name": "My Agent Token", "symbol": "MAT", "supply": "1000000000000000", // atomic units, 6 decimals — e.g. this = 1B tokens "mode": "bonding", "virtualStx": "500000000", // uSTX, min 500000000 (500 STX) "graduationThreshold": "2000000000",// uSTX, min 2000000000 (2000 STX) "feeReceiver": "<STX address that gets 90% of swap fees>" } ``` > **Gotcha:** `virtualStx` / `graduationThreshold` (bonding) or `stxSeed` > (direct) must be **top-level** fields in this POST body, NOT nested under > a `bondingMode`/`directMode` object — even though the `GET /api/protocol` > response's own example schema shows them nested. Nesting them silently > fails validation with `"virtualStx must be >= 500000000 uSTX"` even when > the value is valid. `launkr.ts` already builds the flat body correctly. Response gives you two steps: 1. **contract-deploy** — the token's Clarity source (byte-frozen, don't edit it — see the hash-gate warning above). 2. **contract-call** `create-pool-bonding` (or `create-pool-direct`) on the singleton — only send after step 1 confirms. Poll `GET https://api.hiro.so/extended/v1/tx/{txid}` until `tx_status: "success"`. `create-pool-bonding` args, in order: `token` (trait_reference/principal) · `name` (string-ascii 32) · `symbol` (string-ascii 32) · `decimals` (uint) · `supply` (uint) · `uri` (optional string-utf8 256) · `virtual-stx` (uint) · `graduation-threshold` (uint) · `fee-receiver` (principal) **`decimals` is not a property of the template.** This is the arg most likely to be assumed away. The byte-frozen template ships `(define-data-var token-decimals uint u0)`, and the *only* thing that ever sets it is the token's one-shot `initialize()`, which the singleton calls during pool creation using this very argument. A token that is deployed but has no pool yet reports `get-decimals` → `u0`, because it has not been initialized. `launkr.ts` therefore takes `--decimals` explicitly (default `6`, matching the `tokenDecimals` constant `/api/protocol` reports), checks it against the API's arg like every other parameter, and never reads it back off an uninitialized token. Getting this wrong is permanent — `initialize()` runs once and then answers `ERR_ALREADY_INITIALIZED` forever. `create-pool-direct` is the same shape with `stx-seed` (uint) replacing `virtual-stx`/`graduation-threshold`, and **requires a matching STX post-condition** (`eq`, amount = stxSeed) since it pulls real STX from the caller at creation. Bonding mode pulls no STX at creation — its post-conditions array is empty. > **Resolved (2026-08-05, corrected 2026-08-17):** an earlier version of > `launkr.ts` sent an explicit `Some("")` instead of `none` for an omitted > `uri`, working around a `BadFunctionArgument` broadcast rejection. That > rejection turned out to be specific to a *different* environment (the > published `@aibtc/mcp-server` npm package's own dependency resolution) > rather than a Stacks/Clarity issue — a bare `noneCV()` broadcasts and > confirms fine against this repo's own pinned `@stacks/transactions@7.3.1`. > (A prior version of this note cited two testnet txids for this that > don't exist on-chain — written before the test was actually run, an > error rather than a stale reference. Verified for real afterward: mainnet > txid `29b7e58d636d2be118ca658707220e3f5ff19100fbb264f5aeb00c765202e390`, > `(ok true)`, calling `set-token-uri` with a bare `noneCV()` signed with > this exact pinned dependency version.) `parseLaunkrArg` sends a proper > `none` again — a token launched without `--uri` correctly has `none` > on-chain, not an empty string. ## 2. Quote a trade (free, read-only, no gas) ``` POST https://api.hiro.so/v2/contracts/call-read/<singleton-address>/<singleton-name>/quote-buy { "sender": "<any-stx-address>", "arguments": ["<hex token principal>", "<hex uint stx-in-ustx>"] } ``` Same shape for `quote-sell` (tokens-in → stx-out) and `quote-swap-and-burn` (graduated pools only). Returns `none` if the pool doesn't exist or the input is zero — always check for `none` before using the result. Also useful: `get-pool` (full pool state: reserves, mode, graduation progress, fee-receiver) and `is-paused` (protocol-wide kill switch). ## 3. Swap (buy / sell) Direct contract calls on the singleton — no API round-trip needed once you have a quote. **Buy:** `swap-exact-stx-for-tokens(token, stx-in, min-tokens-out, deadline, recipient)` **Sell:** `swap-exact-tokens-for-stx(token, tokens-in, min-stx-out, deadline, recipient)` — every Launkr token uses the **identical internal FT asset name `strategy-token`**, regardless of display name/symbol (verified against the deployed byte-frozen template source, both networks — only the contract address varies). > **Gotcha — fixed in `launkr.ts`, verified on-chain on both testnet and > mainnet:** under `PostConditionMode.Deny`, **every principal that moves > an asset in the transaction needs a post-condition, not just the > caller.** A buy has the singleton sending back both the FT payout *and* > STX (the treasury + protocol fee legs); a sell has the singleton paying > out STX (proceeds + both fee legs). Post-conditioning only the caller's > own leg — which looks like the obviously-correct, safe thing to do — > aborts with `abort_by_post_condition` every time, because the > uncovered singleton-originated transfers get flagged. One > post-condition per `(principal, asset)` pair covers the aggregate amount > that principal sends of that asset across the whole transaction, so this > doesn't need one condition per individual fee leg. The complete correct > set: > - **Buy** — caller `eq stx-in` (uSTX), singleton `gte 0` (uSTX, covers > the fee legs — not meaningfully boundable since that's the contract's > own fee math, not caller input), singleton `gte min-tokens-out` (FT, > `strategy-token` — this is the real slippage guard). > - **Sell** — caller `eq tokens-in` (FT, `strategy-token`), singleton > `gte min-stx-out` (uSTX — covers proceeds + both fee legs in one > aggregate check, and is itself the real slippage guard). Always call `quote-buy`/`quote-sell` first and set `min-tokens-out` / `min-stx-out` a few % under the quote as a slippage guard. Use `deadline: 0xffffffff` (4294967295) if you don't need a block-height cutoff. **Broadcast is not confirmation.** Every write subcommand waits for its transaction to reach a terminal on-chain status before printing, and reports `success: true, confirmed: true` only once it actually succeeded. This matters most for swaps, which really do abort on slippage and post-condition failures — an earlier version printed `success: true` the instant the txid came back, which made "the trade happened", "the trade is pending" and "the trade aborted" indistinguishable in the output. Pass `--no-wait` for fire-and-forget behaviour; that output is labelled `broadcast: true, confirmed: false` and carries a note telling you to poll the explorer. **Kill switch.** The singleton has a protocol-wide pause that every write checks on-chain first (`check-not-paused`). `bun run launkr/launkr.ts is-paused` reads it for free, and `launch`, `create-pool`, both swaps and `swap-and-burn` all pre-flight it so a paused protocol costs you nothing instead of a transaction fee for a guaranteed abort. **`swap-and-burn(token, stx-in, min-tokens-burned, deadline)`** — spends STX to buy tokens and burns them in the same call. Fee-free (0%), and the STX stays in the pool as protocol-owned liquidity. Graduated pools only — `ERR_NOT_GRADUATED (u217)` on a bonding pool, and `quote-swap-and-burn` returns `none` there. Note the signature has **no `recipient`**: nothing is paid out. Post-conditions under Deny: caller `eq stx-in` (uSTX), plus singleton `gte min-tokens-burned` (FT `strategy-token`) — the burn is an outflow from the singleton's own balance, performed under `(with-ft token-principal "strategy-token" dy)`, so Deny mode requires it covered like any other send. There is no fee leg, so no STX leaves the singleton. ## 4. Recovery, fee-receiver transfer, and other operations **`create-pool`** — recovery path for when `launch` deployed the token but the pool-creation step failed or was interrupted (`launch` is two separate transactions with no atomicity between them). Takes `--token <already-deployed-principal>` plus the same params `launch` would have used, and runs only the pool-creation call — built directly from the documented `create-pool-bonding`/`create-pool-direct` signature above rather than round-tripping through `/api/launch` again (nothing in that response for this step isn't already fully determined by your own input). Re-running `launch` itself after a failed step 2 would deploy a *second* token, not resume — use `create-pool` instead. > `create-pool` takes `--decimals` (default `6`) for the reason given in > section 1: on the exact state this command exists to recover from — token > deployed, no pool — the token has not been initialized, so reading > `get-decimals` off it returns `u0` rather than the truth. An earlier > version did read it back, which would have permanently initialized the > recovered token with 0 decimals. The command also refuses up front if the > token reports a non-zero total supply, since that means `initialize()` has > already run and pool creation could only abort with > `ERR_ALREADY_INITIALIZED`. **`set-fee-receiver` / `accept-fee-receiver`** — the singleton's two-step fee-receiver transfer (`set-pending-fee-receiver` proposed by the current receiver, `accept-fee-receiver` confirmed by the new one), exposed as CLI subcommands. The fee-receiver collects 90% of swap volume permanently once a pool is created — this is the only correction path if it was set wrong. **Deploy verification:** `launch` now fetches the real template source on-chain (`GET /v2/contracts/source/...`) and byte-compares it against the API's `clarityCode` *before* deploying, rather than trusting the API response and finding out only after the deploy fee is spent that the singleton would have rejected it (`ERR_TOKEN_NOT_OURS`) anyway. **Pool-arg verification:** `validatePoolStepMatchesRequest` (see `launch`) checks **all nine** args against what was requested — token principal, name, symbol, decimals, supply, uri, the curve parameters (`virtual-stx`/`graduation-threshold` for bonding, `stx-seed` for direct) and fee-receiver — plus the exact arg count for the mode and the function name itself. Every one of these is capable of coming back wrong from the API, and each of the later additions closed a real hole: the curve parameters are what the entire price curve is built from, `uri` and `decimals` are both written permanently by the one-shot `initialize()`, and the token principal decides which token the pool is even for. `launch` also reports the principal derived from the address it actually deploys under plus the contract name it actually deploys, not the `tokenPrincipal` the API echoes back — a mismatch is a warning on stderr, not something the user is silently handed. **Does graduating a pool unlock token transfers?** Not applicable the way this question originally assumed — see the correction below. Graduating only changes the pool's `mode` field (bonding fee 1% → graduated fee 5%, and enables `swap-and-burn`); it never touches the token contract's allowlist, before or after graduation. **Correction (2026-08-17):** an earlier version of this doc (and
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub