@@ -138,6 +138,10 @@ class TransactionBuilder:
138
138
init = False , default_factory = lambda : []
139
139
)
140
140
141
+ _withdrawal_script_to_redeemers : List [
142
+ Tuple [ScriptType , Optional [Redeemer ]]
143
+ ] = field (init = False , default_factory = lambda : [])
144
+
141
145
_inputs_to_scripts : Dict [UTxO , ScriptType ] = field (
142
146
init = False , default_factory = lambda : {}
143
147
)
@@ -291,6 +295,40 @@ def add_minting_script(
291
295
self ._minting_script_to_redeemers .append ((script , redeemer ))
292
296
return self
293
297
298
+ def add_withdrawal_script (
299
+ self ,
300
+ script : Union [UTxO , NativeScript , PlutusV1Script , PlutusV2Script ],
301
+ redeemer : Optional [Redeemer ] = None ,
302
+ ) -> TransactionBuilder :
303
+ """Add a withdrawal script along with its redeemer to this transaction.
304
+
305
+ Args:
306
+ script (Union[UTxO, PlutusV1Script, PlutusV2Script]): A plutus script.
307
+ redeemer (Optional[Redeemer]): A plutus redeemer to unlock the UTxO.
308
+
309
+ Returns:
310
+ TransactionBuilder: Current transaction builder.
311
+ """
312
+ if redeemer :
313
+ if redeemer .tag is not None and redeemer .tag != RedeemerTag .REWARD :
314
+ raise InvalidArgumentException (
315
+ f"Expect the redeemer tag's type to be { RedeemerTag .REWARD } , "
316
+ f"but got { redeemer .tag } instead."
317
+ )
318
+ redeemer .tag = RedeemerTag .REWARD
319
+ self ._consolidate_redeemer (redeemer )
320
+
321
+ if isinstance (script , UTxO ):
322
+ assert script .output .script is not None
323
+ self ._withdrawal_script_to_redeemers .append (
324
+ (script .output .script , redeemer )
325
+ )
326
+ self .reference_inputs .add (script )
327
+ self ._reference_scripts .append (script .output .script )
328
+ else :
329
+ self ._withdrawal_script_to_redeemers .append ((script , redeemer ))
330
+ return self
331
+
294
332
def add_input_address (self , address : Union [Address , str ]) -> TransactionBuilder :
295
333
"""Add an address to transaction's input address.
296
334
Unlike :meth:`add_input`, which deterministically adds a UTxO to the transaction's inputs, `add_input_address`
@@ -376,6 +414,9 @@ def all_scripts(self) -> List[ScriptType]:
376
414
for s , _ in self ._minting_script_to_redeemers :
377
415
scripts [script_hash (s )] = s
378
416
417
+ for s , _ in self ._withdrawal_script_to_redeemers :
418
+ scripts [script_hash (s )] = s
419
+
379
420
return list (scripts .values ())
380
421
381
422
@property
@@ -397,9 +438,11 @@ def datums(self) -> Dict[DatumHash, Datum]:
397
438
398
439
@property
399
440
def redeemers (self ) -> List [Redeemer ]:
400
- return [r for r in self ._inputs_to_redeemers .values () if r is not None ] + [
401
- r for _ , r in self ._minting_script_to_redeemers if r is not None
402
- ]
441
+ return (
442
+ [r for r in self ._inputs_to_redeemers .values () if r is not None ]
443
+ + [r for _ , r in self ._minting_script_to_redeemers if r is not None ]
444
+ + [r for _ , r in self ._withdrawal_script_to_redeemers if r is not None ]
445
+ )
403
446
404
447
@property
405
448
def script_data_hash (self ) -> Optional [ScriptDataHash ]:
@@ -735,26 +778,31 @@ def _set_redeemer_index(self):
735
778
sorted_mint_policies = sorted (self .mint .keys (), key = lambda x : x .to_cbor ())
736
779
else :
737
780
sorted_mint_policies = []
781
+ if self .withdrawals :
782
+ sorted_withdrawals = sorted (self .withdrawals .keys ())
783
+ else :
784
+ sorted_withdrawals = []
738
785
739
786
for i , utxo in enumerate (self .inputs ):
740
787
if (
741
788
utxo in self ._inputs_to_redeemers
742
789
and self ._inputs_to_redeemers [utxo ].tag == RedeemerTag .SPEND
743
790
):
744
791
self ._inputs_to_redeemers [utxo ].index = i
745
- elif (
746
- utxo in self ._inputs_to_redeemers
747
- and self ._inputs_to_redeemers [utxo ].tag == RedeemerTag .MINT
748
- ):
749
- redeemer = self ._inputs_to_redeemers [utxo ]
750
- redeemer .index = sorted_mint_policies .index (
751
- script_hash (self ._inputs_to_scripts [utxo ])
752
- )
753
792
754
793
for script , redeemer in self ._minting_script_to_redeemers :
755
794
if redeemer is not None :
756
795
redeemer .index = sorted_mint_policies .index (script_hash (script ))
757
796
797
+ for script , redeemer in self ._withdrawal_script_to_redeemers :
798
+ if redeemer is not None :
799
+ script_staking_credential = Address (
800
+ staking_part = script_hash (script ), network = self .context .network
801
+ )
802
+ redeemer .index = sorted_withdrawals .index (
803
+ script_staking_credential .to_primitive ()
804
+ )
805
+
758
806
self .redeemers .sort (key = lambda r : r .index )
759
807
760
808
def _build_tx_body (self ) -> TransactionBody :
@@ -1203,7 +1251,10 @@ def _update_execution_units(
1203
1251
assert (
1204
1252
r .tag is not None
1205
1253
), "Expected tag of redeemer to be set, but found None"
1206
- key = f"{ r .tag .name .lower ()} :{ r .index } "
1254
+ tagname = (
1255
+ r .tag .name .lower () if r .tag != RedeemerTag .REWARD else "withdrawal"
1256
+ )
1257
+ key = f"{ tagname } :{ r .index } "
1207
1258
if (
1208
1259
key not in estimated_execution_units
1209
1260
or estimated_execution_units [key ] is None
0 commit comments