Conversation
…or swap confirmations Follow-up review of #414. Six changes, one of them a blocker. **create-pool would permanently initialize a recovered token with 0 decimals.** #414 replaced a hardcoded `uintCV(6)` with a `get-decimals` read off the already-deployed token, on the reasoning that decimals is "not a parameter to get right, it's a fact to look up." That is inverted for this command: 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* from that very argument. So on the exact state `create-pool` exists to recover from — token deployed, no pool — the read returns u0 and feeds 0 straight back in, unfixable afterwards (ERR_ALREADY_INITIALIZED). Now an explicit `--decimals` (default 6), plus an up-front refusal if the token reports a non-zero total supply, which means initialize() has already run. **The live-config fetch had disabled the deploy verification.** Making /api/protocol authoritative for `template` meant `verifyDeploySourceMatchesTemplate` compared an API-supplied contract body against an API-supplied template address — same origin on both sides, so it could only catch launkr.io disagreeing with itself, not the compromised launkr.io it was written for. Same for `singleton`: a substituted address collects a `--mode direct` STX seed under an `eq` post-condition that authorizes the transfer happily. Still fetches live (that is how a redeploy gets noticed) but NET_CONFIG is the trust anchor again: drift is refused and printed, with `--allow-config-change` as the deliberate override. Also: - swaps and fee-receiver rotation now wait for a terminal on-chain status instead of printing `success: true` on broadcast; `--no-wait` keeps the old behaviour but labels it `broadcast: true, confirmed: false` - `validatePoolStepMatchesRequest` covers all nine args — `uri` and `decimals` were unchecked, and both are written permanently by initialize() - `launch` reports the locally derived token principal rather than the API's unverified `intent.tokenPrincipal` - adds `swap-and-burn`, `quote-swap-and-burn` and `is-paused`, all documented in SKILL.md but previously unreachable; write paths now pre-flight the pause Signatures and post-conditions for the new commands were read off the deployed singleton's interface and source, not inferred. Config-drift refusal and override both verified against the live API; new read commands verified against a real mainnet pool. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G7a33ETgZSZrwKbfQbZ6xJ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up review of #414 (
launkrskill). Six fixes; one is a blocker that would permanently misconfigure a token.Blocker —
create-poolwould initialize a recovered token with 0 decimals#414 replaced a hardcoded
uintCV(6)with aget-decimalsread off the already-deployed token, reasoning it is "not a parameter to get right, it's a fact to look up." That is inverted for this command.From the deployed template source:
The only thing that ever sets it is the token's one-shot
initialize()— andinitialize()is called by the singleton, during pool creation, from this very argument ((try! (contract-call? token initialize name symbol decimals supply uri))inlp-singleton-v6).So on the exact state
create-poolexists to recover from — token deployed, no pool yet —get-decimalsreturnsu0, because the token has no decimals yet. That 0 was fed straight back intocreate-pool-*, permanently initializing the recovered token with 0 decimals.initialize()runs once and answersERR_ALREADY_INITIALIZEDforever after. Strictly worse than the hardcoded6it replaced.Now: explicit
--decimals(default6, thetokenDecimalsconstant/api/protocolreports), plus an up-front refusal if the token reports a non-zero total supply —initialize()mints the whole supply, so that means it has already run and pool creation could only abort.Security — the live-config fetch had quietly disabled the deploy verification
fetchProtocolConfigmade/api/protocolauthoritative for bothsingletonandtemplate, withNET_CONFIGdemoted to a silent fallback. ButverifyDeploySourceMatchesTemplatecompares the API'sclarityCodeagainst the on-chain source at the template address — and once that address also came from the API, both sides of the comparison shared an origin. The check could only catch launkr.io disagreeing with itself, not the compromised or hijacked launkr.io it was written for.Same for
singleton: a substituted address collects a--mode directSTX seed under aneqpost-condition that authorizes the transfer perfectly happily. (Swaps are largely self-protecting — an imposter can't satisfygte min-tokens-outon the real token's FT.)Still fetches live, because that is how a redeploy gets noticed — but
NET_CONFIGis the trust anchor again. Drift is refused and both addresses printed;--allow-config-changeis the deliberate per-invocation override.Verified against the live API by temporarily mis-pinning the singleton:
and
--allow-config-changeproceeding with a stderr warning.Also fixed
launch/create-poolwere changed to wait for confirmation precisely because broadcast ≠ confirmed, but the swaps and fee-receiver rotation still printedsuccess: truethe instant a txid came back — leaving "trade happened", "trade pending" and "trade aborted on a post-condition" indistinguishable. Waiting is now the default everywhere;--no-waitkeeps fire-and-forget but labels itbroadcast: true, confirmed: false.validatePoolStepMatchesRequestnow covers all nine.uriwas caller-supplied and never compared;decimalshad nothing to compare against until--decimalsexisted. Both are written permanently by the one-shotinitialize().launchvalidated against the locally derived${address}.${contractName}but printed the API'sintent.tokenPrincipal(and built the launkr.io link from it). Now reports the derived one; a mismatch warns on stderr.swap-and-burn,quote-swap-and-burnandis-paused; none were callable. Added, with signatures and post-conditions read off the deployed singleton's interface and source rather than inferred from the prose —swap-and-burnhas norecipient, is fee-free, and burns from the singleton's own balance under(with-ft token-principal "strategy-token" dy), so Deny mode needs that outflow covered. Write paths now pre-flightis-paused(one free read) instead of paying a fee to discover a guaranteed abort.Verification
bun run typecheck,bun run validate(202/202),bun test launkr/launkr.test.ts(38/38, up from 25)--decimalsbounds, and theuri/decimalsarg checksis-paused,get-pool,quote-buy,quote-swap-and-burnall run against live mainnet;quote-swap-and-burncorrectly returnsnoneon a bonding pool--allow-config-changeoverride both exercised end-to-endSeparate finding, not fixed here
bun run typecheckonly coverssrc/**/*—tsconfig.jsonhas"include": ["src/**/*"], so no skill CLI is type-checked, including this one. CLAUDE.md and CONTRIBUTING both present it as the pre-PR gate. It is not theoretical: two of the edits in this PR produced type errors that the repotypecheckreported clean and a directtscon the file caught immediately. A repo-wide run over the skill directories currently reports ~144 errors, so fixing it is its own piece of work rather than something to fold in here.🤖 Generated with Claude Code
https://claude.ai/code/session_01G7a33ETgZSZrwKbfQbZ6xJ