Skip to content

Commit ff84553

Browse files
committed
Fix min amount for collateral return
This commit fixes errors "Minimum lovelace amount for collateral return x is greater than collateral change y". The root cause is that the collateral return can contain multi-assets that might exceed the minimum ADA required to hold them.
1 parent 6c2d183 commit ff84553

File tree

2 files changed

+71
-2
lines changed

2 files changed

+71
-2
lines changed

pycardano/txbuilder.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -1381,7 +1381,19 @@ def _set_collateral_return(self, collateral_return_address: Optional[Address]):
13811381
tmp_val = Value()
13821382

13831383
def _add_collateral_input(cur_total, candidate_inputs):
1384-
while cur_total.coin < collateral_amount and candidate_inputs:
1384+
cur_collateral_return = cur_total - collateral_amount
1385+
1386+
while (
1387+
cur_total.coin < collateral_amount
1388+
or 0
1389+
<= cur_collateral_return.coin
1390+
< min_lovelace_post_alonzo(
1391+
TransactionOutput(
1392+
collateral_return_address, cur_collateral_return
1393+
),
1394+
self.context,
1395+
)
1396+
) and candidate_inputs:
13851397
candidate = candidate_inputs.pop()
13861398
if (
13871399
not candidate.output.address.address_type.name.startswith(
@@ -1391,6 +1403,7 @@ def _add_collateral_input(cur_total, candidate_inputs):
13911403
):
13921404
self.collaterals.append(candidate)
13931405
cur_total += candidate.output.amount
1406+
cur_collateral_return = cur_total - collateral_amount
13941407

13951408
sorted_inputs = sorted(
13961409
self.inputs.copy(),

test/pycardano/test_txbuilder.py

+57-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88

99
import pytest
1010

11-
from pycardano import AssetName, RedeemerKey, RedeemerMap, RedeemerValue
11+
from pycardano import (
12+
AssetName,
13+
RedeemerKey,
14+
RedeemerMap,
15+
RedeemerValue,
16+
min_lovelace_post_alonzo,
17+
)
1218
from pycardano.address import Address
1319
from pycardano.certificate import (
1420
PoolRegistration,
@@ -1042,6 +1048,56 @@ def test_collateral_return(chain_context):
10421048
)
10431049

10441050

1051+
def test_collateral_return_min_return_amount(chain_context):
1052+
original_utxos = chain_context.utxos(
1053+
"addr_test1vrm9x2zsux7va6w892g38tvchnzahvcd9tykqf3ygnmwtaqyfg52x"
1054+
)
1055+
with patch.object(chain_context, "utxos") as mock_utxos:
1056+
tx_builder = TransactionBuilder(chain_context)
1057+
tx_in1 = TransactionInput.from_primitive(
1058+
["18cbe6cadecd3f89b60e08e68e5e6c7d72d730aaa1ad21431590f7e6643438ef", 0]
1059+
)
1060+
plutus_script = PlutusV1Script(b"dummy test script")
1061+
script_hash = plutus_script_hash(plutus_script)
1062+
script_address = Address(script_hash)
1063+
datum = PlutusData()
1064+
utxo1 = UTxO(
1065+
tx_in1, TransactionOutput(script_address, 10000000, datum_hash=datum.hash())
1066+
)
1067+
1068+
existing_script_utxo = UTxO(
1069+
TransactionInput.from_primitive(
1070+
[
1071+
"41cb004bec7051621b19b46aea28f0657a586a05ce2013152ea9b9f1a5614cc7",
1072+
1,
1073+
]
1074+
),
1075+
TransactionOutput(script_address, 1234567, script=plutus_script),
1076+
)
1077+
1078+
original_utxos[0].output.amount.multi_asset = MultiAsset.from_primitive(
1079+
{b"1" * 28: {b"Token" + i.to_bytes(10): i for i in range(500)}}
1080+
)
1081+
1082+
original_utxos[0].output.amount.coin = min_lovelace_post_alonzo(
1083+
original_utxos[0].output, chain_context
1084+
)
1085+
1086+
mock_utxos.return_value = original_utxos + [existing_script_utxo]
1087+
1088+
redeemer = Redeemer(PlutusData(), ExecutionUnits(1000000, 1000000))
1089+
tx_builder.add_script_input(utxo1, datum=datum, redeemer=redeemer)
1090+
receiver = Address.from_primitive(
1091+
"addr_test1vrm9x2zsux7va6w892g38tvchnzahvcd9tykqf3ygnmwtaqyfg52x"
1092+
)
1093+
tx_builder.add_output(TransactionOutput(receiver, 5000000))
1094+
tx_body = tx_builder.build(change_address=receiver)
1095+
assert tx_body.collateral_return.address == receiver
1096+
assert tx_body.collateral_return.amount.coin >= min_lovelace_post_alonzo(
1097+
tx_body.collateral_return, chain_context
1098+
)
1099+
1100+
10451101
def test_wrong_redeemer_execution_units(chain_context):
10461102
tx_builder = TransactionBuilder(chain_context)
10471103
tx_in1 = TransactionInput.from_primitive(

0 commit comments

Comments
 (0)