From 6e157f0235ef93e4b2a87d3525726f237d12365e Mon Sep 17 00:00:00 2001 From: "Emmanuel .A" Date: Sat, 14 Mar 2026 00:27:05 +0100 Subject: [PATCH 1/3] feat(tx builder): transaction builder interface V2 --- .../transaction-builder-v2/builder-core.ts | 94 +++++ .../src/types/transaction-builder-v2/index.ts | 5 + .../src/types/transaction-builder-v2/mint.ts | 25 ++ .../src/types/transaction-builder-v2/spend.ts | 32 ++ .../src/types/transaction-builder-v2/vote.ts | 22 ++ .../transaction-builder-v2/withdrawal.ts | 22 ++ .../src/types/transaction-builder/index.ts | 3 +- .../src/mesh-builder-v2/index.ts | 360 ++++++++++++++++++ .../src/mesh-tx-builder/index.ts | 1 + .../src/mesh-tx-builder/tx-builder-core.ts | 11 +- .../test/mesh-tx-builder-v2.test.ts | 138 +++++++ 11 files changed, 708 insertions(+), 5 deletions(-) create mode 100644 packages/mesh-common/src/types/transaction-builder-v2/builder-core.ts create mode 100644 packages/mesh-common/src/types/transaction-builder-v2/index.ts create mode 100644 packages/mesh-common/src/types/transaction-builder-v2/mint.ts create mode 100644 packages/mesh-common/src/types/transaction-builder-v2/spend.ts create mode 100644 packages/mesh-common/src/types/transaction-builder-v2/vote.ts create mode 100644 packages/mesh-common/src/types/transaction-builder-v2/withdrawal.ts create mode 100644 packages/mesh-transaction/src/mesh-builder-v2/index.ts create mode 100644 packages/mesh-transaction/test/mesh-tx-builder-v2.test.ts diff --git a/packages/mesh-common/src/types/transaction-builder-v2/builder-core.ts b/packages/mesh-common/src/types/transaction-builder-v2/builder-core.ts new file mode 100644 index 000000000..d9c764821 --- /dev/null +++ b/packages/mesh-common/src/types/transaction-builder-v2/builder-core.ts @@ -0,0 +1,94 @@ +import { MeshTxBuilder, RefTxIn, Voter, VotingProcedure } from "@meshsdk/core"; +import { SpendRedeemerBuilder } from "./spend"; +import { MintRedeemerBuilder } from "./mint"; +import { WithdrawRedeemerBuilder } from "./withdrawal"; +import { VoteRedeemerBuilder } from "./vote"; + +export interface _MeshTxBuilderV2 + extends Omit { + spendPlutusV1(txHash: string, txIndex: number): SpendRedeemerBuilder; + spendPlutusV2(txHash: string, txIndex: number): SpendRedeemerBuilder; + spendPlutusV3(txHash: string, txIndex: number): SpendRedeemerBuilder; + + mintPlutusV1( + quantity: string, + policyId: string, + assetName: string, + ): MintRedeemerBuilder; + mintPlutusV2( + quantity: string, + policyId: string, + assetName: string, + ): MintRedeemerBuilder; + mintPlutusV3( + quantity: string, + policyId: string, + assetName: string, + ): MintRedeemerBuilder; + + withdrawPlutusV1( + rewardAddress: string, + amount: string, + ): WithdrawRedeemerBuilder; + withdrawPlutusV2( + rewardAddress: string, + amount: string, + ): WithdrawRedeemerBuilder; + withdrawPlutusV3( + rewardAddress: string, + amount: string, + ): WithdrawRedeemerBuilder; + + votePlutusV1( + voter: Voter, + govActionId: RefTxIn, + votingProcedure: VotingProcedure, + ): VoteRedeemerBuilder; + votePlutusV2( + voter: Voter, + govActionId: RefTxIn, + votingProcedure: VotingProcedure, + ): VoteRedeemerBuilder; + votePlutusV3( + voter: Voter, + govActionId: RefTxIn, + votingProcedure: VotingProcedure, + ): VoteRedeemerBuilder; +} + +type ScriptMethodsToDrop = + | "spendingPlutusScript" + | "spendingPlutusScriptV1" + | "spendingPlutusScriptV2" + | "spendingPlutusScriptV3" + | "spendingTxInReference" + | "spendingReferenceTxInInlineDatumPresent" + | "spendingReferenceTxInRedeemerValue" + | "mintPlutusScript" + | "mintPlutusScriptV1" + | "mintPlutusScriptV2" + | "mintPlutusScriptV3" + | "mintingScript" + | "mintTxInReference" + | "mintReferenceTxInRedeemerValue" + | "mintRedeemerValue" + | "withdrawalPlutusScript" + | "withdrawalPlutusScriptV1" + | "withdrawalPlutusScriptV2" + | "withdrawalPlutusScriptV3" + | "withdrawalScript" + | "withdrawalTxInReference" + | "withdrawalReferenceTxInRedeemerValue" + | "withdrawalRedeemerValue" + | "votePlutusScript" + | "votePlutusScriptV1" + | "votePlutusScriptV2" + | "votePlutusScriptV3" + | "voteScript" + | "voteTxInReference" + | "voteReferenceTxInRedeemerValue" + | "voteRedeemerValue" + | "txInScript" + | "txInDatumValue" + | "txInInlineDatumPresent" + | "txInRedeemerValue"; diff --git a/packages/mesh-common/src/types/transaction-builder-v2/index.ts b/packages/mesh-common/src/types/transaction-builder-v2/index.ts new file mode 100644 index 000000000..58cb915ea --- /dev/null +++ b/packages/mesh-common/src/types/transaction-builder-v2/index.ts @@ -0,0 +1,5 @@ +export * from './builder-core'; +export * from './mint'; +export * from './spend'; +export * from './vote'; +export * from './withdrawal' \ No newline at end of file diff --git a/packages/mesh-common/src/types/transaction-builder-v2/mint.ts b/packages/mesh-common/src/types/transaction-builder-v2/mint.ts new file mode 100644 index 000000000..d6083b8bf --- /dev/null +++ b/packages/mesh-common/src/types/transaction-builder-v2/mint.ts @@ -0,0 +1,25 @@ +import { Asset, Budget, BuilderData } from "@meshsdk/core"; +import { _MeshTxBuilderV2 } from "./builder-core"; + +export interface MintRedeemerBuilder { + redeemerJson( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): MintScriptBuilder; + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): MintScriptBuilder; + } + export interface MintScriptBuilder { + script(scriptCbor: string): MintTxOutBuilder; + referenceScript( + txHash: string, + txIndex: number, + scriptSize?: string, + scriptHash?: string, + ): MintTxOutBuilder; + } + export interface MintTxOutBuilder { + txOut(address: string, amount: Asset[]): _MeshTxBuilderV2; + } \ No newline at end of file diff --git a/packages/mesh-common/src/types/transaction-builder-v2/spend.ts b/packages/mesh-common/src/types/transaction-builder-v2/spend.ts new file mode 100644 index 000000000..e1acfba09 --- /dev/null +++ b/packages/mesh-common/src/types/transaction-builder-v2/spend.ts @@ -0,0 +1,32 @@ +import { Asset, Budget, BuilderData } from "@meshsdk/core"; +import { _MeshTxBuilderV2 } from "./builder-core"; + +export interface SpendRedeemerBuilder { + redeemerJson( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): SpendScriptBuilder; + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): SpendScriptBuilder; + } + + export interface SpendScriptBuilder { + script(scriptCbor: string): SpendDatumBuilder; + referenceScript( + refTxHash: string, + refTxIndex: number, + scriptSize?: string, + scriptHash?: string, + ): SpendDatumBuilder; + } + export interface SpendDatumBuilder { + datumHash(datumHash: string): SpendTxOutBuilder; + datumCbor(datumCbor: BuilderData["content"]): SpendTxOutBuilder; + datumJson(datumJson: BuilderData["content"]): SpendTxOutBuilder; + } + + export interface SpendTxOutBuilder { + txOut(address: string, amount: Asset[]): _MeshTxBuilderV2; + } \ No newline at end of file diff --git a/packages/mesh-common/src/types/transaction-builder-v2/vote.ts b/packages/mesh-common/src/types/transaction-builder-v2/vote.ts new file mode 100644 index 000000000..8163d86e2 --- /dev/null +++ b/packages/mesh-common/src/types/transaction-builder-v2/vote.ts @@ -0,0 +1,22 @@ +import { Budget, BuilderData } from "@meshsdk/core"; +import { _MeshTxBuilderV2 } from "./builder-core"; + +export interface VoteRedeemerBuilder { + redeemerJson( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): VoteScriptBuilder; + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): VoteScriptBuilder; +} +export interface VoteScriptBuilder { + script(scriptCbor: string): _MeshTxBuilderV2; + referenceScript( + txHash: string, + txIndex: number, + scriptSize?: string, + scriptHash?: string, + ): _MeshTxBuilderV2; +} diff --git a/packages/mesh-common/src/types/transaction-builder-v2/withdrawal.ts b/packages/mesh-common/src/types/transaction-builder-v2/withdrawal.ts new file mode 100644 index 000000000..a4760ed44 --- /dev/null +++ b/packages/mesh-common/src/types/transaction-builder-v2/withdrawal.ts @@ -0,0 +1,22 @@ +import { Budget, BuilderData } from "@meshsdk/core"; +import { _MeshTxBuilderV2 } from "./builder-core"; + +export interface WithdrawRedeemerBuilder { + redeemerJson( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): WithdrawScriptBuilder; + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): WithdrawScriptBuilder; +} +export interface WithdrawScriptBuilder { + script(scriptCbor: string): _MeshTxBuilderV2; + referenceScript( + txHash: string, + txIndex: number, + scriptSize?: string, + scriptHash?: string, + ): _MeshTxBuilderV2; +} \ No newline at end of file diff --git a/packages/mesh-common/src/types/transaction-builder/index.ts b/packages/mesh-common/src/types/transaction-builder/index.ts index b03e62d93..df79ae535 100644 --- a/packages/mesh-common/src/types/transaction-builder/index.ts +++ b/packages/mesh-common/src/types/transaction-builder/index.ts @@ -19,6 +19,7 @@ export * from "./withdrawal"; export * from "./certificate"; export * from "./vote"; export * from "./proposal"; +export * from "./tx-builder-v2"; export type MeshTxBuilderBody = { inputs: TxIn[]; @@ -35,7 +36,7 @@ export type MeshTxBuilderBody = { certificates: Certificate[]; withdrawals: Withdrawal[]; votes: Vote[]; - proposals: Proposal[]; + proposals: Proposal[]; signingKey: string[]; extraInputs: UTxO[]; chainedTxs: string[]; diff --git a/packages/mesh-transaction/src/mesh-builder-v2/index.ts b/packages/mesh-transaction/src/mesh-builder-v2/index.ts new file mode 100644 index 000000000..d85fa92f9 --- /dev/null +++ b/packages/mesh-transaction/src/mesh-builder-v2/index.ts @@ -0,0 +1,360 @@ +import type { + _MeshTxBuilderV2, + BuilderData, + MintRedeemerBuilder, + MintScriptBuilder, + MintTxOutBuilder, + RefTxIn, + SpendDatumBuilder, + SpendRedeemerBuilder, + SpendScriptBuilder, + SpendTxOutBuilder, + Voter, + VoteRedeemerBuilder, + VoteScriptBuilder, + VotingProcedure, + WithdrawRedeemerBuilder, + WithdrawScriptBuilder, +} from "@meshsdk/common"; +import { Asset, Budget, DEFAULT_REDEEMER_BUDGET } from "@meshsdk/common"; +import { MeshTxBuilder, MeshTxBuilderOptions } from "@meshsdk/core"; + +import { MeshTxBuilderCore } from "../mesh-tx-builder/tx-builder-core"; + +class SpendBuilderImpl + implements + SpendRedeemerBuilder, + SpendScriptBuilder, + SpendDatumBuilder, + SpendTxOutBuilder +{ + constructor(private readonly core: MeshTxBuilder) {} + + redeemerJson( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): SpendScriptBuilder { + this.core.txInRedeemerValue( + redeemer, + "JSON", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this; + } + + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): SpendScriptBuilder { + this.core.txInRedeemerValue( + redeemer, + "CBOR", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this; + } + + script(scriptCbor: string): SpendDatumBuilder { + this.core.txInScript(scriptCbor); + return this; + } + + referenceScript( + refTxHash: string, + refTxIndex: number, + scriptSize?: string, + scriptHash?: string, + ): SpendDatumBuilder { + this.core.spendingTxInReference( + refTxHash, + refTxIndex, + scriptSize, + scriptHash, + ); + return this; + } + + datumJson(datum: BuilderData["content"]): SpendTxOutBuilder { + this.core.txOutInlineDatumValue(datum, "JSON"); + return this; + } + + datumCbor(datum: BuilderData["content"]): SpendTxOutBuilder { + this.core.txOutInlineDatumValue(datum, "CBOR"); + return this; + } + + datumHash(datumHash: string): SpendTxOutBuilder { + this.core.txOutDatumHashValue(datumHash); + return this; + } + + txOut(address: string, amount: Asset[]): _MeshTxBuilderV2 { + this.core.txInInlineDatumPresent(); + this.core.txOut(address, amount); + return this.core as unknown as _MeshTxBuilderV2; + } +} + +class MintBuilderImpl + implements MintRedeemerBuilder, MintScriptBuilder, MintTxOutBuilder +{ + constructor(private readonly core: MeshTxBuilder) {} + + redeemerJson( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): MintScriptBuilder { + this.core.mintRedeemerValue( + redeemer, + "JSON", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this; + } + + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): MintScriptBuilder { + this.core.mintRedeemerValue( + redeemer, + "CBOR", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this; + } + + script(scriptCbor: string): MintTxOutBuilder { + this.core.mintingScript(scriptCbor); + return this; + } + + referenceScript( + txHash: string, + txIndex: number, + scriptSize?: string, + scriptHash?: string, + ): MintTxOutBuilder { + this.core.mintTxInReference(txHash, txIndex, scriptSize, scriptHash); + return this; + } + + txOut(address: string, amount: Asset[]): _MeshTxBuilderV2 { + this.core.txOut(address, amount); + return this.core as unknown as _MeshTxBuilderV2; + } +} + +class WithdrawBuilderImpl + implements WithdrawRedeemerBuilder, WithdrawScriptBuilder +{ + constructor(private readonly core: MeshTxBuilder) {} + + redeemerJson( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): WithdrawScriptBuilder { + this.core.withdrawalRedeemerValue( + redeemer, + "JSON", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this; + } + + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): WithdrawScriptBuilder { + this.core.withdrawalRedeemerValue( + redeemer, + "CBOR", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this; + } + + script(scriptCbor: string): _MeshTxBuilderV2 { + this.core.withdrawalScript(scriptCbor); + return this.core as unknown as _MeshTxBuilderV2; + } + + referenceScript( + txHash: string, + txIndex: number, + scriptSize?: string, + scriptHash?: string, + ): _MeshTxBuilderV2 { + this.core.withdrawalTxInReference(txHash, txIndex, scriptSize, scriptHash); + return this.core as unknown as _MeshTxBuilderV2; + } +} + +class VoteBuilderImpl implements VoteRedeemerBuilder, VoteScriptBuilder { + constructor(private readonly core: MeshTxBuilder) {} + + redeemerJson( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): VoteScriptBuilder { + this.core.voteRedeemerValue( + redeemer, + "JSON", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this; + } + + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): VoteScriptBuilder { + this.core.voteRedeemerValue( + redeemer, + "CBOR", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this; + } + + script(scriptCbor: string): _MeshTxBuilderV2 { + this.core.voteScript(scriptCbor); + return this.core as unknown as _MeshTxBuilderV2; + } + + referenceScript( + txHash: string, + txIndex: number, + scriptSize?: string, + scriptHash?: string, + ): _MeshTxBuilderV2 { + this.core.voteTxInReference(txHash, txIndex, scriptSize, scriptHash); + return this.core as unknown as _MeshTxBuilderV2; + } +} + +class __MeshTxBuilderV2 extends MeshTxBuilder { + spendPlutusV1(txHash: string, txIndex: number): SpendRedeemerBuilder { + this.spendingPlutusScriptV1(); + this.txIn(txHash, txIndex); + return new SpendBuilderImpl(this); + } + spendPlutusV2(txHash: string, txIndex: number): SpendRedeemerBuilder { + this.spendingPlutusScriptV2(); + this.txIn(txHash, txIndex); + return new SpendBuilderImpl(this); + } + spendPlutusV3(txHash: string, txIndex: number): SpendRedeemerBuilder { + this.spendingPlutusScriptV3(); + this.txIn(txHash, txIndex); + return new SpendBuilderImpl(this); + } + + mintPlutusV1( + quantity: string, + policyId: string, + assetName: string, + ): MintRedeemerBuilder { + this.mintPlutusScriptV1(); + this.mint(quantity, policyId, assetName); + return new MintBuilderImpl(this); + } + mintPlutusV2( + quantity: string, + policyId: string, + assetName: string, + ): MintRedeemerBuilder { + this.mintPlutusScriptV2(); + this.mint(quantity, policyId, assetName); + return new MintBuilderImpl(this); + } + mintPlutusV3( + quantity: string, + policyId: string, + assetName: string, + ): MintRedeemerBuilder { + this.mintPlutusScriptV3(); + this.mint(quantity, policyId, assetName); + return new MintBuilderImpl(this); + } + + withdrawPlutusV1( + rewardAddress: string, + coin: string, + ): WithdrawRedeemerBuilder { + this.withdrawalPlutusScriptV1(); + this.withdrawal(rewardAddress, coin); + return new WithdrawBuilderImpl(this); + } + withdrawPlutusV2( + rewardAddress: string, + coin: string, + ): WithdrawRedeemerBuilder { + this.withdrawalPlutusScriptV2(); + this.withdrawal(rewardAddress, coin); + return new WithdrawBuilderImpl(this); + } + withdrawPlutusV3( + rewardAddress: string, + coin: string, + ): WithdrawRedeemerBuilder { + this.withdrawalPlutusScriptV3(); + this.withdrawal(rewardAddress, coin); + return new WithdrawBuilderImpl(this); + } + + votePlutusV1( + voter: Voter, + govActionId: RefTxIn, + votingProcedure: VotingProcedure, + ): VoteRedeemerBuilder { + this.votePlutusScriptV1(); + this.vote(voter, govActionId, votingProcedure); + return new VoteBuilderImpl(this); + } + votePlutusV2( + voter: Voter, + govActionId: RefTxIn, + votingProcedure: VotingProcedure, + ): VoteRedeemerBuilder { + this.votePlutusScriptV2(); + this.vote(voter, govActionId, votingProcedure); + return new VoteBuilderImpl(this); + } + votePlutusV3( + voter: Voter, + govActionId: RefTxIn, + votingProcedure: VotingProcedure, + ): VoteRedeemerBuilder { + this.votePlutusScriptV3(); + this.vote(voter, govActionId, votingProcedure); + return new VoteBuilderImpl(this); + } +} + +/** + * `MeshTxBuilderV2` is a strongly-typed wrapper around the core `MeshTxBuilder` + * that enforces a rigid Type-State machine for Plutus operations. + * + * It natively provides TypeScript autocomplete restrictions (Script -> Datum -> Redeemer) + * for transactions like spending, minting, withdrawing, and voting, dropping standalone + * script methods to prevent incorrect chained execution ordering. + * + * @example + * ```typescript + * const tx = new MeshTxBuilderV2({ + * fetcher: provider, + * evaluator: provider, + * }); + * + * tx.spendPlutusV3(txHash, index) + * .script(scriptCbor) + * .datumJson(datumValue) + * .redeemerJson(redeemerValue) + * .txOut(address, assets); // Drops you back into the main builder methods + * ``` + */ +export const MeshTxBuilderV2: new ( + options?: MeshTxBuilderOptions, +) => _MeshTxBuilderV2 = __MeshTxBuilderV2; diff --git a/packages/mesh-transaction/src/mesh-tx-builder/index.ts b/packages/mesh-transaction/src/mesh-tx-builder/index.ts index b6c6339f5..508a0e7fb 100644 --- a/packages/mesh-transaction/src/mesh-tx-builder/index.ts +++ b/packages/mesh-transaction/src/mesh-tx-builder/index.ts @@ -1878,3 +1878,4 @@ export const getOutputMinLovelace = ( }; export * from "./utils"; +export { MeshTxBuilderV2 } from "../mesh-builder-v2"; diff --git a/packages/mesh-transaction/src/mesh-tx-builder/tx-builder-core.ts b/packages/mesh-transaction/src/mesh-tx-builder/tx-builder-core.ts index 5ba3064ef..aa53f559d 100644 --- a/packages/mesh-transaction/src/mesh-tx-builder/tx-builder-core.ts +++ b/packages/mesh-transaction/src/mesh-tx-builder/tx-builder-core.ts @@ -13,7 +13,6 @@ import { DEFAULT_REDEEMER_BUDGET, DRep, DREP_DEPOSIT, - VOTING_PROPOSAL_DEPOSIT, emptyTxBuilderBody, GovernanceAction, LanguageVersion, @@ -29,8 +28,8 @@ import { PubKeyTxIn, Quantity, Redeemer, - RewardAddress, RefTxIn, + RewardAddress, TxIn, TxInParameter, Unit, @@ -38,6 +37,7 @@ import { UtxoSelection, Vote, Voter, + VOTING_PROPOSAL_DEPOSIT, VotingProcedure, Withdrawal, } from "@meshsdk/common"; @@ -1219,9 +1219,12 @@ export class MeshTxBuilderCore { type: BuilderData["type"] = "Mesh", exUnits = { ...DEFAULT_REDEEMER_BUDGET }, ) => { - if (!this.proposalItem) throw Error("proposalRedeemerValue: Undefined proposal"); + if (!this.proposalItem) + throw Error("proposalRedeemerValue: Undefined proposal"); if (!(this.proposalItem.type === "ScriptProposal")) - throw Error("proposalRedeemerValue: Adding redeemer to non plutus proposal"); + throw Error( + "proposalRedeemerValue: Adding redeemer to non plutus proposal", + ); this.proposalItem.redeemer = this.castBuilderDataToRedeemer( redeemer, type, diff --git a/packages/mesh-transaction/test/mesh-tx-builder-v2.test.ts b/packages/mesh-transaction/test/mesh-tx-builder-v2.test.ts new file mode 100644 index 000000000..422ce67ca --- /dev/null +++ b/packages/mesh-transaction/test/mesh-tx-builder-v2.test.ts @@ -0,0 +1,138 @@ +import { Asset, Budget, LanguageVersion } from "@meshsdk/common"; + +import { MeshTxBuilderV2 } from "../src"; + +describe("MeshTxBuilderV2 Grouped interfaces", () => { + it("should build a tx with spendPlutusV2", () => { + const tx = new MeshTxBuilderV2(); + const mockHash = + "0000000000000000000000000000000000000000000000000000000000000000"; + + tx.spendPlutusV2(mockHash, 0) + .redeemerJson({ action: "spend" }) + .script("112233") + .datumJson({ data: "foo" }) + .txOut( + "addr_test1vzuwvztxzv3j4a2wvy7xntry2a8y869svj4a7y7qy2wvywqzcqxny", + [], + ); + + // @ts-ignore + tx.queueAllLastItem(); + + const body = tx.meshTxBuilderBody; + + // Check we configured script spending correctly + const input = body.inputs[0]; + expect(input?.type).toBe("Script"); + + if (input?.type === "Script") { + expect(input.txIn.txHash).toBe(mockHash); + expect(input.txIn.txIndex).toBe(0); + + // script + expect(input.scriptTxIn.scriptSource?.type).toBe("Provided"); + if (input.scriptTxIn.scriptSource?.type === "Provided") { + expect(input.scriptTxIn.scriptSource?.script.code).toBe("112233"); + expect(input.scriptTxIn.scriptSource?.script.version).toBe("V2"); + } + + // datum + expect(input.scriptTxIn.datumSource?.type).toBe("Inline"); + + // redeemer + expect(input.scriptTxIn.redeemer?.data.type).toBe("JSON"); + } + + // Output + expect(body.outputs.length).toBe(1); + expect(body.outputs[0]?.address).toBe( + "addr_test1vzuwvztxzv3j4a2wvy7xntry2a8y869svj4a7y7qy2wvywqzcqxny", + ); + }); + + it("should have correct mintPlutus interface", () => { + const tx = new MeshTxBuilderV2(); + tx.mintPlutusV2("1", "policyId123", "assetName456") + .redeemerCbor("998877") + .script("556677") + .txOut( + "addr_test1vzuwvztxzv3j4a2wvy7xntry2a8y869svj4a7y7qy2wvywqzcqxny", + [], + ); + + // @ts-ignore + tx.queueAllLastItem(); + + const body = tx.meshTxBuilderBody; + + expect(body.mints[0]?.type).toBe("Plutus"); + expect(body.mints[0]?.policyId).toBe("policyId123"); + expect(body.mints[0]?.mintValue[0]?.amount).toBe("1"); + + if ( + body.mints[0]?.type === "Plutus" && + body.mints[0]?.scriptSource?.type === "Provided" + ) { + expect(body.mints[0]?.scriptSource.script.code).toBe("556677"); + } + + expect(body.mints[0]?.redeemer?.data.type).toBe("CBOR"); + }); + + it("should build a tx with withdrawPlutusV2", () => { + const tx = new MeshTxBuilderV2(); + tx.withdrawPlutusV2("stake_test1uqevw2xnsc0pvn9t9r9c7qryfqfeerchgrlm3ea2nefr9hqp8n5xl", "1000000") + .redeemerJson({ action: "withdraw" }) + .script("445566"); + + // @ts-ignore + tx.queueAllLastItem(); + + const body = tx.meshTxBuilderBody; + + expect(body.withdrawals.length).toBe(1); + expect(body.withdrawals[0]?.type).toBe("ScriptWithdrawal"); + if (body.withdrawals[0]?.type === "ScriptWithdrawal") { + expect(body.withdrawals[0]?.address).toBe("stake_test1uqevw2xnsc0pvn9t9r9c7qryfqfeerchgrlm3ea2nefr9hqp8n5xl"); + expect(body.withdrawals[0]?.coin).toBe("1000000"); + + expect(body.withdrawals[0]?.scriptSource?.type).toBe("Provided"); + if (body.withdrawals[0]?.scriptSource?.type === "Provided") { + expect(body.withdrawals[0]?.scriptSource.script.code).toBe("445566"); + } + + expect(body.withdrawals[0]?.redeemer?.data.type).toBe("JSON"); + } + }); + + it("should build a tx with votePlutusV2", () => { + const tx = new MeshTxBuilderV2(); + tx.votePlutusV2( + { type: "DRep", drepId: "drep_id_here" }, + { txHash: "0000000000000000000000000000000000000000000000000000000000000000", txIndex: 0 }, + { voteKind: "Yes" } + ) + .redeemerJson({ action: "vote" }) + .script("111111"); + + // @ts-ignore + tx.queueAllLastItem(); + + const body = tx.meshTxBuilderBody; + + expect(body.votes.length).toBe(1); + expect(body.votes[0]?.type).toBe("ScriptVote"); + if (body.votes[0]?.type === "ScriptVote") { + expect(body.votes[0]?.vote.voter.type).toBe("DRep"); + expect(body.votes[0]?.vote.votingProcedure.voteKind).toBe("Yes"); + + expect(body.votes[0]?.scriptSource?.type).toBe("Provided"); + if (body.votes[0]?.scriptSource?.type === "Provided") { + expect(body.votes[0]?.scriptSource.script.code).toBe("111111"); + } + + expect(body.votes[0]?.redeemer?.data.type).toBe("JSON"); + } + }); +}); From cd8c8c408c143c152f2b86e0c9e4757e0f266007 Mon Sep 17 00:00:00 2001 From: "Emmanuel .A" Date: Sat, 14 Mar 2026 00:28:07 +0100 Subject: [PATCH 2/3] feat(tx-builder): transaction builder interface v2 --- packages/mesh-common/src/types/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/mesh-common/src/types/index.ts b/packages/mesh-common/src/types/index.ts index ebac87471..a5f59df6e 100644 --- a/packages/mesh-common/src/types/index.ts +++ b/packages/mesh-common/src/types/index.ts @@ -25,3 +25,4 @@ export * from "./transaction-builder"; export * from "./deserialized"; export * from "./blueprint"; export * from "./governance"; +export * from "./transaction-builder-v2"; From acf26fde8bd26559d80c37717f590bad2e38f1dc Mon Sep 17 00:00:00 2001 From: "Emmanuel .A" Date: Tue, 17 Mar 2026 05:06:24 +0100 Subject: [PATCH 3/3] feat: integrate meshTxBuilder V2 prototype --- .../transaction-builder-v2/builder-core.ts | 32 +-- .../src/types/transaction-builder-v2/mint.ts | 45 ++-- .../src/types/transaction-builder-v2/spend.ts | 55 ++-- .../src/types/transaction-builder-v2/vote.ts | 22 +- .../transaction-builder-v2/withdrawal.ts | 24 +- .../src/types/transaction-builder/index.ts | 1 - packages/mesh-transaction/src/index.ts | 1 + .../index.ts | 241 +++++++++--------- .../src/mesh-tx-builder/index.ts | 1 - .../test/mesh-tx-builder-v2.test.ts | 19 +- 10 files changed, 224 insertions(+), 217 deletions(-) rename packages/mesh-transaction/src/{mesh-builder-v2 => mesh-tx-builder-v2}/index.ts (63%) diff --git a/packages/mesh-common/src/types/transaction-builder-v2/builder-core.ts b/packages/mesh-common/src/types/transaction-builder-v2/builder-core.ts index d9c764821..4c7cdf09b 100644 --- a/packages/mesh-common/src/types/transaction-builder-v2/builder-core.ts +++ b/packages/mesh-common/src/types/transaction-builder-v2/builder-core.ts @@ -1,59 +1,59 @@ import { MeshTxBuilder, RefTxIn, Voter, VotingProcedure } from "@meshsdk/core"; -import { SpendRedeemerBuilder } from "./spend"; -import { MintRedeemerBuilder } from "./mint"; -import { WithdrawRedeemerBuilder } from "./withdrawal"; -import { VoteRedeemerBuilder } from "./vote"; +import { SpendScriptBuilder } from "./spend"; +import { MintScriptBuilder } from "./mint"; +import { WithdrawScriptBuilder } from "./withdrawal"; +import { VoteScriptBuilder } from "./vote"; export interface _MeshTxBuilderV2 extends Omit { - spendPlutusV1(txHash: string, txIndex: number): SpendRedeemerBuilder; - spendPlutusV2(txHash: string, txIndex: number): SpendRedeemerBuilder; - spendPlutusV3(txHash: string, txIndex: number): SpendRedeemerBuilder; + spendPlutusV1(txHash: string, txIndex: number): SpendScriptBuilder; + spendPlutusV2(txHash: string, txIndex: number): SpendScriptBuilder; + spendPlutusV3(txHash: string, txIndex: number): SpendScriptBuilder; mintPlutusV1( quantity: string, policyId: string, assetName: string, - ): MintRedeemerBuilder; + ): MintScriptBuilder; mintPlutusV2( quantity: string, policyId: string, assetName: string, - ): MintRedeemerBuilder; + ): MintScriptBuilder; mintPlutusV3( quantity: string, policyId: string, assetName: string, - ): MintRedeemerBuilder; + ): MintScriptBuilder; withdrawPlutusV1( rewardAddress: string, amount: string, - ): WithdrawRedeemerBuilder; + ): WithdrawScriptBuilder; withdrawPlutusV2( rewardAddress: string, amount: string, - ): WithdrawRedeemerBuilder; + ): WithdrawScriptBuilder; withdrawPlutusV3( rewardAddress: string, amount: string, - ): WithdrawRedeemerBuilder; + ): WithdrawScriptBuilder; votePlutusV1( voter: Voter, govActionId: RefTxIn, votingProcedure: VotingProcedure, - ): VoteRedeemerBuilder; + ): VoteScriptBuilder; votePlutusV2( voter: Voter, govActionId: RefTxIn, votingProcedure: VotingProcedure, - ): VoteRedeemerBuilder; + ): VoteScriptBuilder; votePlutusV3( voter: Voter, govActionId: RefTxIn, votingProcedure: VotingProcedure, - ): VoteRedeemerBuilder; + ): VoteScriptBuilder; } type ScriptMethodsToDrop = diff --git a/packages/mesh-common/src/types/transaction-builder-v2/mint.ts b/packages/mesh-common/src/types/transaction-builder-v2/mint.ts index d6083b8bf..83913a77e 100644 --- a/packages/mesh-common/src/types/transaction-builder-v2/mint.ts +++ b/packages/mesh-common/src/types/transaction-builder-v2/mint.ts @@ -1,25 +1,28 @@ import { Asset, Budget, BuilderData } from "@meshsdk/core"; + import { _MeshTxBuilderV2 } from "./builder-core"; +export interface MintScriptBuilder { + script(scriptCbor: string): MintRedeemerBuilder; + referenceScript( + txHash: string, + txIndex: number, + scriptSize?: string, + scriptHash?: string, + ): MintRedeemerBuilder; +} + export interface MintRedeemerBuilder { - redeemerJson( - redeemer: BuilderData["content"], - exUnits?: Budget, - ): MintScriptBuilder; - redeemerCbor( - redeemer: BuilderData["content"], - exUnits?: Budget, - ): MintScriptBuilder; - } - export interface MintScriptBuilder { - script(scriptCbor: string): MintTxOutBuilder; - referenceScript( - txHash: string, - txIndex: number, - scriptSize?: string, - scriptHash?: string, - ): MintTxOutBuilder; - } - export interface MintTxOutBuilder { - txOut(address: string, amount: Asset[]): _MeshTxBuilderV2; - } \ No newline at end of file + redeemerJson( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): MintTxOutBuilder; + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): MintTxOutBuilder; +} + +export interface MintTxOutBuilder { + txOut(address: string, amount: Asset[]): _MeshTxBuilderV2; +} diff --git a/packages/mesh-common/src/types/transaction-builder-v2/spend.ts b/packages/mesh-common/src/types/transaction-builder-v2/spend.ts index e1acfba09..2f05144a7 100644 --- a/packages/mesh-common/src/types/transaction-builder-v2/spend.ts +++ b/packages/mesh-common/src/types/transaction-builder-v2/spend.ts @@ -1,32 +1,33 @@ import { Asset, Budget, BuilderData } from "@meshsdk/core"; import { _MeshTxBuilderV2 } from "./builder-core"; +export interface SpendScriptBuilder { + script(scriptCbor: string): SpendRedeemerBuilder; + referenceScript( + refTxHash: string, + refTxIndex: number, + scriptSize?: string, + scriptHash?: string, + ): SpendRedeemerBuilder; +} + export interface SpendRedeemerBuilder { - redeemerJson( - redeemer: BuilderData["content"], - exUnits?: Budget, - ): SpendScriptBuilder; - redeemerCbor( - redeemer: BuilderData["content"], - exUnits?: Budget, - ): SpendScriptBuilder; - } - - export interface SpendScriptBuilder { - script(scriptCbor: string): SpendDatumBuilder; - referenceScript( - refTxHash: string, - refTxIndex: number, - scriptSize?: string, - scriptHash?: string, - ): SpendDatumBuilder; - } - export interface SpendDatumBuilder { - datumHash(datumHash: string): SpendTxOutBuilder; - datumCbor(datumCbor: BuilderData["content"]): SpendTxOutBuilder; - datumJson(datumJson: BuilderData["content"]): SpendTxOutBuilder; - } + redeemerJson( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): SpendTxOutBuilder; + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): SpendTxOutBuilder; +} + +export interface SpendTxOutBuilder { + txOut(address: string, amount: Asset[]): SpendDatumBuilder; +} - export interface SpendTxOutBuilder { - txOut(address: string, amount: Asset[]): _MeshTxBuilderV2; - } \ No newline at end of file +export interface SpendDatumBuilder { + datumHash(datumHash: string): _MeshTxBuilderV2; + datumCbor(datumCbor: BuilderData["content"]): _MeshTxBuilderV2; + datumJson(datumJson: BuilderData["content"]): _MeshTxBuilderV2; +} \ No newline at end of file diff --git a/packages/mesh-common/src/types/transaction-builder-v2/vote.ts b/packages/mesh-common/src/types/transaction-builder-v2/vote.ts index 8163d86e2..13e7ec962 100644 --- a/packages/mesh-common/src/types/transaction-builder-v2/vote.ts +++ b/packages/mesh-common/src/types/transaction-builder-v2/vote.ts @@ -1,22 +1,24 @@ import { Budget, BuilderData } from "@meshsdk/core"; + import { _MeshTxBuilderV2 } from "./builder-core"; +export interface VoteScriptBuilder { + script(scriptCbor: string): VoteRedeemerBuilder; + referenceScript( + txHash: string, + txIndex: number, + scriptSize?: string, + scriptHash?: string, + ): VoteRedeemerBuilder; +} + export interface VoteRedeemerBuilder { redeemerJson( redeemer: BuilderData["content"], exUnits?: Budget, - ): VoteScriptBuilder; + ): _MeshTxBuilderV2; redeemerCbor( redeemer: BuilderData["content"], exUnits?: Budget, - ): VoteScriptBuilder; -} -export interface VoteScriptBuilder { - script(scriptCbor: string): _MeshTxBuilderV2; - referenceScript( - txHash: string, - txIndex: number, - scriptSize?: string, - scriptHash?: string, ): _MeshTxBuilderV2; } diff --git a/packages/mesh-common/src/types/transaction-builder-v2/withdrawal.ts b/packages/mesh-common/src/types/transaction-builder-v2/withdrawal.ts index a4760ed44..98d84cb26 100644 --- a/packages/mesh-common/src/types/transaction-builder-v2/withdrawal.ts +++ b/packages/mesh-common/src/types/transaction-builder-v2/withdrawal.ts @@ -1,22 +1,24 @@ import { Budget, BuilderData } from "@meshsdk/core"; + import { _MeshTxBuilderV2 } from "./builder-core"; +export interface WithdrawScriptBuilder { + script(scriptCbor: string): WithdrawRedeemerBuilder; + referenceScript( + txHash: string, + txIndex: number, + scriptSize?: string, + scriptHash?: string, + ): WithdrawRedeemerBuilder; +} + export interface WithdrawRedeemerBuilder { redeemerJson( redeemer: BuilderData["content"], exUnits?: Budget, - ): WithdrawScriptBuilder; + ): _MeshTxBuilderV2; redeemerCbor( redeemer: BuilderData["content"], exUnits?: Budget, - ): WithdrawScriptBuilder; -} -export interface WithdrawScriptBuilder { - script(scriptCbor: string): _MeshTxBuilderV2; - referenceScript( - txHash: string, - txIndex: number, - scriptSize?: string, - scriptHash?: string, ): _MeshTxBuilderV2; -} \ No newline at end of file +} diff --git a/packages/mesh-common/src/types/transaction-builder/index.ts b/packages/mesh-common/src/types/transaction-builder/index.ts index df79ae535..7735ede55 100644 --- a/packages/mesh-common/src/types/transaction-builder/index.ts +++ b/packages/mesh-common/src/types/transaction-builder/index.ts @@ -19,7 +19,6 @@ export * from "./withdrawal"; export * from "./certificate"; export * from "./vote"; export * from "./proposal"; -export * from "./tx-builder-v2"; export type MeshTxBuilderBody = { inputs: TxIn[]; diff --git a/packages/mesh-transaction/src/index.ts b/packages/mesh-transaction/src/index.ts index bb9ee5e08..abc7253ce 100644 --- a/packages/mesh-transaction/src/index.ts +++ b/packages/mesh-transaction/src/index.ts @@ -3,4 +3,5 @@ export * from "./scripts"; export * from "./transaction"; export * from "./utils"; export * from "./tx-parser"; +export * from "./mesh-tx-builder-v2"; export { LargestFirstInputSelector } from "./mesh-tx-builder/coin-selection"; diff --git a/packages/mesh-transaction/src/mesh-builder-v2/index.ts b/packages/mesh-transaction/src/mesh-tx-builder-v2/index.ts similarity index 63% rename from packages/mesh-transaction/src/mesh-builder-v2/index.ts rename to packages/mesh-transaction/src/mesh-tx-builder-v2/index.ts index d85fa92f9..6966b3861 100644 --- a/packages/mesh-transaction/src/mesh-builder-v2/index.ts +++ b/packages/mesh-transaction/src/mesh-tx-builder-v2/index.ts @@ -17,24 +17,42 @@ import type { WithdrawScriptBuilder, } from "@meshsdk/common"; import { Asset, Budget, DEFAULT_REDEEMER_BUDGET } from "@meshsdk/common"; -import { MeshTxBuilder, MeshTxBuilderOptions } from "@meshsdk/core"; - -import { MeshTxBuilderCore } from "../mesh-tx-builder/tx-builder-core"; +import { byteString, conStr0, MeshTxBuilder, MeshTxBuilderOptions } from "@meshsdk/core"; class SpendBuilderImpl implements - SpendRedeemerBuilder, SpendScriptBuilder, - SpendDatumBuilder, - SpendTxOutBuilder + SpendRedeemerBuilder, + SpendTxOutBuilder, + SpendDatumBuilder { - constructor(private readonly core: MeshTxBuilder) {} + constructor(private readonly builder: MeshTxBuilder & _MeshTxBuilderV2) {} + + script(scriptCbor: string): SpendRedeemerBuilder { + this.builder.txInScript(scriptCbor); + return this; + } + + referenceScript( + refTxHash: string, + refTxIndex: number, + scriptSize?: string, + scriptHash?: string, + ): SpendRedeemerBuilder { + this.builder.spendingTxInReference( + refTxHash, + refTxIndex, + scriptSize, + scriptHash, + ); + return this; + } redeemerJson( - redeemer: BuilderData["content"], + redeemer: BuilderData["content"],//check back exUnits?: Budget, - ): SpendScriptBuilder { - this.core.txInRedeemerValue( + ): SpendTxOutBuilder { + this.builder.txInRedeemerValue( redeemer, "JSON", exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, @@ -45,8 +63,8 @@ class SpendBuilderImpl redeemerCbor( redeemer: BuilderData["content"], exUnits?: Budget, - ): SpendScriptBuilder { - this.core.txInRedeemerValue( + ): SpendTxOutBuilder { + this.builder.txInRedeemerValue( redeemer, "CBOR", exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, @@ -54,58 +72,53 @@ class SpendBuilderImpl return this; } - script(scriptCbor: string): SpendDatumBuilder { - this.core.txInScript(scriptCbor); + txOut(address: string, amount: Asset[]): SpendDatumBuilder { + this.builder.txInInlineDatumPresent(); + this.builder.txOut(address, amount); return this; } - referenceScript( - refTxHash: string, - refTxIndex: number, - scriptSize?: string, - scriptHash?: string, - ): SpendDatumBuilder { - this.core.spendingTxInReference( - refTxHash, - refTxIndex, - scriptSize, - scriptHash, - ); - return this; + datumJson(datum: BuilderData["content"]): _MeshTxBuilderV2 { + this.builder.txOutInlineDatumValue(datum, "JSON"); + return this.builder as _MeshTxBuilderV2; } - datumJson(datum: BuilderData["content"]): SpendTxOutBuilder { - this.core.txOutInlineDatumValue(datum, "JSON"); - return this; + datumCbor(datum: BuilderData["content"]): _MeshTxBuilderV2 { + this.builder.txOutInlineDatumValue(datum, "CBOR"); + return this.builder as _MeshTxBuilderV2; } - datumCbor(datum: BuilderData["content"]): SpendTxOutBuilder { - this.core.txOutInlineDatumValue(datum, "CBOR"); - return this; + datumHash(datumHash: string): _MeshTxBuilderV2 { + this.builder.txOutDatumHashValue(datumHash); + return this.builder as _MeshTxBuilderV2; } +} - datumHash(datumHash: string): SpendTxOutBuilder { - this.core.txOutDatumHashValue(datumHash); +class MintBuilderImpl + implements MintScriptBuilder, MintRedeemerBuilder, MintTxOutBuilder +{ + constructor(private readonly builder: MeshTxBuilder & _MeshTxBuilderV2) {} + + script(scriptCbor: string): MintRedeemerBuilder { + this.builder.mintingScript(scriptCbor); return this; } - txOut(address: string, amount: Asset[]): _MeshTxBuilderV2 { - this.core.txInInlineDatumPresent(); - this.core.txOut(address, amount); - return this.core as unknown as _MeshTxBuilderV2; + referenceScript( + txHash: string, + txIndex: number, + scriptSize?: string, + scriptHash?: string, + ): MintRedeemerBuilder { + this.builder.mintTxInReference(txHash, txIndex, scriptSize, scriptHash); + return this; } -} - -class MintBuilderImpl - implements MintRedeemerBuilder, MintScriptBuilder, MintTxOutBuilder -{ - constructor(private readonly core: MeshTxBuilder) {} redeemerJson( redeemer: BuilderData["content"], exUnits?: Budget, - ): MintScriptBuilder { - this.core.mintRedeemerValue( + ): MintTxOutBuilder { + this.builder.mintRedeemerValue( redeemer, "JSON", exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, @@ -116,8 +129,8 @@ class MintBuilderImpl redeemerCbor( redeemer: BuilderData["content"], exUnits?: Budget, - ): MintScriptBuilder { - this.core.mintRedeemerValue( + ): MintTxOutBuilder { + this.builder.mintRedeemerValue( redeemer, "CBOR", exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, @@ -125,8 +138,19 @@ class MintBuilderImpl return this; } - script(scriptCbor: string): MintTxOutBuilder { - this.core.mintingScript(scriptCbor); + txOut(address: string, amount: Asset[]): _MeshTxBuilderV2 { + this.builder.txOut(address, amount); + return this.builder as _MeshTxBuilderV2; + } +} + +class WithdrawBuilderImpl + implements WithdrawScriptBuilder, WithdrawRedeemerBuilder +{ + constructor(private readonly builder: MeshTxBuilder & _MeshTxBuilderV2) {} + + script(scriptCbor: string): WithdrawRedeemerBuilder { + this.builder.withdrawalScript(scriptCbor); return this; } @@ -135,49 +159,42 @@ class MintBuilderImpl txIndex: number, scriptSize?: string, scriptHash?: string, - ): MintTxOutBuilder { - this.core.mintTxInReference(txHash, txIndex, scriptSize, scriptHash); + ): WithdrawRedeemerBuilder { + this.builder.withdrawalTxInReference(txHash, txIndex, scriptSize, scriptHash); return this; } - txOut(address: string, amount: Asset[]): _MeshTxBuilderV2 { - this.core.txOut(address, amount); - return this.core as unknown as _MeshTxBuilderV2; - } -} - -class WithdrawBuilderImpl - implements WithdrawRedeemerBuilder, WithdrawScriptBuilder -{ - constructor(private readonly core: MeshTxBuilder) {} - redeemerJson( redeemer: BuilderData["content"], exUnits?: Budget, - ): WithdrawScriptBuilder { - this.core.withdrawalRedeemerValue( + ): _MeshTxBuilderV2 { + this.builder.withdrawalRedeemerValue( redeemer, "JSON", exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, ); - return this; + return this.builder as _MeshTxBuilderV2; } redeemerCbor( redeemer: BuilderData["content"], exUnits?: Budget, - ): WithdrawScriptBuilder { - this.core.withdrawalRedeemerValue( + ): _MeshTxBuilderV2 { + this.builder.withdrawalRedeemerValue( redeemer, "CBOR", exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, ); - return this; + return this.builder as _MeshTxBuilderV2; } +} + +class VoteBuilderImpl implements VoteScriptBuilder, VoteRedeemerBuilder { + constructor(private readonly builder: MeshTxBuilder & _MeshTxBuilderV2) {} - script(scriptCbor: string): _MeshTxBuilderV2 { - this.core.withdrawalScript(scriptCbor); - return this.core as unknown as _MeshTxBuilderV2; + script(scriptCbor: string): VoteRedeemerBuilder { + this.builder.voteScript(scriptCbor); + return this; } referenceScript( @@ -185,67 +202,48 @@ class WithdrawBuilderImpl txIndex: number, scriptSize?: string, scriptHash?: string, - ): _MeshTxBuilderV2 { - this.core.withdrawalTxInReference(txHash, txIndex, scriptSize, scriptHash); - return this.core as unknown as _MeshTxBuilderV2; + ): VoteRedeemerBuilder { + this.builder.voteTxInReference(txHash, txIndex, scriptSize, scriptHash); + return this; } -} - -class VoteBuilderImpl implements VoteRedeemerBuilder, VoteScriptBuilder { - constructor(private readonly core: MeshTxBuilder) {} redeemerJson( redeemer: BuilderData["content"], exUnits?: Budget, - ): VoteScriptBuilder { - this.core.voteRedeemerValue( + ): _MeshTxBuilderV2 { + this.builder.voteRedeemerValue( redeemer, "JSON", exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, ); - return this; + return this.builder as _MeshTxBuilderV2; } redeemerCbor( redeemer: BuilderData["content"], exUnits?: Budget, - ): VoteScriptBuilder { - this.core.voteRedeemerValue( + ): _MeshTxBuilderV2 { + this.builder.voteRedeemerValue( redeemer, "CBOR", exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, ); - return this; - } - - script(scriptCbor: string): _MeshTxBuilderV2 { - this.core.voteScript(scriptCbor); - return this.core as unknown as _MeshTxBuilderV2; - } - - referenceScript( - txHash: string, - txIndex: number, - scriptSize?: string, - scriptHash?: string, - ): _MeshTxBuilderV2 { - this.core.voteTxInReference(txHash, txIndex, scriptSize, scriptHash); - return this.core as unknown as _MeshTxBuilderV2; + return this.builder as _MeshTxBuilderV2; } } -class __MeshTxBuilderV2 extends MeshTxBuilder { - spendPlutusV1(txHash: string, txIndex: number): SpendRedeemerBuilder { +class TxBuilderV2 extends MeshTxBuilder { + spendPlutusV1(txHash: string, txIndex: number): SpendScriptBuilder { this.spendingPlutusScriptV1(); this.txIn(txHash, txIndex); return new SpendBuilderImpl(this); } - spendPlutusV2(txHash: string, txIndex: number): SpendRedeemerBuilder { + spendPlutusV2(txHash: string, txIndex: number): SpendScriptBuilder { this.spendingPlutusScriptV2(); this.txIn(txHash, txIndex); return new SpendBuilderImpl(this); } - spendPlutusV3(txHash: string, txIndex: number): SpendRedeemerBuilder { + spendPlutusV3(txHash: string, txIndex: number): SpendScriptBuilder { this.spendingPlutusScriptV3(); this.txIn(txHash, txIndex); return new SpendBuilderImpl(this); @@ -255,7 +253,7 @@ class __MeshTxBuilderV2 extends MeshTxBuilder { quantity: string, policyId: string, assetName: string, - ): MintRedeemerBuilder { + ): MintScriptBuilder { this.mintPlutusScriptV1(); this.mint(quantity, policyId, assetName); return new MintBuilderImpl(this); @@ -264,7 +262,7 @@ class __MeshTxBuilderV2 extends MeshTxBuilder { quantity: string, policyId: string, assetName: string, - ): MintRedeemerBuilder { + ): MintScriptBuilder { this.mintPlutusScriptV2(); this.mint(quantity, policyId, assetName); return new MintBuilderImpl(this); @@ -273,7 +271,7 @@ class __MeshTxBuilderV2 extends MeshTxBuilder { quantity: string, policyId: string, assetName: string, - ): MintRedeemerBuilder { + ): MintScriptBuilder { this.mintPlutusScriptV3(); this.mint(quantity, policyId, assetName); return new MintBuilderImpl(this); @@ -281,16 +279,16 @@ class __MeshTxBuilderV2 extends MeshTxBuilder { withdrawPlutusV1( rewardAddress: string, - coin: string, - ): WithdrawRedeemerBuilder { + amount: string, + ): WithdrawScriptBuilder { this.withdrawalPlutusScriptV1(); - this.withdrawal(rewardAddress, coin); + this.withdrawal(rewardAddress, amount); return new WithdrawBuilderImpl(this); } withdrawPlutusV2( rewardAddress: string, coin: string, - ): WithdrawRedeemerBuilder { + ): WithdrawScriptBuilder { this.withdrawalPlutusScriptV2(); this.withdrawal(rewardAddress, coin); return new WithdrawBuilderImpl(this); @@ -298,7 +296,7 @@ class __MeshTxBuilderV2 extends MeshTxBuilder { withdrawPlutusV3( rewardAddress: string, coin: string, - ): WithdrawRedeemerBuilder { + ): WithdrawScriptBuilder { this.withdrawalPlutusScriptV3(); this.withdrawal(rewardAddress, coin); return new WithdrawBuilderImpl(this); @@ -308,7 +306,7 @@ class __MeshTxBuilderV2 extends MeshTxBuilder { voter: Voter, govActionId: RefTxIn, votingProcedure: VotingProcedure, - ): VoteRedeemerBuilder { + ): VoteScriptBuilder { this.votePlutusScriptV1(); this.vote(voter, govActionId, votingProcedure); return new VoteBuilderImpl(this); @@ -317,7 +315,7 @@ class __MeshTxBuilderV2 extends MeshTxBuilder { voter: Voter, govActionId: RefTxIn, votingProcedure: VotingProcedure, - ): VoteRedeemerBuilder { + ): VoteScriptBuilder { this.votePlutusScriptV2(); this.vote(voter, govActionId, votingProcedure); return new VoteBuilderImpl(this); @@ -326,7 +324,7 @@ class __MeshTxBuilderV2 extends MeshTxBuilder { voter: Voter, govActionId: RefTxIn, votingProcedure: VotingProcedure, - ): VoteRedeemerBuilder { + ): VoteScriptBuilder { this.votePlutusScriptV3(); this.vote(voter, govActionId, votingProcedure); return new VoteBuilderImpl(this); @@ -334,10 +332,10 @@ class __MeshTxBuilderV2 extends MeshTxBuilder { } /** - * `MeshTxBuilderV2` is a strongly-typed wrapper around the core `MeshTxBuilder` + * \`MeshTxBuilderV2\` is a strongly-typed wrapper around the core \`MeshTxBuilder\` * that enforces a rigid Type-State machine for Plutus operations. * - * It natively provides TypeScript autocomplete restrictions (Script -> Datum -> Redeemer) + * It natively provides TypeScript autocomplete restrictions (Script -> Redeemer -> TxOut -> Datum) * for transactions like spending, minting, withdrawing, and voting, dropping standalone * script methods to prevent incorrect chained execution ordering. * @@ -350,11 +348,12 @@ class __MeshTxBuilderV2 extends MeshTxBuilder { * * tx.spendPlutusV3(txHash, index) * .script(scriptCbor) - * .datumJson(datumValue) * .redeemerJson(redeemerValue) - * .txOut(address, assets); // Drops you back into the main builder methods + * .txOut(address, assets) + * .datumJson(datumValue); // Drops you back into the main builder methods * ``` */ + export const MeshTxBuilderV2: new ( options?: MeshTxBuilderOptions, -) => _MeshTxBuilderV2 = __MeshTxBuilderV2; +) => _MeshTxBuilderV2 = TxBuilderV2; \ No newline at end of file diff --git a/packages/mesh-transaction/src/mesh-tx-builder/index.ts b/packages/mesh-transaction/src/mesh-tx-builder/index.ts index 508a0e7fb..b6c6339f5 100644 --- a/packages/mesh-transaction/src/mesh-tx-builder/index.ts +++ b/packages/mesh-transaction/src/mesh-tx-builder/index.ts @@ -1878,4 +1878,3 @@ export const getOutputMinLovelace = ( }; export * from "./utils"; -export { MeshTxBuilderV2 } from "../mesh-builder-v2"; diff --git a/packages/mesh-transaction/test/mesh-tx-builder-v2.test.ts b/packages/mesh-transaction/test/mesh-tx-builder-v2.test.ts index 422ce67ca..f8caae1f7 100644 --- a/packages/mesh-transaction/test/mesh-tx-builder-v2.test.ts +++ b/packages/mesh-transaction/test/mesh-tx-builder-v2.test.ts @@ -2,6 +2,7 @@ import { Asset, Budget, LanguageVersion } from "@meshsdk/common"; import { MeshTxBuilderV2 } from "../src"; + describe("MeshTxBuilderV2 Grouped interfaces", () => { it("should build a tx with spendPlutusV2", () => { const tx = new MeshTxBuilderV2(); @@ -9,13 +10,13 @@ describe("MeshTxBuilderV2 Grouped interfaces", () => { "0000000000000000000000000000000000000000000000000000000000000000"; tx.spendPlutusV2(mockHash, 0) - .redeemerJson({ action: "spend" }) .script("112233") - .datumJson({ data: "foo" }) + .redeemerJson({ action: "spend" }) .txOut( "addr_test1vzuwvztxzv3j4a2wvy7xntry2a8y869svj4a7y7qy2wvywqzcqxny", - [], - ); + [] + ) + .datumJson({ data: "foo" }); // @ts-ignore tx.queueAllLastItem(); @@ -54,8 +55,8 @@ describe("MeshTxBuilderV2 Grouped interfaces", () => { it("should have correct mintPlutus interface", () => { const tx = new MeshTxBuilderV2(); tx.mintPlutusV2("1", "policyId123", "assetName456") - .redeemerCbor("998877") .script("556677") + .redeemerCbor("998877") .txOut( "addr_test1vzuwvztxzv3j4a2wvy7xntry2a8y869svj4a7y7qy2wvywqzcqxny", [], @@ -83,8 +84,8 @@ describe("MeshTxBuilderV2 Grouped interfaces", () => { it("should build a tx with withdrawPlutusV2", () => { const tx = new MeshTxBuilderV2(); tx.withdrawPlutusV2("stake_test1uqevw2xnsc0pvn9t9r9c7qryfqfeerchgrlm3ea2nefr9hqp8n5xl", "1000000") - .redeemerJson({ action: "withdraw" }) - .script("445566"); + .script("445566") + .redeemerJson({ action: "withdraw" }); // @ts-ignore tx.queueAllLastItem(); @@ -113,8 +114,8 @@ describe("MeshTxBuilderV2 Grouped interfaces", () => { { txHash: "0000000000000000000000000000000000000000000000000000000000000000", txIndex: 0 }, { voteKind: "Yes" } ) - .redeemerJson({ action: "vote" }) - .script("111111"); + .script("111111") + .redeemerJson({ action: "vote" }); // @ts-ignore tx.queueAllLastItem();