@@ -25,8 +25,6 @@ def _fund_script(
2525 dst_addr : clusterlib .AddressRecord ,
2626 plutus_op : plutus_common .PlutusOp ,
2727 amount : int ,
28- fee_txsize : int = FEE_REDEEM_TXSIZE ,
29- deposit_amount : int = 0 ,
3028 tokens : list [clusterlib_utils .Token ] | None = None , # tokens must already be in `payment_addr`
3129 tokens_collateral : list [clusterlib_utils .Token ]
3230 | None = None , # tokens must already be in `payment_addr`
@@ -57,7 +55,7 @@ def _fund_script(
5755
5856 script_txout = plutus_common .txout_factory (
5957 address = script_address ,
60- amount = amount + redeem_cost . fee + fee_txsize + deposit_amount ,
58+ amount = amount ,
6159 plutus_op = plutus_op ,
6260 embed_datum = embed_datum ,
6361 )
@@ -126,12 +124,14 @@ def _fund_script(
126124def _spend_locked_txin ( # noqa: C901
127125 temp_template : str ,
128126 cluster_obj : clusterlib .ClusterLib ,
127+ payment_addr : clusterlib .AddressRecord ,
129128 dst_addr : clusterlib .AddressRecord ,
130129 script_utxos : list [clusterlib .UTXOData ],
131130 collateral_utxos : list [clusterlib .UTXOData ],
132131 plutus_op : plutus_common .PlutusOp ,
133132 amount : int ,
134- fee_txsize : int = FEE_REDEEM_TXSIZE ,
133+ fee_txsize : int | None = None ,
134+ deposit_amount : int = 0 ,
135135 txins : clusterlib .OptionalUTXOData = (),
136136 tx_files : clusterlib .TxFiles | None = None ,
137137 invalid_hereafter : int | None = None ,
@@ -144,22 +144,35 @@ def _spend_locked_txin( # noqa: C901
144144 """Spend the locked UTxO."""
145145 assert plutus_op .execution_cost
146146
147+ if fee_txsize is None :
148+ fee_txsize = FEE_REDEEM_TXSIZE
149+
147150 tx_files = tx_files or clusterlib .TxFiles ()
148151 spent_tokens = tokens or ()
152+ spent_tokens_dict = {r .coin : r for r in spent_tokens }
153+ available_tokens = [
154+ clusterlib_utils .Token (coin = r .coin , amount = r .amount )
155+ for r in script_utxos
156+ if r .coin != clusterlib .DEFAULT_COIN
157+ ]
149158
150- # Change will be returned to address of the first script
151- change_rec = script_utxos [0 ]
159+ script_amount = clusterlib .calculate_utxos_balance (utxos = script_utxos )
160+
161+ # Spend all locked funds
162+ if amount == - 1 :
163+ amount = script_amount
164+
165+ # Change that was calculated manually will be returned to address of the first script.
166+ # The remaining change that is automatically handled by the `build` command will be returned
167+ # to `payment_addr`, because it would be inaccessible on script address without proper
168+ # datum hash (datum hash is not provided for change that is handled by `build` command).
169+ script_change_rec = script_utxos [0 ]
152170
153171 redeem_cost = plutus_common .compute_cost (
154172 execution_cost = plutus_op .execution_cost ,
155173 protocol_params = cluster_obj .g_query .get_protocol_params (),
156174 )
157175
158- script_utxos_lovelace = [u for u in script_utxos if u .coin == clusterlib .DEFAULT_COIN ]
159- script_lovelace_balance = clusterlib .calculate_utxos_balance (
160- utxos = [* script_utxos_lovelace , * txins ]
161- )
162-
163176 # Spend the "locked" UTxO
164177
165178 plutus_txins = [
@@ -184,34 +197,75 @@ def _spend_locked_txin( # noqa: C901
184197 txouts = [
185198 clusterlib .TxOut (address = dst_addr .address , amount = amount ),
186199 ]
187- # Append change
188- if script_lovelace_balance > amount + redeem_cost .fee + fee_txsize :
189- txouts .append (
190- clusterlib .TxOut (
191- address = change_rec .address ,
192- amount = script_lovelace_balance - amount - redeem_cost .fee - fee_txsize ,
193- datum_hash = change_rec .datum_hash ,
194- )
195- )
196200
197- for token in spent_tokens :
198- txouts .append (
199- clusterlib .TxOut (address = dst_addr .address , amount = token .amount , coin = token .coin )
200- )
201- # Append change
201+ lovelace_change_needed = False
202+ for token in available_tokens :
203+ spent_amount = 0
202204 script_token_balance = clusterlib .calculate_utxos_balance (
203205 utxos = script_utxos , coin = token .coin
204206 )
205- if script_token_balance > token .amount :
207+ if stoken := spent_tokens_dict .get (token .coin ):
208+ spent_amount = stoken .amount
209+ txouts .append (
210+ clusterlib .TxOut (address = dst_addr .address , amount = spent_amount , coin = stoken .coin )
211+ )
212+ # Append change
213+ if script_token_balance > spent_amount :
214+ lovelace_change_needed = True
206215 txouts .append (
207216 clusterlib .TxOut (
208- address = change_rec .address ,
209- amount = script_token_balance - token . amount ,
217+ address = script_change_rec .address ,
218+ amount = script_token_balance - spent_amount ,
210219 coin = token .coin ,
211- datum_hash = change_rec .datum_hash ,
220+ datum_hash = script_change_rec .datum_hash ,
212221 )
213222 )
214223
224+ # Add minimum (+ some) required Lovelace to change Tx output
225+ lovelace_token_change = 0
226+ if lovelace_change_needed :
227+ lovelace_token_change = 4_000_000
228+ txouts .append (
229+ clusterlib .TxOut (
230+ address = script_change_rec .address ,
231+ amount = lovelace_token_change ,
232+ coin = clusterlib .DEFAULT_COIN ,
233+ datum_hash = script_change_rec .datum_hash ,
234+ )
235+ )
236+
237+ input_lovelace_balance = clusterlib .calculate_utxos_balance (utxos = [* txins ]) + script_amount
238+ funds_needed = amount + redeem_cost .fee + fee_txsize + deposit_amount + lovelace_token_change
239+
240+ if input_lovelace_balance < funds_needed :
241+ # Add additional funds to cover fee and Lovelace change for token txouts
242+ fee_txin = next (
243+ r
244+ for r in clusterlib_utils .get_just_lovelace_utxos (
245+ address_utxos = cluster_obj .g_query .get_utxo (address = payment_addr .address )
246+ )
247+ if r .amount >= 100_000_000
248+ )
249+ txins = [
250+ * txins ,
251+ fee_txin ,
252+ ]
253+ tx_files = dataclasses .replace (
254+ tx_files ,
255+ signing_key_files = list ({* tx_files .signing_key_files , payment_addr .skey_file }),
256+ )
257+ input_lovelace_balance = clusterlib .calculate_utxos_balance (utxos = [* txins ]) + script_amount
258+
259+ # Append change
260+ change_lovelace = input_lovelace_balance - funds_needed
261+ if change_lovelace > 0 :
262+ txouts .append (
263+ clusterlib .TxOut (
264+ address = payment_addr .address ,
265+ amount = change_lovelace ,
266+ )
267+ )
268+
215269 tx_raw_output = cluster_obj .g_transaction .build_raw_tx_bare (
216270 out_file = f"{ temp_template } _step2_tx.body" ,
217271 txins = txins ,
@@ -233,6 +287,7 @@ def _spend_locked_txin( # noqa: C901
233287 return "" , tx_raw_output
234288
235289 dst_init_balance = cluster_obj .g_query .get_address_balance (dst_addr .address )
290+ script_utxos_lovelace = [u for u in script_utxos if u .coin == clusterlib .DEFAULT_COIN ]
236291
237292 if not script_valid :
238293 cluster_obj .g_transaction .submit_tx_bare (tx_file = tx_signed )
0 commit comments