Skip to content

Commit f114b2c

Browse files
authored
Merge pull request #655 from MeshJS/feature-update/script-metadata
Feature update/script metadata
2 parents f3a7605 + 20febca commit f114b2c

File tree

9 files changed

+251
-0
lines changed

9 files changed

+251
-0
lines changed

apps/playground/src/pages/apis/txbuilder/basics/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import TxbuilderCustomPP from "./custom-pp";
1414
import TxbuilderInitializeTxbuilder from "./initialize-txbuilder";
1515
import TxbuilderMultisig from "./multisig";
1616
import TxbuilderMultisigNativeScript from "./multisig-native-script";
17+
import TxbuilderScriptMetadata from "./script-metadata";
1718
import TxbuilderSendValues from "./send-values";
1819
import TxbuilderSetFee from "./set-fee";
1920
import TxbuilderSetNetwork from "./set-network";
@@ -29,6 +30,7 @@ const ReactPage: NextPage = () => {
2930
{ label: "Build with object", to: "buildWithObject" },
3031
{ label: "Coin selection", to: "coinSelection" },
3132
{ label: "Set metadata", to: "cip20" },
33+
{ label: "Set script metadata ", to: "scriptMetadata" },
3234
{ label: "Set required signers", to: "requiredSigners" },
3335
{ label: "Set time", to: "setTime" },
3436
{ label: "Set network", to: "setNetwork" },
@@ -79,6 +81,7 @@ const ReactPage: NextPage = () => {
7981
<TxbuilderBuildWithObject />
8082
<TxbuilderCoinSelection />
8183
<TxbuilderCip20 />
84+
<TxbuilderScriptMetadata />
8285
<TxbuilderSetRequiredSigners />
8386
<TxbuilderSetTime />
8487
<TxbuilderSetNetwork />
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { useState } from "react";
2+
3+
import { useWallet } from "@meshsdk/react";
4+
5+
import Textarea from "~/components/form/textarea";
6+
import Select from "~/components/form/select";
7+
import InputTable from "~/components/sections/input-table";
8+
import LiveCodeDemo from "~/components/sections/live-code-demo";
9+
import TwoColumnsScroll from "~/components/sections/two-columns-scroll";
10+
import Codeblock from "~/components/text/codeblock";
11+
import { getTxBuilder, txbuilderCode } from "../common";
12+
13+
export default function TxbuilderScriptMetadata() {
14+
return (
15+
<TwoColumnsScroll
16+
sidebarTo="scriptMetadata"
17+
title="Set Script Metadata"
18+
leftSection={Left()}
19+
rightSection={Right()}
20+
/>
21+
);
22+
}
23+
24+
function Left() {
25+
return (
26+
<>
27+
<p>
28+
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.
29+
The accepted script types are: <code>Native</code>, <code>PlutusV1</code>, <code>PlutusV2</code>, and <code>PlutusV3</code>.
30+
</p>
31+
<Codeblock
32+
data={`txBuilder\n .metadataScript(scriptCbor, scriptType)\n`}
33+
/>
34+
</>
35+
);
36+
}
37+
38+
function Right() {
39+
const { wallet, connected } = useWallet();
40+
41+
const [scriptCbor, setScriptCbor] = useState<string>("830302828200581c4fa1dd19be215b14a30f2a73f8b29e25bc917fbb2b3325b18394dca78200581c546a29981d02d06ea800e9bb9da1a9d8fc0e251a52a683f55bd7aa8f");
42+
const [scriptType, setScriptType] = useState<"PlutusV1" | "PlutusV2" | "PlutusV3" | "Native">("Native");
43+
44+
async function runDemo() {
45+
const utxos = await wallet.getUtxos();
46+
const changeAddress = await wallet.getChangeAddress();
47+
const txBuilder = getTxBuilder();
48+
49+
const unsignedTx = await txBuilder
50+
.changeAddress(changeAddress)
51+
.metadataScript(scriptCbor, scriptType)
52+
.selectUtxosFrom(utxos)
53+
.complete();
54+
55+
const signedTx = await wallet.signTx(unsignedTx);
56+
const txHash = await wallet.submitTx(signedTx);
57+
58+
return txHash;
59+
}
60+
61+
let codeSnippet = ``;
62+
codeSnippet += `const utxos = await wallet.getUtxos();\n`;
63+
codeSnippet += `const changeAddress = await wallet.getChangeAddress();\n\n`;
64+
codeSnippet += `const scriptCbor = "${scriptCbor}";\n`;
65+
codeSnippet += `const scriptType = "${scriptType}";\n\n`;
66+
codeSnippet += txbuilderCode;
67+
codeSnippet += `const unsignedTx = await txBuilder\n`;
68+
codeSnippet += ` .changeAddress(changeAddress)\n`;
69+
codeSnippet += ` .metadataScript(scriptCbor, scriptType)\n`;
70+
codeSnippet += ` .selectUtxosFrom(utxos)\n`;
71+
codeSnippet += ` .complete();\n`;
72+
codeSnippet += `\n`;
73+
codeSnippet += `const signedTx = await wallet.signTx(unsignedTx);\n`;
74+
codeSnippet += `const txHash = await wallet.submitTx(signedTx);\n`;
75+
76+
return (
77+
<LiveCodeDemo
78+
title="Add Script"
79+
subtitle="Add a script to a transaction's metadata."
80+
code={codeSnippet}
81+
runCodeFunction={runDemo}
82+
disabled={!connected}
83+
runDemoButtonTooltip={
84+
!connected ? "Connect wallet to run this demo" : undefined
85+
}
86+
runDemoShowBrowseWalletConnect={true}
87+
>
88+
<InputTable
89+
listInputs={[
90+
<Textarea
91+
value={scriptCbor}
92+
onChange={(e) => setScriptCbor(e.target.value)}
93+
label="Script CBOR"
94+
key="script-cbor"
95+
/>,
96+
<Select
97+
key="script-type"
98+
id="scriptType"
99+
label="Script Type"
100+
value={scriptType}
101+
onChange={(e) =>
102+
setScriptType(e.target.value as "PlutusV1" | "PlutusV2" | "PlutusV3" | "Native")
103+
}
104+
options={{
105+
PlutusV1: "PlutusV1",
106+
PlutusV2: "PlutusV2",
107+
PlutusV3: "PlutusV3",
108+
Native: "Native",
109+
}}
110+
/>,
111+
]}
112+
/>
113+
</LiveCodeDemo>
114+
);
115+
}

packages/mesh-common/src/types/transaction-builder/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export type MeshTxBuilderBody = {
2828
mints: MintParam[];
2929
changeAddress: string;
3030
metadata: TxMetadata;
31+
scriptMetadata: ScriptMetadata[];
3132
validityRange: ValidityRange;
3233
certificates: Certificate[];
3334
withdrawals: Withdrawal[];
@@ -54,6 +55,7 @@ export const emptyTxBuilderBody = (): MeshTxBuilderBody => ({
5455
mints: [],
5556
changeAddress: "",
5657
metadata: new Map<bigint, Metadatum>(),
58+
scriptMetadata: [],
5759
validityRange: {},
5860
certificates: [],
5961
withdrawals: [],
@@ -100,6 +102,10 @@ export type Metadata = {
100102
metadata: string;
101103
};
102104

105+
export type ScriptMetadata = {
106+
scriptType: "PlutusV1" | "PlutusV2" | "PlutusV3" | "Native";
107+
scriptCbor: string;
108+
};
103109
// Utilities
104110

105111
export type RequiredWith<T, K extends keyof T> = Required<T> & {

packages/mesh-core-cst/src/serializer/index.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
PubKeyTxIn,
4848
RefTxIn,
4949
RequiredWith,
50+
ScriptMetadata,
5051
ScriptSource,
5152
ScriptTxIn,
5253
ScriptVote,
@@ -606,6 +607,7 @@ class CardanoSDKSerializerCore {
606607
referenceInputs,
607608
mints,
608609
metadata,
610+
scriptMetadata,
609611
validityRange,
610612
certificates,
611613
withdrawals,
@@ -702,6 +704,11 @@ class CardanoSDKSerializerCore {
702704
throwErrorWithOrigin("Error serializing metadata", e);
703705
}
704706
}
707+
try {
708+
this.addScriptMetadata(scriptMetadata);
709+
} catch (e) {
710+
throwErrorWithOrigin("Error serializing script metadata", e);
711+
}
705712

706713
return this.txBody;
707714
};
@@ -1385,6 +1392,45 @@ class CardanoSDKSerializerCore {
13851392
);
13861393
};
13871394

1395+
private addScriptMetadata = (scripts: ScriptMetadata[]) => {
1396+
let nativeScriptArray = [];
1397+
let plutusV1ScriptArray = [];
1398+
let plutusV2ScriptArray = [];
1399+
let plutusV3ScriptArray = [];
1400+
for (let script of scripts) {
1401+
switch (script.scriptType) {
1402+
case "Native": {
1403+
nativeScriptArray.push(
1404+
NativeScript.fromCbor(HexBlob(script.scriptCbor)),
1405+
);
1406+
break;
1407+
}
1408+
case "PlutusV1": {
1409+
plutusV1ScriptArray.push(
1410+
PlutusV1Script.fromCbor(HexBlob(script.scriptCbor)),
1411+
);
1412+
break;
1413+
}
1414+
case "PlutusV2": {
1415+
plutusV2ScriptArray.push(
1416+
PlutusV2Script.fromCbor(HexBlob(script.scriptCbor)),
1417+
);
1418+
break;
1419+
}
1420+
case "PlutusV3": {
1421+
plutusV3ScriptArray.push(
1422+
PlutusV3Script.fromCbor(HexBlob(script.scriptCbor)),
1423+
);
1424+
break;
1425+
}
1426+
}
1427+
}
1428+
this.txAuxilliaryData.setNativeScripts(nativeScriptArray);
1429+
this.txAuxilliaryData.setPlutusV1Scripts(plutusV1ScriptArray);
1430+
this.txAuxilliaryData.setPlutusV2Scripts(plutusV2ScriptArray);
1431+
this.txAuxilliaryData.setPlutusV3Scripts(plutusV3ScriptArray);
1432+
};
1433+
13881434
private createMockedWitnessSet = (
13891435
requiredSignaturesCount: number,
13901436
requiredByronSignatures: string[],

packages/mesh-core-cst/test/sanitize-outputs.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ describe("Sanitize outputs", () => {
3434
changeAddress:
3535
"addr_test1qpvx0sacufuypa2k4sngk7q40zc5c4npl337uusdh64kv0uafhxhu32dys6pvn6wlw8dav6cmp4pmtv7cc3yel9uu0nq93swx9",
3636
metadata: new Map(),
37+
scriptMetadata: [],
3738
validityRange: {},
3839
certificates: [],
3940
withdrawals: [],

packages/mesh-core-cst/test/unbalanced.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ describe("Unbalanced", () => {
3939
changeAddress:
4040
"addr_test1qpvx0sacufuypa2k4sngk7q40zc5c4npl337uusdh64kv0uafhxhu32dys6pvn6wlw8dav6cmp4pmtv7cc3yel9uu0nq93swx9",
4141
metadata: new Map(),
42+
scriptMetadata: [],
4243
validityRange: {},
4344
certificates: [],
4445
withdrawals: [],

packages/mesh-core-cst/test/utils/serializer.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ describe("Build transaction with custom protocol params", () => {
267267
changeAddress:
268268
"addr_test1qra9zdhfa8kteyr3mfe7adkf5nlh8jl5xcg9e7pcp5w9yhyf5tek6vpnha97yd5yw9pezm3wyd77fyrfs3ynftyg7njs5cfz2x",
269269
metadata: new Map(),
270+
scriptMetadata: [],
270271
validityRange: {},
271272
certificates: [],
272273
withdrawals: [],

packages/mesh-transaction/src/mesh-tx-builder/tx-builder-core.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,6 +1440,23 @@ export class MeshTxBuilderCore {
14401440
return this;
14411441
};
14421442

1443+
/**
1444+
* Add metadata script to the transaction
1445+
* @param scriptCbor The script in CBOR format
1446+
* @param scriptType The type of the script, either "Native", "PlutusV1", "PlutusV2" or "PlutusV3"
1447+
* @returns The MeshTxBuilder instance
1448+
*/
1449+
metadataScript = (
1450+
scriptCbor: string,
1451+
scriptType: "Native" | "PlutusV1" | "PlutusV2" | "PlutusV3",
1452+
) => {
1453+
this.meshTxBuilderBody.scriptMetadata.push({
1454+
scriptCbor,
1455+
scriptType,
1456+
});
1457+
return this;
1458+
};
1459+
14431460
/**
14441461
* Sign the transaction with the private key
14451462
* @param skeyHex The private key in cborHex (with or without 5820 prefix, i.e. the format when generated from cardano-cli)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import {
2+
MeshTxBuilder,
3+
NativeScript,
4+
resolveNativeScriptHex,
5+
} from "@meshsdk/core";
6+
import { Transaction, TxCBOR } from "@meshsdk/core-cst";
7+
8+
import { alwaysSucceedCbor } from "../test-util";
9+
10+
describe("MeshTxBuilder Script Metadata", () => {
11+
it("should create a transaction with plutusV3 metadata", async () => {
12+
const txBuilder = new MeshTxBuilder();
13+
14+
let txHex = txBuilder
15+
.txIn(
16+
"2cb57168ee66b68bd04a0d595060b546edf30c04ae1031b883c9ac797967dd85",
17+
3,
18+
[{ unit: "lovelace", quantity: "9891607895" }],
19+
"addr_test1vru4e2un2tq50q4rv6qzk7t8w34gjdtw3y2uzuqxzj0ldrqqactxh",
20+
)
21+
.txOut(
22+
"addr_test1vru4e2un2tq50q4rv6qzk7t8w34gjdtw3y2uzuqxzj0ldrqqactxh",
23+
[{ unit: "lovelace", quantity: "2000000" }],
24+
)
25+
.changeAddress(
26+
"addr_test1vru4e2un2tq50q4rv6qzk7t8w34gjdtw3y2uzuqxzj0ldrqqactxh",
27+
)
28+
.metadataScript(alwaysSucceedCbor, "PlutusV3")
29+
.completeSync();
30+
31+
const cardanoTx = Transaction.fromCbor(TxCBOR(txHex));
32+
expect(cardanoTx.auxiliaryData()?.plutusV3Scripts).toBeDefined();
33+
});
34+
35+
it("should create a transaction with native script metadata", async () => {
36+
const txBuilder = new MeshTxBuilder();
37+
const nativeScript: NativeScript = {
38+
type: "all",
39+
scripts: [],
40+
};
41+
let txHex = txBuilder
42+
.txIn(
43+
"2cb57168ee66b68bd04a0d595060b546edf30c04ae1031b883c9ac797967dd85",
44+
3,
45+
[{ unit: "lovelace", quantity: "9891607895" }],
46+
"addr_test1vru4e2un2tq50q4rv6qzk7t8w34gjdtw3y2uzuqxzj0ldrqqactxh",
47+
)
48+
.txOut(
49+
"addr_test1vru4e2un2tq50q4rv6qzk7t8w34gjdtw3y2uzuqxzj0ldrqqactxh",
50+
[{ unit: "lovelace", quantity: "2000000" }],
51+
)
52+
.changeAddress(
53+
"addr_test1vru4e2un2tq50q4rv6qzk7t8w34gjdtw3y2uzuqxzj0ldrqqactxh",
54+
)
55+
.metadataScript(resolveNativeScriptHex(nativeScript), "Native")
56+
.completeSync();
57+
58+
const cardanoTx = Transaction.fromCbor(TxCBOR(txHex));
59+
expect(cardanoTx.auxiliaryData()?.nativeScripts).toBeDefined();
60+
});
61+
});

0 commit comments

Comments
 (0)