Skip to content

Commit

Permalink
patch: fixing change (#149)
Browse files Browse the repository at this point in the history
* debugging balancing stages

* balancer fixed

* fmt

* patch: fix balancer
  • Loading branch information
micahkendall authored Sep 8, 2024
1 parent 8986eab commit dcb0d76
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/silly-tomatoes-visit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@blaze-cardano/tx": patch
---

patch: fix balancer
112 changes: 104 additions & 8 deletions packages/blaze-tx/src/tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -880,7 +880,10 @@ export class TxBuilder {
let utxo: TransactionUnspentOutput | undefined;
// Find the matching UTxO for the input.
for (const iterUtxo of this.utxoScope.values()) {
if (iterUtxo.input().toCbor() == input.toCbor()) {
if (
iterUtxo.input().transactionId() == input.transactionId() &&
iterUtxo.input().index() == input.index()
) {
utxo = iterUtxo;
}
}
Expand All @@ -892,10 +895,6 @@ export class TxBuilder {
inputValue = value.merge(inputValue, utxo.output().amount());
}

this.body.outputs()[this.changeOutputIndex!] = new TransactionOutput(
this.changeAddress!,
value.zero(),
);
// Aggregate the total output value from all outputs.
for (const output of this.body.outputs().values()) {
outputValue = value.merge(outputValue, output.amount());
Expand Down Expand Up @@ -943,7 +942,85 @@ export class TxBuilder {
if (spareAmount != 0n) {
return value.merge(tilt, new Value(-spareAmount)); // Subtract 5 ADA from the excess.
}
return tilt;
return value.merge(
tilt,
this.body.outputs()[this.changeOutputIndex!]!.amount(),
);
}

private balanced() {
let withdrawalAmount = 0n;
const withdrawals = this.body.withdrawals();
if (withdrawals !== undefined) {
for (const account of withdrawals.keys()) {
withdrawalAmount += withdrawals.get(account)!;
}
}
// Initialize values for input, output, and minted amounts.
let inputValue = new Value(withdrawalAmount);
let outputValue = new Value(bigintMax(this.fee, this.minimumFee));
const mintValue = new Value(0n, this.body.mint());

// Aggregate the total input value from all inputs.
for (const input of this.body.inputs().values()) {
let utxo: TransactionUnspentOutput | undefined;
// Find the matching UTxO for the input.
for (const iterUtxo of this.utxoScope.values()) {
if (
iterUtxo.input().transactionId() == input.transactionId() &&
iterUtxo.input().index() == input.index()
) {
utxo = iterUtxo;
}
}
// Throw an error if a matching UTxO cannot be found.
if (!utxo) {
throw new Error("Unreachable! UTxO missing!");
}
// Merge the UTxO's output amount into the total input value.
inputValue = value.merge(inputValue, utxo.output().amount());
}
for (const output of this.body.outputs().values()) {
outputValue = value.merge(outputValue, output.amount());
}

for (const cert of this.body.certs()?.values() || []) {
switch (cert.kind()) {
case 0: // Stake Registration
outputValue = value.merge(
outputValue,
new Value(BigInt(this.params.stakeKeyDeposit)),
);
break;
case 1: // Stake Deregistration
inputValue = value.merge(
inputValue,
new Value(BigInt(this.params.stakeKeyDeposit)),
);
break;
case 3: // Pool Registration
if (this.params.poolDeposit) {
outputValue = value.merge(
outputValue,
new Value(BigInt(this.params.poolDeposit)),
);
}
break;
case 4: // Pool Retirement
if (this.params.poolDeposit) {
inputValue = value.merge(
inputValue,
new Value(BigInt(this.params.poolDeposit)),
);
}
break;
}
}
const tilt = value.merge(
value.merge(inputValue, value.negate(outputValue)),
mintValue,
);
return tilt.toCbor() == value.zero().toCbor();
}

/**
Expand Down Expand Up @@ -1408,14 +1485,33 @@ export class TxBuilder {
excessValue = this.getPitch();

this.body.setFee(bigintMax(this.fee, this.minimumFee) + this.feePadding);
this.balanceChange(excessValue);
this.balanceChange(Value.fromCbor(excessValue.toCbor()));
const changeOutput = this.body.outputs()[this.changeOutputIndex!]!;
if (changeOutput.amount().coin() > excessValue.coin()) {
const excessDifference = value.merge(
changeOutput!.amount(),
value.negate(excessValue),
);
// we must add more inputs, to cover the difference
if (spareInputs.length == 0) {
throw new Error("Tx builder could not satisfy coin selection");
}
const selectionResult = this.coinSelector(
spareInputs,
excessDifference,
);
spareInputs = selectionResult.inputs;
for (const input of selectionResult.selectedInputs) {
this.addInput(input);
}
}
if (this.body.collateral()) {
this.balanceCollateralChange();
}
draft_tx.setBody(this.body);
draft_size = final_size;
final_size = draft_tx.toCbor().length / 2;
} while (final_size != draft_size);
} while (final_size != draft_size || !this.balanced());
// Return the fully constructed transaction.
tw.setVkeys(CborSet.fromCore([], VkeyWitness.fromCore));
return new Transaction(this.body, tw, this.auxiliaryData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe("Transaction Building", () => {
),
),
new TransactionUnspentOutput(
new TransactionInput(TransactionId("0".repeat(64)), 0n),
new TransactionInput(TransactionId("1".padStart(64, "0")), 0n),
new TransactionOutput(
testAddress,
value.makeValue(40_000_000n, [ASSET_NAME_1, 1n], [ASSET_NAME_2, 1n]),
Expand All @@ -52,7 +52,7 @@ describe("Transaction Building", () => {
.addUnspentOutputs(utxos)
.setNetworkId(NetworkId.Testnet)
.setChangeAddress(testAddress)
.payAssets(testAddress, value.makeValue(48_708_444n, [ASSET_NAME_1, 1n]))
.payAssets(testAddress, value.makeValue(48_708_900n, [ASSET_NAME_1, 1n]))
.complete();

const inputValue =
Expand Down

0 comments on commit dcb0d76

Please sign in to comment.