Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export type MeshTxBuilderBody = {
expectedByronAddressWitnesses: string[];
totalCollateral?: Quantity;
collateralReturnAddress?: string;
donation?: Quantity;
};

export const emptyTxBuilderBody = (): MeshTxBuilderBody => ({
Expand Down
4 changes: 4 additions & 0 deletions packages/mesh-core-csl/src/core/adaptor/fromObj/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ export const txBuilderBodyFromObj = (objJson: any): MeshTxBuilderBody => {
txBuilderBody.network = networkFromObj(obj.network);
}

if (obj.donation) {
txBuilderBody.donation = obj.donation.toString();
}

return txBuilderBody;
};

Expand Down
2 changes: 2 additions & 0 deletions packages/mesh-core-csl/src/core/adaptor/toObj/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const meshTxBuilderBodyToObj = ({
votes,
fee,
network,
donation,
}: MeshTxBuilderBody) => {
let mintsObj: object[] = [];
mints.forEach((mint: MintParam) => {
Expand Down Expand Up @@ -61,6 +62,7 @@ export const meshTxBuilderBodyToObj = ({
votes: votes.map(voteToObj),
fee,
network: networkToObj(network),
...(donation !== undefined ? { donation } : {}),
};
};

Expand Down
5 changes: 5 additions & 0 deletions packages/mesh-core-csl/src/core/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ export class CSLSerializer implements IMeshTxSerializer {
txBody: MeshTxBuilderBody,
protocolParams?: Protocol,
): string {
if (txBody.donation) {
throw new Error(
"Treasury donation is not supported by the CSL serializer, use CSTSerializer instead",
);
}
const txBodyJson = JSONbig.stringify(meshTxBuilderBodyToObj(txBody));
const params = JSONbig.stringify(protocolParams || this.protocolParams);

Expand Down
8 changes: 8 additions & 0 deletions packages/mesh-core-cst/src/serializer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,7 @@ class CardanoSDKSerializerCore {
totalCollateral,
collateralReturnAddress,
changeAddress,
donation,
} = txBuilderBody;

const uniqueRefInputs = this.removeBodyInputRefInputOverlap(
Expand Down Expand Up @@ -690,6 +691,13 @@ class CardanoSDKSerializerCore {
);
}
}
if (donation) {
try {
this.txBody.setDonation(BigInt(donation));
} catch (e) {
throwErrorWithOrigin("Error serializing donation", e);
}
}
try {
this.addAllReferenceInputs(uniqueRefInputs);
} catch (e) {
Expand Down
6 changes: 5 additions & 1 deletion packages/mesh-transaction/src/mesh-tx-builder/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ export class MeshTxBuilder extends MeshTxBuilderCore {
const utxosForSelection = await this.getUtxosForSelection();
const implicitValue = {
withdrawals: this.getTotalWithdrawal(),
deposit: this.getTotalDeposit(),
deposit: this.getTotalDeposit() + this.getDonation(),
reclaimDeposit: this.getTotalRefund(),
mint: this.getTotalMint(),
};
Expand Down Expand Up @@ -1311,6 +1311,10 @@ export class MeshTxBuilder extends MeshTxBuilderCore {
return accum;
};

protected getDonation = (): bigint => {
return BigInt(this.meshTxBuilderBody.donation ?? 0);
};

protected getTotalRefund = (): bigint => {
let accum = 0n;
for (let cert of this.meshTxBuilderBody.certificates) {
Expand Down
11 changes: 11 additions & 0 deletions packages/mesh-transaction/src/mesh-tx-builder/tx-builder-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1659,6 +1659,17 @@ export class MeshTxBuilderCore {
return this;
};

/**
* Sets the treasury donation in lovelace.
*/
setDonation = (donation: string) => {
if (BigInt(donation) <= 0n) {
throw new Error("Donation must be a positive amount of lovelace");
}
this.meshTxBuilderBody.donation = donation;
return this;
};

/**
* Sets the network to use, this is mainly to know the cost models to be used to calculate script integrity hash
* @param network The specific network this transaction is being built for ("testnet" | "preview" | "preprod" | "mainnet")
Expand Down
79 changes: 79 additions & 0 deletions packages/mesh-transaction/test/mesh-tx-builder/donation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { CSLSerializer } from "@meshsdk/core-csl";
import { Transaction, TxCBOR } from "@meshsdk/core-cst";
import { MeshTxBuilder } from "@meshsdk/transaction";

import { calculateOutputLovelaces, txHash } from "../test-util";

const address =
"addr_test1qpvx0sacufuypa2k4sngk7q40zc5c4npl337uusdh64kv0uafhxhu32dys6pvn6wlw8dav6cmp4pmtv7cc3yel9uu0nq93swx9";

describe("MeshTxBuilder donation", () => {
let txBuilder: MeshTxBuilder;

beforeEach(() => {
txBuilder = new MeshTxBuilder();
});

it("Should set donation in the tx body and account for it when balancing", async () => {
const tx = await txBuilder
.txIn(
txHash("tx0"),
0,
[{ unit: "lovelace", quantity: "5000000" }],
address,
0,
)
.txOut(address, [{ unit: "lovelace", quantity: "1000000" }])
.setDonation("1500000")
.changeAddress(address)
.complete();

const cardanoTx = Transaction.fromCbor(TxCBOR(tx));
expect(cardanoTx.body().donation()).toEqual(BigInt(1500000));
expect(
calculateOutputLovelaces(tx) +
cardanoTx.body().fee() +
cardanoTx.body().donation()!,
).toEqual(BigInt(5000000));
});

it("Should fail when inputs cannot cover the donation", async () => {
await expect(
txBuilder
.txIn(
txHash("tx0"),
0,
[{ unit: "lovelace", quantity: "2000000" }],
address,
0,
)
.txOut(address, [{ unit: "lovelace", quantity: "1000000" }])
.setDonation("1500000")
.changeAddress(address)
.complete(),
).rejects.toThrow();
});

it("Should reject a non-positive donation", () => {
expect(() => txBuilder.setDonation("0")).toThrow();
expect(() => txBuilder.setDonation("-1")).toThrow();
});

it("Should throw for the CSL serializer", async () => {
const cslBuilder = new MeshTxBuilder({ serializer: new CSLSerializer() });
await expect(
cslBuilder
.txIn(
txHash("tx0"),
0,
[{ unit: "lovelace", quantity: "5000000" }],
address,
0,
)
.txOut(address, [{ unit: "lovelace", quantity: "1000000" }])
.setDonation("1500000")
.changeAddress(address)
.complete(),
).rejects.toThrow(/donation/i);
});
});
Loading