feat: add recoverable cross-mint swaps - #383
Conversation
🦋 Changeset detectedLatest commit: aa1b42d The changes in this PR will be included in the next version bump. This PR includes changesets to release 8 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
@igbopharaoh - hopefully this will help you out: /**
* Fee to add on top of `amount` so the resulting outputs can later be spent at no cost to the
* receiver (ie sender pays fees). This is the amount `includeFees` adds.
*
* @remarks
* The fee depends on the output count, taken from the default denomination split of `amount`.
* Pass `nOutputs` when pricing up custom denomination sets.
* @param amount The amount the receiver should net after swap fees.
* @param opts.keysetId Optional `keysetId` to price against (default: the wallet's bound keyset)
* @param opts.nOutputs Optional Override the output count for custom denoms (default: optimal
* split).
* @returns The fee, zero when the keyset charges no input fees.
*/
getFeesToInclude(amount: AmountLike, opts?: { keysetId?: string; nOutputs?: number }): Amount Will be in CTS v4.8.0, will release end of the first week of August. |
|
Thanks @robwoodgate |
|
@codex kindly review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 82e4600e3d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| const TERMINAL_STATES = new Set<MintSwapOperationState>(['completed', 'cancelled', 'failed']); | ||
| const AUTOMATIC_STATES = new Set<MintSwapOperationState>([ | ||
| 'preparing', |
There was a problem hiding this comment.
Exclude live preparations from recovery sweeps
With the default processor enabled, every newly created parent enters preparing with no nextAttemptAt, so MintSwapOperationProcessor considers it due on the next five-second sweep. Since prepare() does not hold the operation lock while performing its multiple remote quote calls, a preparation lasting longer than one sweep can concurrently run recoverPreparing(), which marks the parent failed; the in-progress preparation then cannot transition it to prepared. Distinguish stale preparations from active ones or protect preparation with a lock/lease before sweeping this state.
Useful? React with 👍 / 👎.
| .slice(offset, offset + limit); | ||
| } | ||
| const [children, parents] = await Promise.all([ | ||
| this.historyRepository.getPaginatedHistoryEntries(10_000, 0), |
There was a problem hiding this comment.
Preserve pagination beyond the first 10,000 history rows
Whenever a mint-swap repository is configured—which Manager now always does—history pagination first truncates ordinary history to 10,000 rows and only then applies the requested offset and limit. For wallets with more than 10,000 entries, older records are therefore unreachable (for example, an offset of 10,000 returns no ordinary entries), and merged pages near that boundary are incomplete. The merge needs repository-level pagination or an unbounded/iterative fetch rather than this fixed cap.
Useful? React with 👍 / 👎.
| @@ -0,0 +1,13 @@ | |||
| --- | |||
| '@cashu/coco-core': patch | |||
There was a problem hiding this comment.
Publish the required repository additions as a breaking release
The core package is marked as a patch even though this change adds two mandatory members to the public Repositories contract. An existing third-party adapter installed against the current 1.0 API will therefore be accepted by a patch upgrade but fail during initializeCoco() when startup recovery calls the missing mintSwapOperationRepository; TypeScript adapters also must change before recompiling. Either preserve compatibility with optional/fallback repositories or release @cashu/coco-core with the appropriate breaking version.
Useful? React with 👍 / 👎.
| if (entry.type === 'mint-swap') { | ||
| return entry.sourceMintUrl === filter.mintUrl || entry.destinationMintUrl === filter.mintUrl; |
There was a problem hiding this comment.
Normalize the mint URL before filtering history
The new mintUrl filter compares the caller's string directly with normalized persisted URLs. Inputs that identify the same mint but need normalization, such as https://mint.example/ when storage contains https://mint.example, therefore return no ordinary or mint-swap history even though the rest of the mint-swap API accepts and normalizes those URLs. Normalize filter.mintUrl once before applying these comparisons.
Useful? React with 👍 / 👎.
d5f3bd5 to
aa1b42d
Compare
|
Hey man! This is great and I think the direction is solid. However this PR suffers from a similar issue I ran into when working with Wayfinder. This adds close to 8k lines in over 100 files and spans so many independent concepts that its becoming close to impossible to savely review. Could you please look into chunking this into independently mergeable slices. If that is not possible, maybe we can find a way to turn this into stacked PRs, however independent slices would be much better |
|
Hello @Egge21M GM. Appreciate the feedback. |
Problem
Coco exposes mint and melt operations separately, leaving callers to manually coordinate moving value between mints. That flow spans two mints, Lightning settlement, variable source fees, destination issuance, and several ambiguous failure boundaries without one durable recovery authority.
Summary
Specification And Tracking
Closes #364
Closes #365
Closes #366
Closes #367
Closes #368
Closes #369
Closes #370
Closes #371
Closes #372
Resolves #163
Compatibility
This is additive for wallet callers, but it expands mandatory repository interfaces and persisted schemas. Custom adapters must implement the mint-swap operation and event-outbox repositories and preserve parent ownership on mint and melt children.
Verification
bun run --filter='@cashu/coco-core' test:unit(1,097 passed)bun run --filter='@cashu/coco-react' test(49 passed)bun run typecheckbun run buildbun run docs:buildgit diff --check origin/master...HEADChangeset
.changeset/mint-swaps.mdwith patch releases for all affected published packages