Repository navigation
Conversation
MeshTxBuilder evaluates scripts before it adds the change output, so the units it
declares can be wrong for the transaction it finally builds: a script sees the
whole transaction, and an extra output can change what it costs, which changes
the fee, which changes the change. Today that is worked around by hard-coding
ExUnits or applying a multiplier.
This adds an optional balancer that runs the loop instead:
const txHex = await new MeshTxBuilder({ fetcher, balancer: new ScalusTxBalancer("preprod") })
.txOut(bob, [{ unit: "lovelace", quantity: "25000000" }])
.changeAddress(alice)
.selectUtxosFrom(utxos)
.complete();
ITxBalancer sits in @meshsdk/common beside IEvaluator, so the mechanism is not
tied to Scalus. ScalusTxBalancer implements it. Coin selection and change
placement stay with the builder, which passes the index of the output that should
absorb the difference; an implementation may edit only that output's lovelace,
the fee and the redeemers.
Opt-in: a builder with no balancer serializes exactly as before, which a test
pins.
Mesh's own Protocol maps onto the record Scalus reads field for field, so there
is no Blockfrost JSON round trip, and UTxOs go over as CIP-30 [input, output]
pairs that toTxUnspentOutput already produces.
Needs scalus 1.3.0, whose balancer.balanceTx is marked experimental: it may change
shape in any release. Nothing else in the repo depends on it.
|
Hi @nau ! Thanks for you contribution! Could you clarify did you have issues with ex units recalculation in mesh ? |
|
Hi @lisicky, thanks, and you're right: I agree that balancing belongs in coin selection. Looking closer, two gaps remain there:
Repro: a V3 validator whose cost depends on the fee. Mesh declares 5 322 mem, while the final transaction needs 2 311 524, so it fails phase 2. #861 fixes this inside Closing this one in favour of #861. |
MeshTxBuilderevaluates scripts before coin selection adds the change output. A script sees thewhole transaction, so its execution units can be wrong for the transaction finally built, and the
fee and the change both depend on those units. This adds an opt-in balancer that settles the three
together.
ITxBalancerin@meshsdk/common:balanceTx(tx, utxos, params, changeOutputIndex). Coinselection and change placement stay with the builder. A balancer edits only the change output's
lovelace, the fee and the redeemers.
ScalusTxBalancerin@meshsdk/core-cst, through Scalus'sbalancer.balanceTx. Keys a nativescript needs, which cannot be inferred from the inputs, go in
extraSigners.new MeshTxBuilder({ balancer })runs it at the end ofcomplete(). Without one, nothingchanges.
Builds on #850 (Scalus 1.3.0).
Tests
The balancer tests start from a draft with the fee set to zero, so the fee they check is one the
balancer computed. The
MeshTxBuildertest checks whatcomplete()hands the balancer: the changeoutput's index, and the selected UTxO with its address and value.
Node
Scalus is loaded with
import(), so the CJS build works on Node 18. Checked there: the CJS buildloads and the balancer reaches Scalus.