diff --git a/packages/mesh-common/src/data/json/aliases.ts b/packages/mesh-common/src/data/json/aliases.ts index e38ebded8..cc71225bf 100644 --- a/packages/mesh-common/src/data/json/aliases.ts +++ b/packages/mesh-common/src/data/json/aliases.ts @@ -88,13 +88,13 @@ export type Tuple = { list: T }; * Aiken alias * The Plutus Data Option in JSON */ -export type Option = Some | None; +export type Option = Some | None; /** * Aiken alias * The Plutus Data Option - Some in JSON */ -export type Some = ConStr0<[T]>; +export type Some = ConStr0<[T]>; /** * Aiken alias @@ -246,7 +246,7 @@ export const tuple = (...args: T): Tuple => ({ * @param value The optional value of the option * @returns Return None constructor if the value is not provided, otherwise return Some constructor with the value */ -export const option = (value?: T): Option => { +export const option = (value?: T): Option => { if (!value) { return none(); } @@ -258,7 +258,7 @@ export const option = (value?: T): Option => { * @param value The value of the option * @returns The Plutus Data Option - Some object */ -export const some = (value: T): Some => conStr0([value]); +export const some = (value: T): Some => conStr0([value]); /** * The utility function to create a Plutus Data Option - None in JSON diff --git a/packages/mesh-common/src/data/json/constructors.ts b/packages/mesh-common/src/data/json/constructors.ts index 02743be99..4a1fc3146 100644 --- a/packages/mesh-common/src/data/json/constructors.ts +++ b/packages/mesh-common/src/data/json/constructors.ts @@ -1,27 +1,43 @@ +import { PlutusData } from "."; + /** * The Plutus Data constructor object, representing custom data type in JSON */ -export type ConStr = { constructor: N; fields: T }; - +export interface ConStr { + constructor: N; + fields: T; +} /** * The Plutus Data index 0 constructor object, representing custom data type in JSON */ -export type ConStr0 = ConStr<0, T>; +export interface ConStr0 extends ConStr< + 0, + T +> {} /** * The Plutus Data index 1 constructor object, representing custom data type in JSON */ -export type ConStr1 = ConStr<1, T>; +export interface ConStr1 extends ConStr< + 1, + T +> {} /** * The Plutus Data index 2 constructor object, representing custom data type in JSON */ -export type ConStr2 = ConStr<2, T>; +export interface ConStr2 extends ConStr< + 2, + T +> {} /** * The Plutus Data index 3 constructor object, representing custom data type in JSON */ -export type ConStr3 = ConStr<3, T>; +export interface ConStr3 extends ConStr< + 3, + T +> {} /** * The utility function to create a Plutus Data constructor object, representing custom data type in JSON @@ -29,17 +45,14 @@ export type ConStr3 = ConStr<3, T>; * @param fields The items in array * @returns The Plutus Data constructor object */ -export const conStr = ( +export const conStr = ( constructor: N, fields: T, ): ConStr => { if (!Array.isArray(fields)) { throw new Error("fields of a constructor must be an array"); } - return { - constructor, - fields, - }; + return { constructor, fields }; }; /** @@ -47,25 +60,29 @@ export const conStr = ( * @param fields The items of in array * @returns The Plutus Data constructor object */ -export const conStr0 = (fields: T): ConStr0 => conStr<0, T>(0, fields); +export const conStr0 = (fields: T): ConStr0 => + conStr<0, T>(0, fields); /** * The utility function to create a Plutus Data index 1 constructor object, representing custom data type in JSON * @param fields The items of in array * @returns The Plutus Data constructor object */ -export const conStr1 = (fields: T): ConStr1 => conStr<1, T>(1, fields); +export const conStr1 = (fields: T): ConStr1 => + conStr<1, T>(1, fields); /** * The utility function to create a Plutus Data index 2 constructor object, representing custom data type in JSON * @param fields The items of in array * @returns The Plutus Data constructor object */ -export const conStr2 = (fields: T): ConStr2 => conStr<2, T>(2, fields); +export const conStr2 = (fields: T): ConStr2 => + conStr<2, T>(2, fields); /** * The utility function to create a Plutus Data index 3 constructor object, representing custom data type in JSON * @param fields The items of in array * @returns The Plutus Data constructor object */ -export const conStr3 = (fields: T): ConStr3 => conStr<3, T>(3, fields); +export const conStr3 = (fields: T): ConStr3 => + conStr<3, T>(3, fields); diff --git a/packages/mesh-common/src/types/transaction-builder/index.ts b/packages/mesh-common/src/types/transaction-builder/index.ts index b03e62d93..7735ede55 100644 --- a/packages/mesh-common/src/types/transaction-builder/index.ts +++ b/packages/mesh-common/src/types/transaction-builder/index.ts @@ -35,7 +35,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/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-tx-builder-v2/index.ts b/packages/mesh-transaction/src/mesh-tx-builder-v2/index.ts new file mode 100644 index 000000000..ab26b2de0 --- /dev/null +++ b/packages/mesh-transaction/src/mesh-tx-builder-v2/index.ts @@ -0,0 +1,363 @@ +import type { + _MeshTxBuilderV2, + MintRedeemerBuilder, + MintScriptBuilder, + MintTxOutBuilder, + SpendDatumBuilder, + SpendRedeemerBuilder, + SpendScriptBuilder, + SpendTxOutBuilder, + VoteRedeemerBuilder, + VoteScriptBuilder, + WithdrawRedeemerBuilder, + WithdrawScriptBuilder, +} from "./types"; +import { + Asset, + Budget, + BuilderData, + DEFAULT_REDEEMER_BUDGET, + RefTxIn, + Voter, + VotingProcedure, +} from "@meshsdk/common"; +import { MeshTxBuilder, MeshTxBuilderOptions } from "../mesh-tx-builder"; + +class SpendBuilderImpl + implements + SpendScriptBuilder, + SpendRedeemerBuilder, + SpendTxOutBuilder, + SpendDatumBuilder +{ + 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"],//check back + exUnits?: Budget, + ): SpendTxOutBuilder { + this.builder.txInRedeemerValue( + redeemer, + "JSON", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this; + } + + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): SpendTxOutBuilder { + this.builder.txInRedeemerValue( + redeemer, + "CBOR", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this; + } + + txOut(address: string, amount: Asset[]): SpendDatumBuilder { + this.builder.txInInlineDatumPresent(); + this.builder.txOut(address, amount); + return this; + } + + datumJson(datum: BuilderData["content"]): _MeshTxBuilderV2 { + this.builder.txOutInlineDatumValue(datum, "JSON"); + return this.builder as _MeshTxBuilderV2; + } + + datumCbor(datum: BuilderData["content"]): _MeshTxBuilderV2 { + this.builder.txOutInlineDatumValue(datum, "CBOR"); + return this.builder as _MeshTxBuilderV2; + } + + datumHash(datumHash: string): _MeshTxBuilderV2 { + this.builder.txOutDatumHashValue(datumHash); + return this.builder as _MeshTxBuilderV2; + } +} + +class MintBuilderImpl + implements MintScriptBuilder, MintRedeemerBuilder, MintTxOutBuilder +{ + constructor(private readonly builder: MeshTxBuilder & _MeshTxBuilderV2) {} + + script(scriptCbor: string): MintRedeemerBuilder { + this.builder.mintingScript(scriptCbor); + return this; + } + + referenceScript( + txHash: string, + txIndex: number, + scriptSize?: string, + scriptHash?: string, + ): MintRedeemerBuilder { + this.builder.mintTxInReference(txHash, txIndex, scriptSize, scriptHash); + return this; + } + + redeemerJson( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): MintTxOutBuilder { + this.builder.mintRedeemerValue( + redeemer, + "JSON", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this; + } + + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): MintTxOutBuilder { + this.builder.mintRedeemerValue( + redeemer, + "CBOR", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this; + } + + 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; + } + + referenceScript( + txHash: string, + txIndex: number, + scriptSize?: string, + scriptHash?: string, + ): WithdrawRedeemerBuilder { + this.builder.withdrawalTxInReference(txHash, txIndex, scriptSize, scriptHash); + return this; + } + + redeemerJson( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): _MeshTxBuilderV2 { + this.builder.withdrawalRedeemerValue( + redeemer, + "JSON", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this.builder as _MeshTxBuilderV2; + } + + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): _MeshTxBuilderV2 { + this.builder.withdrawalRedeemerValue( + redeemer, + "CBOR", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this.builder as _MeshTxBuilderV2; + } +} + +class VoteBuilderImpl implements VoteScriptBuilder, VoteRedeemerBuilder { + constructor(private readonly builder: MeshTxBuilder & _MeshTxBuilderV2) {} + + script(scriptCbor: string): VoteRedeemerBuilder { + this.builder.voteScript(scriptCbor); + return this; + } + + referenceScript( + txHash: string, + txIndex: number, + scriptSize?: string, + scriptHash?: string, + ): VoteRedeemerBuilder { + this.builder.voteTxInReference(txHash, txIndex, scriptSize, scriptHash); + return this; + } + + redeemerJson( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): _MeshTxBuilderV2 { + this.builder.voteRedeemerValue( + redeemer, + "JSON", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this.builder as _MeshTxBuilderV2; + } + + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): _MeshTxBuilderV2 { + this.builder.voteRedeemerValue( + redeemer, + "CBOR", + exUnits ?? { ...DEFAULT_REDEEMER_BUDGET }, + ); + return this.builder as _MeshTxBuilderV2; + } +} + +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): SpendScriptBuilder { + this.spendingPlutusScriptV2(); + this.txIn(txHash, txIndex); + return new SpendBuilderImpl(this); + } + spendPlutusV3(txHash: string, txIndex: number): SpendScriptBuilder { + this.spendingPlutusScriptV3(); + this.txIn(txHash, txIndex); + return new SpendBuilderImpl(this); + } + + mintPlutusV1( + quantity: string, + policyId: string, + assetName: string, + ): MintScriptBuilder { + this.mintPlutusScriptV1(); + this.mint(quantity, policyId, assetName); + return new MintBuilderImpl(this); + } + mintPlutusV2( + quantity: string, + policyId: string, + assetName: string, + ): MintScriptBuilder { + this.mintPlutusScriptV2(); + this.mint(quantity, policyId, assetName); + return new MintBuilderImpl(this); + } + mintPlutusV3( + quantity: string, + policyId: string, + assetName: string, + ): MintScriptBuilder { + this.mintPlutusScriptV3(); + this.mint(quantity, policyId, assetName); + return new MintBuilderImpl(this); + } + + withdrawPlutusV1( + rewardAddress: string, + amount: string, + ): WithdrawScriptBuilder { + this.withdrawalPlutusScriptV1(); + this.withdrawal(rewardAddress, amount); + return new WithdrawBuilderImpl(this); + } + withdrawPlutusV2( + rewardAddress: string, + coin: string, + ): WithdrawScriptBuilder { + this.withdrawalPlutusScriptV2(); + this.withdrawal(rewardAddress, coin); + return new WithdrawBuilderImpl(this); + } + withdrawPlutusV3( + rewardAddress: string, + coin: string, + ): WithdrawScriptBuilder { + this.withdrawalPlutusScriptV3(); + this.withdrawal(rewardAddress, coin); + return new WithdrawBuilderImpl(this); + } + + votePlutusV1( + voter: Voter, + govActionId: RefTxIn, + votingProcedure: VotingProcedure, + ): VoteScriptBuilder { + this.votePlutusScriptV1(); + this.vote(voter, govActionId, votingProcedure); + return new VoteBuilderImpl(this); + } + votePlutusV2( + voter: Voter, + govActionId: RefTxIn, + votingProcedure: VotingProcedure, + ): VoteScriptBuilder { + this.votePlutusScriptV2(); + this.vote(voter, govActionId, votingProcedure); + return new VoteBuilderImpl(this); + } + votePlutusV3( + voter: Voter, + govActionId: RefTxIn, + votingProcedure: VotingProcedure, + ): VoteScriptBuilder { + 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 -> Redeemer -> TxOut -> Datum) + * 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) + * .redeemerJson(redeemerValue) + * .txOut(address, assets) + * .datumJson(datumValue); // Drops you back into the main builder methods + * ``` + */ + +export const MeshTxBuilderV2: new ( + options?: MeshTxBuilderOptions, +) => _MeshTxBuilderV2 = TxBuilderV2; diff --git a/packages/mesh-transaction/src/mesh-tx-builder-v2/types.ts b/packages/mesh-transaction/src/mesh-tx-builder-v2/types.ts new file mode 100644 index 000000000..bab8d5825 --- /dev/null +++ b/packages/mesh-transaction/src/mesh-tx-builder-v2/types.ts @@ -0,0 +1,200 @@ +import { Asset, Budget, BuilderData, RefTxIn, Voter, VotingProcedure } from "@meshsdk/common"; + +import { MeshTxBuilder } from "../mesh-tx-builder"; + +// ── Core V2 Builder Interface ────────────────────────────────────────── + +export interface _MeshTxBuilderV2 + extends Omit { + 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, + ): MintScriptBuilder; + mintPlutusV2( + quantity: string, + policyId: string, + assetName: string, + ): MintScriptBuilder; + mintPlutusV3( + quantity: string, + policyId: string, + assetName: string, + ): MintScriptBuilder; + + withdrawPlutusV1( + rewardAddress: string, + amount: string, + ): WithdrawScriptBuilder; + withdrawPlutusV2( + rewardAddress: string, + amount: string, + ): WithdrawScriptBuilder; + withdrawPlutusV3( + rewardAddress: string, + amount: string, + ): WithdrawScriptBuilder; + + votePlutusV1( + voter: Voter, + govActionId: RefTxIn, + votingProcedure: VotingProcedure, + ): VoteScriptBuilder; + votePlutusV2( + voter: Voter, + govActionId: RefTxIn, + votingProcedure: VotingProcedure, + ): VoteScriptBuilder; + votePlutusV3( + voter: Voter, + govActionId: RefTxIn, + votingProcedure: VotingProcedure, + ): VoteScriptBuilder; +} + +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"; + +// ── Spend Builder Types ──────────────────────────────────────────────── + +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, + ): SpendTxOutBuilder; + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): SpendTxOutBuilder; +} + +export interface SpendTxOutBuilder { + txOut(address: string, amount: Asset[]): SpendDatumBuilder; +} + +export interface SpendDatumBuilder { + datumHash(datumHash: string): _MeshTxBuilderV2; + datumCbor(datumCbor: BuilderData["content"]): _MeshTxBuilderV2; + datumJson(datumJson: BuilderData["content"]): _MeshTxBuilderV2; +} + +// ── Mint Builder Types ───────────────────────────────────────────────── + +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, + ): MintTxOutBuilder; + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): MintTxOutBuilder; +} + +export interface MintTxOutBuilder { + txOut(address: string, amount: Asset[]): _MeshTxBuilderV2; +} + +// ── Vote Builder Types ───────────────────────────────────────────────── + +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, + ): _MeshTxBuilderV2; + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): _MeshTxBuilderV2; +} + +// ── Withdrawal Builder Types ─────────────────────────────────────────── + +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, + ): _MeshTxBuilderV2; + redeemerCbor( + redeemer: BuilderData["content"], + exUnits?: Budget, + ): _MeshTxBuilderV2; +} 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..f8caae1f7 --- /dev/null +++ b/packages/mesh-transaction/test/mesh-tx-builder-v2.test.ts @@ -0,0 +1,139 @@ +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) + .script("112233") + .redeemerJson({ action: "spend" }) + .txOut( + "addr_test1vzuwvztxzv3j4a2wvy7xntry2a8y869svj4a7y7qy2wvywqzcqxny", + [] + ) + .datumJson({ data: "foo" }); + + // @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") + .script("556677") + .redeemerCbor("998877") + .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") + .script("445566") + .redeemerJson({ action: "withdraw" }); + + // @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" } + ) + .script("111111") + .redeemerJson({ action: "vote" }); + + // @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"); + } + }); +});