sdk/builders/phases/ChangeCreation.ts
ChangeCreation overview
Change Creation Phase - Change Output Generation
Creates change outputs from leftover assets using a cascading retry strategy. Handles both unfrack (N outputs) and single output approaches with fallback options.
Added in v2.0.0
Table of contents
utils
executeChangeCreation
Change Creation Phase
Creates change outputs from leftover assets using a cascading retry strategy. Both unfrack (N outputs) and single output follow the same retry pattern: try with available funds → if insufficient, reselect (up to MAX_ATTEMPTS) → fallback.
Symmetric Retry Flow (Unfrack vs Single Output):
UNFRACK (N outputs) SINGLE OUTPUT (1 output)
─────────────────────────────────────────────────────────────────
Try: Create N outputs Try: Create 1 output
↓ ↓
Check: leftover >= (minUTxO × N)? Check: leftover >= minUTxO?
↓ ↓
If NO (not affordable): If NO (insufficient):
├─ attempt < MAX? → Reselect ├─ attempt < MAX? → Reselect
└─ attempt >= MAX? → Fallback └─ attempt >= MAX? → Fallback
↓
Fallback: Fallback:
└─ Single output ├─ drainTo (merge into output)
├─ (retry/fallback) ├─ burn (leftover → fee)
└─ ... └─ errorDetailed Flow:
1. Calculate tentative leftover (inputs - outputs - contextFee)
2. If unfrack enabled and canUnfrack=true:
→ Try createUnfrackedChangeOutputs() (N outputs)
→ Success: store N outputs, goto FeeCalculation
→ Not affordable:
├─ If attempt < MAX_ATTEMPTS: reselect (add more UTxOs)
└─ If attempt >= MAX_ATTEMPTS: canUnfrack=false, goto step 3
3. Single output approach:
→ Create 1 change output with leftover
→ Success: store 1 output, goto FeeCalculation
→ Not affordable:
├─ If attempt < MAX_ATTEMPTS: reselect (add more UTxOs)
└─ If attempt >= MAX_ATTEMPTS: goto step 4
4. Insufficient change fallbacks (single-output only):
a. If drainTo specified: merge into existing output
b. If onInsufficientChange="burn": leftover becomes fee
c. If onInsufficientChange="error": throw errorKey Principles:
- Unfrack and single output use SAME retry mechanism (reselection up to MAX_ATTEMPTS)
- Phase loop handles fee convergence (leftover recalculated each iteration)
- Last subdivision output absorbs remainder for exact balance
- canUnfrack flag prevents retry loops (once false, stays false)
- drainTo and burn are terminal fallbacks (single-output only)
- Unfrack outputs bypass drainTo/burn (they're already valid)
Signature
export declare const executeChangeCreation: () => Effect.Effect<
PhaseResult,
TransactionBuilderError,
PhaseContextTag | TxContext | ChangeAddressTag | BuildOptionsTag | ProtocolParametersTag | AvailableUtxosTag
>