Skip to content

Commit 6078263

Browse files
authored
Skip collateral return if it is too small (#411)
* Skip collateral return if it is too small * Minor fix
1 parent d7d2053 commit 6078263

File tree

2 files changed

+87
-1
lines changed

2 files changed

+87
-1
lines changed

pycardano/txbuilder.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ class TransactionBuilder:
144144

145145
_collateral_return: Optional[TransactionOutput] = field(init=False, default=None)
146146

147+
collateral_return_threshold: int = 1_000_000
148+
"""The minimum amount of lovelace above which
149+
the remaining collateral (total_collateral_amount - actually_used_amount) will be returned."""
150+
147151
_total_collateral: Optional[int] = field(init=False, default=None)
148152

149153
_inputs_to_redeemers: Dict[UTxO, Redeemer] = field(
@@ -1336,6 +1340,20 @@ def build(
13361340

13371341
return tx_body
13381342

1343+
def _should_add_collateral_return(self, collateral_return: Value) -> bool:
1344+
"""Check if it is necessary to add a collateral return output.
1345+
1346+
Args:
1347+
collateral_return (Value): The potential collateral return amount.
1348+
1349+
Returns:
1350+
bool: True if a collateral return output should be added, False otherwise.
1351+
"""
1352+
return (
1353+
collateral_return.coin > max(self.collateral_return_threshold, 1_000_000)
1354+
or collateral_return.multi_asset.count(lambda p, n, v: v > 0) > 0
1355+
)
1356+
13391357
def _set_collateral_return(self, collateral_return_address: Optional[Address]):
13401358
"""Calculate and set the change returned from the collateral inputs.
13411359
@@ -1370,7 +1388,8 @@ def _add_collateral_input(cur_total, candidate_inputs):
13701388

13711389
while (
13721390
cur_total.coin < collateral_amount
1373-
or 0
1391+
or self._should_add_collateral_return(cur_collateral_return)
1392+
and 0
13741393
<= cur_collateral_return.coin
13751394
< min_lovelace_post_alonzo(
13761395
TransactionOutput(
@@ -1422,6 +1441,10 @@ def _add_collateral_input(cur_total, candidate_inputs):
14221441
)
14231442
else:
14241443
return_amount = total_input - collateral_amount
1444+
1445+
if not self._should_add_collateral_return(return_amount):
1446+
return # No need to return collateral if the remaining amount is too small
1447+
14251448
min_lovelace_val = min_lovelace_post_alonzo(
14261449
TransactionOutput(collateral_return_address, return_amount),
14271450
self.context,

test/pycardano/test_txbuilder.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,69 @@ def test_collateral_return(chain_context):
10481048
)
10491049

10501050

1051+
@pytest.mark.parametrize(
1052+
"collateral_amount, collateral_return_threshold, has_return",
1053+
[
1054+
(Value(4_000_000), 0, False),
1055+
(Value(4_000_000), 1_000_000, False),
1056+
(Value(6_000_000), 2_000_000, True),
1057+
(Value(6_000_000), 3_000_000, False),
1058+
(
1059+
Value(
1060+
6_000_000,
1061+
MultiAsset.from_primitive({b"1" * 28: {b"Token1": 1, b"Token2": 2}}),
1062+
),
1063+
3_000_000,
1064+
True,
1065+
),
1066+
],
1067+
)
1068+
def test_no_collateral_return(
1069+
chain_context, collateral_amount, collateral_return_threshold, has_return
1070+
):
1071+
original_utxos = chain_context.utxos(
1072+
"addr_test1vrm9x2zsux7va6w892g38tvchnzahvcd9tykqf3ygnmwtaqyfg52x"
1073+
)
1074+
with patch.object(chain_context, "utxos") as mock_utxos:
1075+
tx_builder = TransactionBuilder(
1076+
chain_context, collateral_return_threshold=collateral_return_threshold
1077+
)
1078+
tx_in1 = TransactionInput.from_primitive(
1079+
["18cbe6cadecd3f89b60e08e68e5e6c7d72d730aaa1ad21431590f7e6643438ef", 0]
1080+
)
1081+
plutus_script = PlutusV1Script(b"dummy test script")
1082+
script_hash = plutus_script_hash(plutus_script)
1083+
script_address = Address(script_hash)
1084+
datum = PlutusData()
1085+
utxo1 = UTxO(
1086+
tx_in1, TransactionOutput(script_address, 10000000, datum_hash=datum.hash())
1087+
)
1088+
1089+
existing_script_utxo = UTxO(
1090+
TransactionInput.from_primitive(
1091+
[
1092+
"41cb004bec7051621b19b46aea28f0657a586a05ce2013152ea9b9f1a5614cc7",
1093+
1,
1094+
]
1095+
),
1096+
TransactionOutput(script_address, 1234567, script=plutus_script),
1097+
)
1098+
1099+
original_utxos[0].output.amount = collateral_amount
1100+
1101+
mock_utxos.return_value = original_utxos[:1] + [existing_script_utxo]
1102+
1103+
redeemer = Redeemer(PlutusData(), ExecutionUnits(1000000, 1000000))
1104+
tx_builder.add_script_input(utxo1, datum=datum, redeemer=redeemer)
1105+
receiver = Address.from_primitive(
1106+
"addr_test1vrm9x2zsux7va6w892g38tvchnzahvcd9tykqf3ygnmwtaqyfg52x"
1107+
)
1108+
tx_builder.add_output(TransactionOutput(receiver, 5000000))
1109+
tx_body = tx_builder.build(change_address=receiver)
1110+
assert (tx_body.collateral_return is not None) == has_return
1111+
assert (tx_body.total_collateral is not None) == has_return
1112+
1113+
10511114
def test_collateral_return_min_return_amount(chain_context):
10521115
original_utxos = chain_context.utxos(
10531116
"addr_test1vrm9x2zsux7va6w892g38tvchnzahvcd9tykqf3ygnmwtaqyfg52x"

0 commit comments

Comments
 (0)