11/* eslint-disable max-params */
22import { Cardano , coalesceValueQuantities } from '@cardano-sdk/core' ;
33import { InputSelectionError , InputSelectionFailure } from '../InputSelectionError' ;
4- import { InputSelectionParameters , InputSelector , SelectionConstraints , SelectionResult } from '../types' ;
5- import {
6- addTokenMaps ,
7- getCoinQuantity ,
8- hasNegativeAssetValue ,
9- sortByCoins ,
10- stubMaxSizeAddress ,
11- subtractTokenMaps ,
12- toValues
13- } from '../util' ;
14- import { sortUtxoByTxIn , splitChange } from './util' ;
4+ import { InputSelectionParameters , InputSelector , SelectionResult } from '../types' ;
5+ import { addTokenMaps , getCoinQuantity , hasNegativeAssetValue , subtractTokenMaps , toValues } from '../util' ;
6+ import { sortUtxoByTxIn , splitChangeAndComputeFee } from './util' ;
157
168/** Greedy selection initialization properties. */
179export interface GreedySelectorProps {
@@ -30,146 +22,6 @@ export interface GreedySelectorProps {
3022 getChangeAddresses : ( ) => Promise < Map < Cardano . PaymentAddress , number > > ;
3123}
3224
33- /**
34- * Given a set of input and outputs, compute the fee. Then extract the fee from the change output
35- * with the highest value.
36- *
37- * @param changeLovelace The available amount of lovelace to be used as change.
38- * @param constraints The selection constraints.
39- * @param inputs The inputs of the transaction.
40- * @param outputs The outputs of the transaction.
41- * @param changeOutputs The list of change outputs.
42- * @param currentFee The current computed fee for this selection.
43- */
44- const adjustOutputsForFee = async (
45- changeLovelace : bigint ,
46- constraints : SelectionConstraints ,
47- inputs : Set < Cardano . Utxo > ,
48- outputs : Set < Cardano . TxOut > ,
49- changeOutputs : Array < Cardano . TxOut > ,
50- currentFee : bigint
51- ) : Promise < {
52- fee : bigint ;
53- change : Array < Cardano . TxOut > ;
54- feeAccountedFor : boolean ;
55- redeemers ?: Array < Cardano . Redeemer > ;
56- } > => {
57- const totalOutputs = new Set ( [ ...outputs , ...changeOutputs ] ) ;
58- const { fee, redeemers } = await constraints . computeMinimumCost ( {
59- change : [ ] ,
60- fee : currentFee ,
61- inputs,
62- outputs : totalOutputs
63- } ) ;
64-
65- if ( fee === changeLovelace ) return { change : [ ] , fee, feeAccountedFor : true , redeemers } ;
66-
67- if ( changeLovelace < fee ) throw new InputSelectionError ( InputSelectionFailure . UtxoBalanceInsufficient ) ;
68-
69- const updatedOutputs = [ ...changeOutputs ] ;
70-
71- updatedOutputs . sort ( sortByCoins ) ;
72-
73- let feeAccountedFor = false ;
74- for ( const output of updatedOutputs ) {
75- const adjustedCoins = output . value . coins - fee ;
76-
77- if ( adjustedCoins >= constraints . computeMinimumCoinQuantity ( output ) ) {
78- output . value . coins = adjustedCoins ;
79- feeAccountedFor = true ;
80- break ;
81- }
82- }
83-
84- return { change : [ ...updatedOutputs ] , fee, feeAccountedFor, redeemers } ;
85- } ;
86-
87- /**
88- * Recursively compute the fee and compute change outputs until it finds a set of change outputs that satisfies the fee.
89- *
90- * @param inputs The inputs of the transaction.
91- * @param outputs The outputs of the transaction.
92- * @param changeLovelace The total amount of lovelace in the change.
93- * @param changeAssets The total assets to be distributed as change.
94- * @param constraints The selection constraints.
95- * @param getChangeAddresses A callback that returns a list of addresses and their proportions.
96- * @param fee The current computed fee for this selection.
97- */
98- const splitChangeAndComputeFee = async (
99- inputs : Set < Cardano . Utxo > ,
100- outputs : Set < Cardano . TxOut > ,
101- changeLovelace : bigint ,
102- changeAssets : Cardano . TokenMap | undefined ,
103- constraints : SelectionConstraints ,
104- getChangeAddresses : ( ) => Promise < Map < Cardano . PaymentAddress , number > > ,
105- fee : bigint
106- ) : Promise < { fee : bigint ; change : Array < Cardano . TxOut > ; feeAccountedFor : boolean } > => {
107- const changeOutputs = await splitChange (
108- getChangeAddresses ,
109- changeLovelace ,
110- changeAssets ,
111- constraints . computeMinimumCoinQuantity ,
112- constraints . tokenBundleSizeExceedsLimit ,
113- fee
114- ) ;
115-
116- let adjustedChangeOutputs = await adjustOutputsForFee (
117- changeLovelace ,
118- constraints ,
119- inputs ,
120- outputs ,
121- changeOutputs ,
122- fee
123- ) ;
124-
125- // If the newly computed fee is higher than tha available balance for change,
126- // but there are unallocated native assets, return the assets as change with 0n coins.
127- if ( adjustedChangeOutputs . fee >= changeLovelace ) {
128- const result = {
129- change : [
130- {
131- address : stubMaxSizeAddress ,
132- value : {
133- assets : changeAssets ,
134- coins : 0n
135- }
136- }
137- ] ,
138- fee : adjustedChangeOutputs . fee ,
139- feeAccountedFor : true
140- } ;
141-
142- if ( result . change [ 0 ] . value . coins < constraints . computeMinimumCoinQuantity ( result . change [ 0 ] ) )
143- throw new InputSelectionError ( InputSelectionFailure . UtxoFullyDepleted ) ;
144-
145- return result ;
146- }
147-
148- if ( fee < adjustedChangeOutputs . fee ) {
149- adjustedChangeOutputs = await splitChangeAndComputeFee (
150- inputs ,
151- outputs ,
152- changeLovelace ,
153- changeAssets ,
154- constraints ,
155- getChangeAddresses ,
156- adjustedChangeOutputs . fee
157- ) ;
158-
159- if ( adjustedChangeOutputs . change . length === 0 )
160- throw new InputSelectionError ( InputSelectionFailure . UtxoFullyDepleted ) ;
161- }
162-
163- for ( const out of adjustedChangeOutputs . change ) {
164- if ( out . value . coins < constraints . computeMinimumCoinQuantity ( out ) )
165- throw new InputSelectionError ( InputSelectionFailure . UtxoFullyDepleted ) ;
166- }
167-
168- if ( ! adjustedChangeOutputs . feeAccountedFor ) throw new InputSelectionError ( InputSelectionFailure . UtxoFullyDepleted ) ;
169-
170- return adjustedChangeOutputs ;
171- } ;
172-
17325/** Selects all UTXOs to fulfill the amount required for the given outputs and return the remaining balance as change. */
17426export class GreedyInputSelector implements InputSelector {
17527 #props: GreedySelectorProps ;
0 commit comments