Skip to content

Feature request: Add coin_ids support to cancel_offer, cancel_offers, send_xch, send_cat (and fee coin selection for split/combine) #780

Description

@Lowestofttim

Summary

Following on from #760 / PR #761 which added coin_ids to MakeOffer — we'd like to request the same capability be extended to the remaining transaction endpoints so that callers can control which coins are used for transaction fees.

Problem

We run an automated Chia market maker that creates, cancels, and manages dozens of offers per cycle. To pay transaction fees we pre-split a pool of ~50 small XCH coins (0.001 XCH each). The problem is that only make_offer lets us specify which coins to use — every other endpoint auto-selects fee coins internally via select_coins().

This causes two issues:

1. MEMPOOL_CONFLICT / BAD_AGGREGATE_SIGNATURE on concurrent operations

When we fire multiple operations in quick succession (e.g. cancel a batch of stale offers, then immediately create replacements), Sage's internal coin selector can pick the same fee coin for two different transactions before the first one has been submitted to the mempool. This produces MEMPOOL_CONFLICT or BAD_AGGREGATE_SIGNATURE errors from the node.

With make_offer we solved this by passing a dedicated fee coin in coin_ids for each concurrent create — each transaction gets its own coin, no collisions. But we can't do the same for cancels, sends, splits, or combines.

2. Large trading coins consumed for tiny fees

The select_coins() algorithm picks the smallest coin >= fee amount from the entire selectable XCH pool. If our 50 small fee coins are all pending/spent, Sage falls through to our carefully-sized trading coins (e.g. a 0.1 XCH coin used to pay a 0.000013 XCH fee). The trading coin gets consumed, a change output is created with a new coin ID, and the prepared coin layout is disrupted — offers referencing the original coin become invalid.

Requested changes

Add coin_ids: Option<Vec<String>> to the following request structs, wired through prepare_spends_for_selection() the same way MakeOffer already does:

Priority 1 — Cancel endpoints (most impactful for market makers)

Struct Current fields Requested addition
CancelOffer offer_id, fee, auto_submit coin_ids: Option<Vec<String>>
CancelOffers offer_ids, fee, auto_submit coin_ids: Option<Vec<String>>

Priority 2 — Send endpoints

Struct Current fields Requested addition
SendXch address, amount, fee, memos, clawback, auto_submit coin_ids: Option<Vec<String>>
SendCat asset_id, address, amount, fee, include_hint, memos, clawback, auto_submit coin_ids: Option<Vec<String>>

Priority 3 — Split / Combine (already have coin_ids for input coins)

These already accept coin_ids for specifying which coins to split/combine, but the fee is still auto-selected from the remaining pool. Two options:

Option A: Allow the existing coin_ids to also cover the fee (same unified-pool approach as MakeOffer) — if the pre-selected coins have enough XCH left over after the split/combine, use that for the fee.

Option B: Add a separate fee_coin_ids: Option<Vec<String>> field for explicit fee coin selection.

Option A is simpler and consistent with MakeOffer's approach.

Implementation reference

PR #761 already established the pattern:

  1. Parse coin_ids from the request
    1. Call prepare_spends_for_selection(ctx, &selected_coin_ids) to pre-load them
    1. select_spends() then only auto-selects additional coins if the pre-selected ones don't fully cover the required amounts
      The same pattern can be applied to all the endpoints above with minimal code changes.

Use case

Any automated tooling (market makers, portfolio rebalancers, batch offer managers) that needs to:

  • Fire multiple fee-paying operations without waiting for each to confirm
    • Prevent the wallet's auto-selector from consuming carefully-prepared trading coins for fee payments
      • Maintain deterministic coin usage across concurrent transactions
        Happy to submit a PR for this if the approach is agreed — just wanted to discuss the scope first.

Related: #760, PR #761

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions