3
3
"""
4
4
5
5
import random
6
+ from copy import deepcopy
6
7
from typing import Iterable , List , Optional , Tuple
7
8
8
9
from pycardano .address import Address
@@ -36,6 +37,7 @@ def select(
36
37
max_input_count : Optional [int ] = None ,
37
38
include_max_fee : Optional [bool ] = True ,
38
39
respect_min_utxo : Optional [bool ] = True ,
40
+ existing_amount : Optional [Value ] = None ,
39
41
) -> Tuple [List [UTxO ], Value ]:
40
42
"""From an input list of UTxOs, select a subset of UTxOs whose sum (including ADA and multi-assets)
41
43
is equal to or larger than the sum of a set of outputs.
@@ -50,6 +52,7 @@ def select(
50
52
respect_min_utxo (bool): Respect minimum amount of ADA required to hold a multi-asset bundle in the change.
51
53
Defaults to True. If disabled, the selection will not add addition amount of ADA to change even
52
54
when the amount is too small to hold a multi-asset bundle.
55
+ existing_amount (Value): A starting amount already existed before selection. Defaults to 0.
53
56
54
57
Returns:
55
58
Tuple[List[UTxO], Value]: A tuple containing:
@@ -83,6 +86,7 @@ def select(
83
86
max_input_count : Optional [int ] = None ,
84
87
include_max_fee : Optional [bool ] = True ,
85
88
respect_min_utxo : Optional [bool ] = True ,
89
+ existing_amount : Optional [Value ] = None ,
86
90
) -> Tuple [List [UTxO ], Value ]:
87
91
available : List [UTxO ] = sorted (utxos , key = lambda utxo : utxo .output .lovelace )
88
92
max_fee = max_tx_fee (context ) if include_max_fee else 0
@@ -91,15 +95,14 @@ def select(
91
95
total_requested += o .amount
92
96
93
97
selected = []
94
- selected_amount = Value ()
98
+ selected_amount = existing_amount if existing_amount is not None else Value ()
95
99
96
100
while not total_requested <= selected_amount :
97
101
if not available :
98
102
raise InsufficientUTxOBalanceException ("UTxO Balance insufficient!" )
99
103
to_add = available .pop ()
100
104
selected .append (to_add )
101
105
selected_amount += to_add .output .amount
102
-
103
106
if max_input_count and len (selected ) > max_input_count :
104
107
raise MaxInputCountExceededException (
105
108
f"Max input count: { max_input_count } exceeded!"
@@ -108,9 +111,8 @@ def select(
108
111
if respect_min_utxo :
109
112
change = selected_amount - total_requested
110
113
min_change_amount = min_lovelace_post_alonzo (
111
- TransactionOutput (_FAKE_ADDR , change ), context
114
+ TransactionOutput (_FAKE_ADDR , deepcopy ( change ) ), context
112
115
)
113
-
114
116
if change .coin < min_change_amount :
115
117
additional , _ = self .select (
116
118
available ,
@@ -127,7 +129,6 @@ def select(
127
129
for u in additional :
128
130
selected .append (u )
129
131
selected_amount += u .output .amount
130
-
131
132
return selected , selected_amount - total_requested
132
133
133
134
@@ -218,10 +219,9 @@ def _find_diff_by_former(a: Value, b: Value) -> int:
218
219
else :
219
220
policy_id = list (a .multi_asset .keys ())[0 ]
220
221
asset_name = list (a .multi_asset [policy_id ].keys ())[0 ]
221
- return (
222
- a .multi_asset [policy_id ][asset_name ]
223
- - b .multi_asset [policy_id ][asset_name ]
224
- )
222
+ return a .multi_asset [policy_id ].get (asset_name , 0 ) - b .multi_asset [
223
+ policy_id
224
+ ].get (asset_name , 0 )
225
225
226
226
def _improve (
227
227
self ,
@@ -272,6 +272,7 @@ def select(
272
272
max_input_count : Optional [int ] = None ,
273
273
include_max_fee : Optional [bool ] = True ,
274
274
respect_min_utxo : Optional [bool ] = True ,
275
+ existing_amount : Optional [Value ] = None ,
275
276
) -> Tuple [List [UTxO ], Value ]:
276
277
# Shallow copy the list
277
278
remaining = list (utxos )
@@ -281,11 +282,13 @@ def select(
281
282
request_sum += o .amount
282
283
283
284
assets = self ._split_by_asset (request_sum )
285
+
284
286
request_sorted = sorted (assets , key = self ._get_single_asset_val , reverse = True )
285
287
286
288
# Phase 1 - random select
287
289
selected : List [UTxO ] = []
288
- selected_amount = Value ()
290
+ selected_amount = existing_amount if existing_amount is not None else Value ()
291
+
289
292
for r in request_sorted :
290
293
self ._random_select_subset (r , remaining , selected , selected_amount )
291
294
if max_input_count and len (selected ) > max_input_count :
@@ -315,7 +318,7 @@ def select(
315
318
if respect_min_utxo :
316
319
change = selected_amount - request_sum
317
320
min_change_amount = min_lovelace_post_alonzo (
318
- TransactionOutput (_FAKE_ADDR , change ), context
321
+ TransactionOutput (_FAKE_ADDR , deepcopy ( change ) ), context
319
322
)
320
323
321
324
if change .coin < min_change_amount :
0 commit comments