1
1
/* eslint-disable max-params */
2
2
import { Cardano , coalesceValueQuantities } from '@cardano-sdk/core' ;
3
3
import { 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' ;
15
7
16
8
/** Greedy selection initialization properties. */
17
9
export interface GreedySelectorProps {
@@ -30,146 +22,6 @@ export interface GreedySelectorProps {
30
22
getChangeAddresses : ( ) => Promise < Map < Cardano . PaymentAddress , number > > ;
31
23
}
32
24
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
-
173
25
/** Selects all UTXOs to fulfill the amount required for the given outputs and return the remaining balance as change. */
174
26
export class GreedyInputSelector implements InputSelector {
175
27
#props: GreedySelectorProps ;
0 commit comments