diff --git a/pycardano/txbuilder.py b/pycardano/txbuilder.py index f1f2ff50..4ecf99ed 100644 --- a/pycardano/txbuilder.py +++ b/pycardano/txbuilder.py @@ -1381,7 +1381,19 @@ def _set_collateral_return(self, collateral_return_address: Optional[Address]): tmp_val = Value() def _add_collateral_input(cur_total, candidate_inputs): - while cur_total.coin < collateral_amount and candidate_inputs: + cur_collateral_return = cur_total - collateral_amount + + while ( + cur_total.coin < collateral_amount + or 0 + <= cur_collateral_return.coin + < min_lovelace_post_alonzo( + TransactionOutput( + collateral_return_address, cur_collateral_return + ), + self.context, + ) + ) and candidate_inputs: candidate = candidate_inputs.pop() if ( not candidate.output.address.address_type.name.startswith( @@ -1391,6 +1403,7 @@ def _add_collateral_input(cur_total, candidate_inputs): ): self.collaterals.append(candidate) cur_total += candidate.output.amount + cur_collateral_return = cur_total - collateral_amount sorted_inputs = sorted( self.inputs.copy(), diff --git a/test/pycardano/test_txbuilder.py b/test/pycardano/test_txbuilder.py index 1fc50f7f..93e3c554 100644 --- a/test/pycardano/test_txbuilder.py +++ b/test/pycardano/test_txbuilder.py @@ -8,7 +8,13 @@ import pytest -from pycardano import AssetName, RedeemerKey, RedeemerMap, RedeemerValue +from pycardano import ( + AssetName, + RedeemerKey, + RedeemerMap, + RedeemerValue, + min_lovelace_post_alonzo, +) from pycardano.address import Address from pycardano.certificate import ( PoolRegistration, @@ -1042,6 +1048,61 @@ def test_collateral_return(chain_context): ) +def test_collateral_return_min_return_amount(chain_context): + original_utxos = chain_context.utxos( + "addr_test1vrm9x2zsux7va6w892g38tvchnzahvcd9tykqf3ygnmwtaqyfg52x" + ) + with patch.object(chain_context, "utxos") as mock_utxos: + tx_builder = TransactionBuilder(chain_context) + tx_in1 = TransactionInput.from_primitive( + ["18cbe6cadecd3f89b60e08e68e5e6c7d72d730aaa1ad21431590f7e6643438ef", 0] + ) + plutus_script = PlutusV1Script(b"dummy test script") + script_hash = plutus_script_hash(plutus_script) + script_address = Address(script_hash) + datum = PlutusData() + utxo1 = UTxO( + tx_in1, TransactionOutput(script_address, 10000000, datum_hash=datum.hash()) + ) + + existing_script_utxo = UTxO( + TransactionInput.from_primitive( + [ + "41cb004bec7051621b19b46aea28f0657a586a05ce2013152ea9b9f1a5614cc7", + 1, + ] + ), + TransactionOutput(script_address, 1234567, script=plutus_script), + ) + + original_utxos[0].output.amount.multi_asset = MultiAsset.from_primitive( + { + b"1" + * 28: { + b"Token" + i.to_bytes(10, byteorder="big"): i for i in range(500) + } + } + ) + + original_utxos[0].output.amount.coin = min_lovelace_post_alonzo( + original_utxos[0].output, chain_context + ) + + mock_utxos.return_value = original_utxos + [existing_script_utxo] + + redeemer = Redeemer(PlutusData(), ExecutionUnits(1000000, 1000000)) + tx_builder.add_script_input(utxo1, datum=datum, redeemer=redeemer) + receiver = Address.from_primitive( + "addr_test1vrm9x2zsux7va6w892g38tvchnzahvcd9tykqf3ygnmwtaqyfg52x" + ) + tx_builder.add_output(TransactionOutput(receiver, 5000000)) + tx_body = tx_builder.build(change_address=receiver) + assert tx_body.collateral_return.address == receiver + assert tx_body.collateral_return.amount.coin >= min_lovelace_post_alonzo( + tx_body.collateral_return, chain_context + ) + + def test_wrong_redeemer_execution_units(chain_context): tx_builder = TransactionBuilder(chain_context) tx_in1 = TransactionInput.from_primitive(