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
3 changes: 3 additions & 0 deletions apps/playground/src/pages/apis/txbuilder/basics/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import TxbuilderCustomPP from "./custom-pp";
import TxbuilderInitializeTxbuilder from "./initialize-txbuilder";
import TxbuilderMultisig from "./multisig";
import TxbuilderMultisigNativeScript from "./multisig-native-script";
import TxbuilderScriptMetadata from "./script-metadata";
import TxbuilderSendValues from "./send-values";
import TxbuilderSetFee from "./set-fee";
import TxbuilderSetNetwork from "./set-network";
Expand All @@ -29,6 +30,7 @@ const ReactPage: NextPage = () => {
{ label: "Build with object", to: "buildWithObject" },
{ label: "Coin selection", to: "coinSelection" },
{ label: "Set metadata", to: "cip20" },
{ label: "Set script metadata ", to: "scriptMetadata" },
{ label: "Set required signers", to: "requiredSigners" },
{ label: "Set time", to: "setTime" },
{ label: "Set network", to: "setNetwork" },
Expand Down Expand Up @@ -79,6 +81,7 @@ const ReactPage: NextPage = () => {
<TxbuilderBuildWithObject />
<TxbuilderCoinSelection />
<TxbuilderCip20 />
<TxbuilderScriptMetadata />
<TxbuilderSetRequiredSigners />
<TxbuilderSetTime />
<TxbuilderSetNetwork />
Expand Down
115 changes: 115 additions & 0 deletions apps/playground/src/pages/apis/txbuilder/basics/script-metadata.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { useState } from "react";

import { useWallet } from "@meshsdk/react";

import Textarea from "~/components/form/textarea";
import Select from "~/components/form/select";
import InputTable from "~/components/sections/input-table";
import LiveCodeDemo from "~/components/sections/live-code-demo";
import TwoColumnsScroll from "~/components/sections/two-columns-scroll";
import Codeblock from "~/components/text/codeblock";
import { getTxBuilder, txbuilderCode } from "../common";

export default function TxbuilderScriptMetadata() {
return (
<TwoColumnsScroll
sidebarTo="scriptMetadata"
title="Set Script Metadata"
leftSection={Left()}
rightSection={Right()}
/>
);
}

function Left() {
return (
<>
<p>
Add a metadata script to the transaction using <code>.metadataScript()</code>. This demo shows how to attach a CBOR-encoded script along with its type to a transaction.
The accepted script types are: <code>Native</code>, <code>PlutusV1</code>, <code>PlutusV2</code>, and <code>PlutusV3</code>.
</p>
<Codeblock
data={`txBuilder\n .metadataScript(scriptCbor, scriptType)\n`}
/>
</>
);
}

function Right() {
const { wallet, connected } = useWallet();

const [scriptCbor, setScriptCbor] = useState<string>("830302828200581c4fa1dd19be215b14a30f2a73f8b29e25bc917fbb2b3325b18394dca78200581c546a29981d02d06ea800e9bb9da1a9d8fc0e251a52a683f55bd7aa8f");
const [scriptType, setScriptType] = useState<"PlutusV1" | "PlutusV2" | "PlutusV3" | "Native">("Native");

async function runDemo() {
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const txBuilder = getTxBuilder();

const unsignedTx = await txBuilder
.changeAddress(changeAddress)
.metadataScript(scriptCbor, scriptType)
.selectUtxosFrom(utxos)
.complete();

const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);

return txHash;
}

let codeSnippet = ``;
codeSnippet += `const utxos = await wallet.getUtxos();\n`;
codeSnippet += `const changeAddress = await wallet.getChangeAddress();\n\n`;
codeSnippet += `const scriptCbor = "${scriptCbor}";\n`;
codeSnippet += `const scriptType = "${scriptType}";\n\n`;
codeSnippet += txbuilderCode;
codeSnippet += `const unsignedTx = await txBuilder\n`;
codeSnippet += ` .changeAddress(changeAddress)\n`;
codeSnippet += ` .metadataScript(scriptCbor, scriptType)\n`;
codeSnippet += ` .selectUtxosFrom(utxos)\n`;
codeSnippet += ` .complete();\n`;
codeSnippet += `\n`;
codeSnippet += `const signedTx = await wallet.signTx(unsignedTx);\n`;
codeSnippet += `const txHash = await wallet.submitTx(signedTx);\n`;

return (
<LiveCodeDemo
title="Add Script"
subtitle="Add a script to a transaction's metadata."
code={codeSnippet}
runCodeFunction={runDemo}
disabled={!connected}
runDemoButtonTooltip={
!connected ? "Connect wallet to run this demo" : undefined
}
runDemoShowBrowseWalletConnect={true}
>
<InputTable
listInputs={[
<Textarea
value={scriptCbor}
onChange={(e) => setScriptCbor(e.target.value)}
label="Script CBOR"
key="script-cbor"
/>,
<Select
key="script-type"
id="scriptType"
label="Script Type"
value={scriptType}
onChange={(e) =>
setScriptType(e.target.value as "PlutusV1" | "PlutusV2" | "PlutusV3" | "Native")
}
options={{
PlutusV1: "PlutusV1",
PlutusV2: "PlutusV2",
PlutusV3: "PlutusV3",
Native: "Native",
}}
/>,
]}
/>
</LiveCodeDemo>
);
}
6 changes: 6 additions & 0 deletions packages/mesh-common/src/types/transaction-builder/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type MeshTxBuilderBody = {
mints: MintParam[];
changeAddress: string;
metadata: TxMetadata;
scriptMetadata: ScriptMetadata[];
validityRange: ValidityRange;
certificates: Certificate[];
withdrawals: Withdrawal[];
Expand All @@ -54,6 +55,7 @@ export const emptyTxBuilderBody = (): MeshTxBuilderBody => ({
mints: [],
changeAddress: "",
metadata: new Map<bigint, Metadatum>(),
scriptMetadata: [],
validityRange: {},
certificates: [],
withdrawals: [],
Expand Down Expand Up @@ -100,6 +102,10 @@ export type Metadata = {
metadata: string;
};

export type ScriptMetadata = {
scriptType: "PlutusV1" | "PlutusV2" | "PlutusV3" | "Native";
scriptCbor: string;
};
// Utilities

export type RequiredWith<T, K extends keyof T> = Required<T> & {
Expand Down
46 changes: 46 additions & 0 deletions packages/mesh-core-cst/src/serializer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
PubKeyTxIn,
RefTxIn,
RequiredWith,
ScriptMetadata,
ScriptSource,
ScriptTxIn,
ScriptVote,
Expand Down Expand Up @@ -606,6 +607,7 @@ class CardanoSDKSerializerCore {
referenceInputs,
mints,
metadata,
scriptMetadata,
validityRange,
certificates,
withdrawals,
Expand Down Expand Up @@ -702,6 +704,11 @@ class CardanoSDKSerializerCore {
throwErrorWithOrigin("Error serializing metadata", e);
}
}
try {
this.addScriptMetadata(scriptMetadata);
} catch (e) {
throwErrorWithOrigin("Error serializing script metadata", e);
}

return this.txBody;
};
Expand Down Expand Up @@ -1385,6 +1392,45 @@ class CardanoSDKSerializerCore {
);
};

private addScriptMetadata = (scripts: ScriptMetadata[]) => {
let nativeScriptArray = [];
let plutusV1ScriptArray = [];
let plutusV2ScriptArray = [];
let plutusV3ScriptArray = [];
for (let script of scripts) {
switch (script.scriptType) {
case "Native": {
nativeScriptArray.push(
NativeScript.fromCbor(HexBlob(script.scriptCbor)),
);
break;
}
case "PlutusV1": {
plutusV1ScriptArray.push(
PlutusV1Script.fromCbor(HexBlob(script.scriptCbor)),
);
break;
}
case "PlutusV2": {
plutusV2ScriptArray.push(
PlutusV2Script.fromCbor(HexBlob(script.scriptCbor)),
);
break;
}
case "PlutusV3": {
plutusV3ScriptArray.push(
PlutusV3Script.fromCbor(HexBlob(script.scriptCbor)),
);
break;
}
}
}
this.txAuxilliaryData.setNativeScripts(nativeScriptArray);
this.txAuxilliaryData.setPlutusV1Scripts(plutusV1ScriptArray);
this.txAuxilliaryData.setPlutusV2Scripts(plutusV2ScriptArray);
this.txAuxilliaryData.setPlutusV3Scripts(plutusV3ScriptArray);
};

private createMockedWitnessSet = (
requiredSignaturesCount: number,
requiredByronSignatures: string[],
Expand Down
1 change: 1 addition & 0 deletions packages/mesh-core-cst/test/sanitize-outputs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe("Sanitize outputs", () => {
changeAddress:
"addr_test1qpvx0sacufuypa2k4sngk7q40zc5c4npl337uusdh64kv0uafhxhu32dys6pvn6wlw8dav6cmp4pmtv7cc3yel9uu0nq93swx9",
metadata: new Map(),
scriptMetadata: [],
validityRange: {},
certificates: [],
withdrawals: [],
Expand Down
1 change: 1 addition & 0 deletions packages/mesh-core-cst/test/unbalanced.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe("Unbalanced", () => {
changeAddress:
"addr_test1qpvx0sacufuypa2k4sngk7q40zc5c4npl337uusdh64kv0uafhxhu32dys6pvn6wlw8dav6cmp4pmtv7cc3yel9uu0nq93swx9",
metadata: new Map(),
scriptMetadata: [],
validityRange: {},
certificates: [],
withdrawals: [],
Expand Down
1 change: 1 addition & 0 deletions packages/mesh-core-cst/test/utils/serializer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ describe("Build transaction with custom protocol params", () => {
changeAddress:
"addr_test1qra9zdhfa8kteyr3mfe7adkf5nlh8jl5xcg9e7pcp5w9yhyf5tek6vpnha97yd5yw9pezm3wyd77fyrfs3ynftyg7njs5cfz2x",
metadata: new Map(),
scriptMetadata: [],
validityRange: {},
certificates: [],
withdrawals: [],
Expand Down
17 changes: 17 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 @@ -1440,6 +1440,23 @@ export class MeshTxBuilderCore {
return this;
};

/**
* Add metadata script to the transaction
* @param scriptCbor The script in CBOR format
* @param scriptType The type of the script, either "Native", "PlutusV1", "PlutusV2" or "PlutusV3"
* @returns The MeshTxBuilder instance
*/
metadataScript = (
scriptCbor: string,
scriptType: "Native" | "PlutusV1" | "PlutusV2" | "PlutusV3",
) => {
this.meshTxBuilderBody.scriptMetadata.push({
scriptCbor,
scriptType,
});
return this;
};

/**
* Sign the transaction with the private key
* @param skeyHex The private key in cborHex (with or without 5820 prefix, i.e. the format when generated from cardano-cli)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
MeshTxBuilder,
NativeScript,
resolveNativeScriptHex,
} from "@meshsdk/core";
import { Transaction, TxCBOR } from "@meshsdk/core-cst";

import { alwaysSucceedCbor } from "../test-util";

describe("MeshTxBuilder Script Metadata", () => {
it("should create a transaction with plutusV3 metadata", async () => {
const txBuilder = new MeshTxBuilder();

let txHex = txBuilder
.txIn(
"2cb57168ee66b68bd04a0d595060b546edf30c04ae1031b883c9ac797967dd85",
3,
[{ unit: "lovelace", quantity: "9891607895" }],
"addr_test1vru4e2un2tq50q4rv6qzk7t8w34gjdtw3y2uzuqxzj0ldrqqactxh",
)
.txOut(
"addr_test1vru4e2un2tq50q4rv6qzk7t8w34gjdtw3y2uzuqxzj0ldrqqactxh",
[{ unit: "lovelace", quantity: "2000000" }],
)
.changeAddress(
"addr_test1vru4e2un2tq50q4rv6qzk7t8w34gjdtw3y2uzuqxzj0ldrqqactxh",
)
.metadataScript(alwaysSucceedCbor, "PlutusV3")
.completeSync();

const cardanoTx = Transaction.fromCbor(TxCBOR(txHex));
expect(cardanoTx.auxiliaryData()?.plutusV3Scripts).toBeDefined();
});

it("should create a transaction with native script metadata", async () => {
const txBuilder = new MeshTxBuilder();
const nativeScript: NativeScript = {
type: "all",
scripts: [],
};
let txHex = txBuilder
.txIn(
"2cb57168ee66b68bd04a0d595060b546edf30c04ae1031b883c9ac797967dd85",
3,
[{ unit: "lovelace", quantity: "9891607895" }],
"addr_test1vru4e2un2tq50q4rv6qzk7t8w34gjdtw3y2uzuqxzj0ldrqqactxh",
)
.txOut(
"addr_test1vru4e2un2tq50q4rv6qzk7t8w34gjdtw3y2uzuqxzj0ldrqqactxh",
[{ unit: "lovelace", quantity: "2000000" }],
)
.changeAddress(
"addr_test1vru4e2un2tq50q4rv6qzk7t8w34gjdtw3y2uzuqxzj0ldrqqactxh",
)
.metadataScript(resolveNativeScriptHex(nativeScript), "Native")
.completeSync();

const cardanoTx = Transaction.fromCbor(TxCBOR(txHex));
expect(cardanoTx.auxiliaryData()?.nativeScripts).toBeDefined();
});
});