Skip to content

Commit 4ad6321

Browse files
Merge pull request #120 from mlabs-haskell/sam/preserve-budget-order
Preserve output order
2 parents 1ab405a + 7eb9075 commit 4ad6321

File tree

2 files changed

+44
-22
lines changed

2 files changed

+44
-22
lines changed

src/BotPlutusInterface/Balance.hs

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import Control.Monad.Trans.Either (EitherT, hoistEither, newEitherT, runEitherT)
2626
import Data.Coerce (coerce)
2727
import Data.Either.Combinators (rightToMaybe)
2828
import Data.Kind (Type)
29-
import Data.List (partition, (\\))
29+
import Data.List ((\\))
3030
import Data.Map (Map)
3131
import Data.Map qualified as Map
3232
import Data.Maybe (fromMaybe, mapMaybe)
@@ -311,17 +311,17 @@ addTxCollaterals utxos tx =
311311
handleNonAdaChange :: Address -> Map TxOutRef TxOut -> Tx -> Either Text Tx
312312
handleNonAdaChange changeAddr utxos tx =
313313
let nonAdaChange = getNonAdaChange utxos tx
314+
newOutput =
315+
TxOut
316+
{ txOutAddress = changeAddr
317+
, txOutValue = nonAdaChange
318+
, txOutDatumHash = Nothing
319+
}
314320
outputs =
315-
case partition ((==) changeAddr . Tx.txOutAddress) $ txOutputs tx of
316-
([], txOuts) ->
317-
TxOut
318-
{ txOutAddress = changeAddr
319-
, txOutValue = nonAdaChange
320-
, txOutDatumHash = Nothing
321-
} :
322-
txOuts
323-
(txOut@TxOut {txOutValue = v} : txOuts, txOuts') ->
324-
txOut {txOutValue = v <> nonAdaChange} : (txOuts <> txOuts')
321+
modifyFirst
322+
((==) changeAddr . Tx.txOutAddress)
323+
(Just . maybe newOutput (addValueToTxOut nonAdaChange))
324+
(txOutputs tx)
325325
in if isValueNat nonAdaChange
326326
then Right $ if Value.isZero nonAdaChange then tx else tx {txOutputs = outputs}
327327
else Left "Not enough inputs to balance tokens."
@@ -332,18 +332,40 @@ hasChangeUTxO changeAddr tx =
332332

333333
-- | Adds ada change to a transaction, assuming there is already an output going to ownPkh. Otherwise, this is identity
334334
addAdaChange :: Address -> Integer -> Tx -> Tx
335+
addAdaChange _ 0 tx = tx
335336
addAdaChange changeAddr change tx =
336337
tx
337338
{ txOutputs =
338-
case partition ((==) changeAddr . Tx.txOutAddress) $ txOutputs tx of
339-
(txOut@TxOut {txOutValue = v} : txOuts, txOuts') ->
340-
txOut {txOutValue = v <> Ada.lovelaceValueOf change} : (txOuts <> txOuts')
341-
_ -> txOutputs tx
339+
modifyFirst
340+
((==) changeAddr . Tx.txOutAddress)
341+
(fmap $ addValueToTxOut $ Ada.lovelaceValueOf change)
342+
(txOutputs tx)
342343
}
343344

345+
consJust :: forall (a :: Type). Maybe a -> [a] -> [a]
346+
consJust (Just x) = (x :)
347+
consJust _ = id
348+
349+
{- | Modifies the first element matching a predicate, or, if none found, call the modifier with Nothing
350+
Calling this function ensures the modifier will always be run once
351+
-}
352+
modifyFirst ::
353+
forall (a :: Type).
354+
-- | Predicate for value to update
355+
(a -> Bool) ->
356+
-- | Modifier, input Maybe representing existing value (or Nothing if missing), output value representing new value (or Nothing to remove)
357+
(Maybe a -> Maybe a) ->
358+
[a] ->
359+
[a]
360+
modifyFirst _ m [] = m Nothing `consJust` []
361+
modifyFirst p m (x : xs) = if p x then m (Just x) `consJust` xs else x : modifyFirst p m xs
362+
363+
addValueToTxOut :: Value -> TxOut -> TxOut
364+
addValueToTxOut val txOut = txOut {txOutValue = txOutValue txOut <> val}
365+
344366
-- | Adds a 1 lovelace output to a transaction
345367
addOutput :: Address -> Tx -> Tx
346-
addOutput changeAddr tx = tx {txOutputs = changeTxOut : txOutputs tx}
368+
addOutput changeAddr tx = tx {txOutputs = txOutputs tx ++ [changeTxOut]}
347369
where
348370
changeTxOut =
349371
TxOut

test/Spec/BotPlutusInterface/Contract.hs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,8 @@ sendAda = do
156156
, [text|
157157
cardano-cli transaction build-raw --alonzo-era
158158
--tx-in ${inTxId}#0
159-
--tx-out ${addr1}+50
160159
--tx-out ${addr2}+1000
160+
--tx-out ${addr1}+50
161161
--required-signer ./signing-keys/signing-key-${pkh1'}.skey
162162
--fee 300
163163
--protocol-params-file ./protocol.json --out-file ./txs/tx-?.raw
@@ -411,8 +411,8 @@ sendTokens = do
411411
, [text|
412412
cardano-cli transaction build-raw --alonzo-era
413413
--tx-in ${inTxId1}#0
414-
--tx-out ${addr1}+50 + 95 abcd1234.74657374546F6B656E
415414
--tx-out ${addr2}+1000 + 5 abcd1234.74657374546F6B656E
415+
--tx-out ${addr1}+50 + 95 abcd1234.74657374546F6B656E
416416
--required-signer ./signing-keys/signing-key-${pkh1'}.skey
417417
--fee 300
418418
--protocol-params-file ./protocol.json --out-file ./txs/tx-?.raw
@@ -453,8 +453,8 @@ sendTokensWithoutName = do
453453
, [text|
454454
cardano-cli transaction build-raw --alonzo-era
455455
--tx-in ${inTxId1}#0
456-
--tx-out ${addr1}+50 + 95 abcd1234
457456
--tx-out ${addr2}+1000 + 5 abcd1234
457+
--tx-out ${addr1}+50 + 95 abcd1234
458458
--required-signer ./signing-keys/signing-key-${pkh1'}.skey
459459
--fee 300
460460
--protocol-params-file ./protocol.json --out-file ./txs/tx-?.raw
@@ -520,8 +520,8 @@ mintTokens = do
520520
cardano-cli transaction build-raw --alonzo-era
521521
--tx-in ${inTxId}#0
522522
--tx-in-collateral ${inTxId}#0
523-
--tx-out ${addr1}+496700
524523
--tx-out ${addr2}+1000 + 5 ${curSymbol'}.74657374546F6B656E
524+
--tx-out ${addr1}+496700
525525
--mint-script-file ./result-scripts/policy-${curSymbol'}.plutus
526526
--mint-redeemer-file ./result-scripts/redeemer-${redeemerHash}.json
527527
--mint-execution-units (500000,2000)
@@ -609,9 +609,9 @@ spendToValidator = do
609609
, [text|
610610
cardano-cli transaction build-raw --alonzo-era
611611
--tx-in ${inTxId}#0
612-
--tx-out ${addr1}+200
613612
--tx-out ${valAddr'}+500
614613
--tx-out-datum-embed-file ./result-scripts/datum-${datumHash'}.json
614+
--tx-out ${addr1}+200
615615
--required-signer ./signing-keys/signing-key-${pkh1'}.skey
616616
--fee 300
617617
--protocol-params-file ./protocol.json --out-file ./txs/tx-?.raw
@@ -707,8 +707,8 @@ redeemFromValidator = do
707707
--tx-in-redeemer-file ./result-scripts/redeemer-${redeemerHash}.json
708708
--tx-in-execution-units (500000,2000)
709709
--tx-in-collateral ${inTxId}#0
710-
--tx-out ${addr1}+498350
711710
--tx-out ${addr2}+500
711+
--tx-out ${addr1}+498350
712712
--required-signer ./signing-keys/signing-key-${pkh1'}.skey
713713
--fee 502400
714714
--protocol-params-file ./protocol.json --out-file ./txs/tx-?.raw

0 commit comments

Comments
 (0)