@@ -86,17 +86,39 @@ balanceTxIO pabConf ownPkh unbalancedTx =
8686 preBalancedTx <- hoistEither $ addTxCollaterals utxoIndex tx >>= addSignatories ownPkh privKeys requiredSigs
8787
8888 -- Balance the tx
89- balancedTx <- loop utxoIndex privKeys [] preBalancedTx
89+ (balancedTx, minUtxos) <- loop utxoIndex privKeys [] preBalancedTx
90+
91+ -- Check if we have Ada change
92+ let adaChange = getAdaChange utxoIndex (lovelaceValue $ txFee balancedTx) balancedTx
93+ -- If we have no change UTxO, but we do have change, we need to add an output for it
94+ -- We'll add a minimal output, run the loop again so it gets minUTxO, then update change
95+ balancedTxWithChange <-
96+ if adaChange /= 0 && not (hasChangeUTxO ownPkh balancedTx)
97+ then
98+ let changeAddr = Ledger. pubKeyHashAddress (Ledger. PaymentPubKeyHash ownPkh) Nothing
99+ changeTxOut =
100+ TxOut
101+ { txOutAddress = changeAddr
102+ , txOutValue = Ada. lovelaceValueOf 1
103+ , txOutDatumHash = Nothing
104+ }
105+ preBalancedTxWithChange = balancedTx {txOutputs = changeTxOut : txOutputs balancedTx}
106+ in fst <$> loop utxoIndex privKeys minUtxos preBalancedTxWithChange
107+ else pure balancedTx
108+
109+ -- Get the updated change, add it to the tx
110+ let finalAdaChange = getAdaChange utxoIndex (lovelaceValue $ txFee balancedTxWithChange) balancedTxWithChange
111+ fullyBalancedTx = addAdaChange ownPkh finalAdaChange balancedTxWithChange
90112
91113 -- finally, we must update the signatories
92- hoistEither $ addSignatories ownPkh privKeys requiredSigs balancedTx
114+ hoistEither $ addSignatories ownPkh privKeys requiredSigs fullyBalancedTx
93115 where
94116 loop ::
95117 Map TxOutRef TxOut ->
96118 Map PubKeyHash DummyPrivKey ->
97119 [(TxOut , Integer )] ->
98120 Tx ->
99- EitherT Text (Eff effs ) Tx
121+ EitherT Text (Eff effs ) ( Tx , [( TxOut , Integer )])
100122 loop utxoIndex privKeys prevMinUtxos tx = do
101123 void $ lift $ Files. writeAll @ w pabConf tx
102124 nextMinUtxos <-
@@ -123,7 +145,7 @@ balanceTxIO pabConf ownPkh unbalancedTx =
123145 let balanceTxWithFees = balancedTx {txFee = Ada. lovelaceValueOf fees}
124146
125147 if balanceTxWithFees == tx
126- then pure balanceTxWithFees
148+ then pure ( balanceTxWithFees, minUtxos)
127149 else loop utxoIndex privKeys minUtxos balanceTxWithFees
128150
129151calculateMinUtxos ::
@@ -146,7 +168,26 @@ balanceTxStep ::
146168balanceTxStep minUtxos fees utxos ownPkh tx =
147169 Right (addLovelaces minUtxos tx)
148170 >>= balanceTxIns utxos fees
149- >>= handleChange ownPkh utxos fees
171+ >>= handleNonAdaChange ownPkh utxos fees
172+
173+ -- | Get change value of a transaction, taking inputs, outputs, mint and fees into account
174+ getChange :: Map TxOutRef TxOut -> Integer -> Tx -> Value
175+ getChange utxos fees tx =
176+ let txInRefs = map Tx. txInRef $ Set. toList $ txInputs tx
177+ inputValue = mconcat $ map Tx. txOutValue $ mapMaybe (`Map.lookup` utxos) txInRefs
178+ outputValue = mconcat $ map Tx. txOutValue $ txOutputs tx
179+ nonMintedOutputValue = outputValue `minus` txMint tx
180+ change = (inputValue `minus` nonMintedOutputValue) `minus` Ada. lovelaceValueOf fees
181+ in change
182+
183+ lovelaceValue :: Value -> Integer
184+ lovelaceValue = flip Value. assetClassValueOf $ Value. assetClass " " " "
185+
186+ getAdaChange :: Map TxOutRef TxOut -> Integer -> Tx -> Integer
187+ getAdaChange utxos fees = lovelaceValue . getChange utxos fees
188+
189+ getNonAdaChange :: Map TxOutRef TxOut -> Integer -> Tx -> Value
190+ getNonAdaChange utxos fees = Ledger. noAdaValue . getChange utxos fees
150191
151192-- | Getting the necessary utxos to cover the fees for the transaction
152193collectTxIns :: Set TxIn -> Map TxOutRef TxOut -> Value -> Either Text (Set TxIn )
@@ -232,30 +273,45 @@ addTxCollaterals utxos tx = do
232273 _ -> Left " There are no utxos to be used as collateral"
233274 filterAdaOnly = Map. filter (isAdaOnly . txOutValue)
234275
235- -- | Ensures all change goes back to user
236- handleChange :: PubKeyHash -> Map TxOutRef TxOut -> Integer -> Tx -> Either Text Tx
237- handleChange ownPkh utxos fees tx =
276+ -- | Ensures all non ada change goes back to user
277+ handleNonAdaChange :: PubKeyHash -> Map TxOutRef TxOut -> Integer -> Tx -> Either Text Tx
278+ handleNonAdaChange ownPkh utxos fees tx =
238279 let changeAddr = Ledger. pubKeyHashAddress (Ledger. PaymentPubKeyHash ownPkh) Nothing
239- txInRefs = map Tx. txInRef $ Set. toList $ txInputs tx
240- inputValue = mconcat $ map Tx. txOutValue $ mapMaybe (`Map.lookup` utxos) txInRefs
241- outputValue = mconcat $ map Tx. txOutValue $ txOutputs tx
242- nonMintedOutputValue = outputValue `minus` txMint tx
243- change = (inputValue `minus` nonMintedOutputValue) `minus` Ada. lovelaceValueOf fees
280+ nonAdaChange = getNonAdaChange utxos fees tx
244281 outputs =
245282 case partition ((==) changeAddr . Tx. txOutAddress) $ txOutputs tx of
246283 ([] , txOuts) ->
247284 TxOut
248285 { txOutAddress = changeAddr
249- , txOutValue = change
286+ , txOutValue = nonAdaChange
250287 , txOutDatumHash = Nothing
251288 } :
252289 txOuts
253290 (txOut@ TxOut {txOutValue = v} : txOuts, txOuts') ->
254- txOut {txOutValue = v <> change } : (txOuts <> txOuts')
255- in if isValueNat change
256- then Right $ if Value. isZero change then tx else tx {txOutputs = outputs}
291+ txOut {txOutValue = v <> nonAdaChange } : (txOuts <> txOuts')
292+ in if isValueNat nonAdaChange
293+ then Right $ if Value. isZero nonAdaChange then tx else tx {txOutputs = outputs}
257294 else Left " Not enough inputs to balance tokens."
258295
296+ hasChangeUTxO :: PubKeyHash -> Tx -> Bool
297+ hasChangeUTxO ownPkh tx =
298+ any ((==) changeAddr . Tx. txOutAddress) $ txOutputs tx
299+ where
300+ changeAddr = Ledger. pubKeyHashAddress (Ledger. PaymentPubKeyHash ownPkh) Nothing
301+
302+ -- | Adds ada change to a transaction, assuming there is already an output going to ownPkh. Otherwise, this is identity
303+ addAdaChange :: PubKeyHash -> Integer -> Tx -> Tx
304+ addAdaChange ownPkh change tx =
305+ tx
306+ { txOutputs =
307+ case partition ((==) changeAddr . Tx. txOutAddress) $ txOutputs tx of
308+ (txOut@ TxOut {txOutValue = v} : txOuts, txOuts') ->
309+ txOut {txOutValue = v <> Ada. lovelaceValueOf change} : (txOuts <> txOuts')
310+ _ -> txOutputs tx
311+ }
312+ where
313+ changeAddr = Ledger. pubKeyHashAddress (Ledger. PaymentPubKeyHash ownPkh) Nothing
314+
259315{- | Add the required signatorioes to the transaction. Be aware the the signature itself is invalid,
260316 and will be ignored. Only the pub key hashes are used, mapped to signing key files on disk.
261317-}
0 commit comments