Skip to content

Savings deposit fails with "CAT spend failed. Tail not revealed and parent is not a CAT" — backend builds lineage proof from the wrong key #5

Description

@kdc2000

Summary

POST /savings/deposit on api.circuitdao.com returns HTTP 422
{"detail":"CAT spend failed. Tail not revealed and parent is not a CAT"}
for any wallet whose BYC coin is not held by the first synthetic public key in the
synthetic_pks list. Since the circuitdao.com frontend always sends keys in derivation
order, any user whose BYC sits at a later derivation index (normal for address-rotating
wallets like Sage) can never deposit through the UI.

The wallet is not involved: the error occurs while the backend builds the unsigned bundle,
before any signing request. The affected coin's on-chain lineage is a completely standard
CAT v2 (verified: parent puzzle reveal uncurries to the CAT2 mod with the BYC tail).

Reproduction (public keys only, only_estimate_fee: true, nothing signed)

Wallet: Sage 0.12.11, ~19 addresses holding coins. The only BYC coin
(2d7600f1886d67e7e07fddaf72ccd463ede24fc75f4531ad8a6a177c1cc6597e, 16169 mojos,
height 9140937) is owned by derivation index 108. Its parent
(9ff24546b2f8f41fd7436968ea54bc82978a2f53e2d014aefaa39d1e2e778605) is a standard
BYC CAT2 coin whose inner puzzle hash is also index 108's p2 puzzle hash.

POST https://api.circuitdao.com/savings/deposit with
{"only_estimate_fee": true, "synthetic_pks": [...], "amount": 1000, "fee_per_cost": "5", "treasury_withdraw_amount": null}:

synthetic_pks (by derivation index) Result
[108] 200 OK, valid bundle
[108, 0] 200 OK
[0, 108] 422 "CAT spend failed. Tail not revealed and parent is not a CAT"
[21, 108] 422 same error
[108, 21, 23, 36, 64, 71, 74, 90, 96, 102] 200 OK
[21, 23, 36, 64, 71, 74, 90, 96, 102, 108] 422 same error
[0, 1, 4, 5, 6, 11, 12, 17, 19] (no 108) 422 "No … coins available" (expected)

Same coin, same key set — only the order of synthetic_pks changes the outcome.
Deposit succeeds iff the key owning the BYC coin (and its parent's inner puzzle) is first.

Coin discovery clearly uses all provided keys (last row finds no coins), so the
order-sensitivity is isolated to lineage-proof construction: the reconstructed parent coin
id doesn't match parent_coin_info, parent_is_cat ends up false, and
get_cat_solution_info raises (circuit_analytics/drivers/cat.py, the
Tail not revealed and parent is not a CAT branch).

Suggested fix

When building a user CAT coin spend, derive parent_inner_puzzle_hash from the parent
coin's actual puzzle reveal (uncurry the CAT to extract the inner puzzle hash), or record
it per-coin at indexing time. It must never be assumed to correspond to any particular
entry in synthetic_pks — the parent's inner puzzle can belong to any derivation index,
or to no wallet key at all (offer settlement, protocol change, etc.).

Impact

  • Reproducible 100% of the time for affected wallets; the UI gives no indication the
    problem is server-side.
  • Likely affects /vault/repay (BYC) and any other endpoint that spends user CATs.
  • The error message misleads users into blaming their wallet.

Happy to provide the probe script or run further ordered-key tests on request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions