Skip to content
Open
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 @@ -19,6 +19,7 @@ import TxbuilderSetNetwork from "./set-network";
import TxbuilderSetRequiredSigners from "./set-required-signers";
import TxbuilderSetTime from "./set-time";
import TxbuilderCustomPP from "./custom-pp";
import TxbuilderScriptMetadata from "./script-metadata";

const ReactPage: NextPage = () => {
const sidebarItems = [
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 @@ -81,6 +83,7 @@ const ReactPage: NextPage = () => {
{/* <TxbuilderSetMetadata /> */}
<TxbuilderCoinSelection />
<TxbuilderCip20 />
<TxbuilderScriptMetadata />
{/* <TxbuilderSetCollateral /> */}
<TxbuilderSetRequiredSigners />
<TxbuilderSetTime />
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 Down Expand Up @@ -59,6 +60,7 @@ export const emptyTxBuilderBody = (): MeshTxBuilderBody => ({
mints: [],
changeAddress: "",
metadata: new Map<bigint, Metadatum>(),
scriptMetadata: [],
validityRange: {},
certificates: [],
withdrawals: [],
Expand Down Expand Up @@ -110,6 +112,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 @@ -46,6 +46,7 @@ import {
PubKeyTxIn,
RefTxIn,
RequiredWith,
ScriptMetadata,
ScriptSource,
ScriptTxIn,
ScriptVote,
Expand Down Expand Up @@ -586,6 +587,7 @@ class CardanoSDKSerializerCore {
referenceInputs,
mints,
metadata,
scriptMetadata,
validityRange,
certificates,
withdrawals,
Expand Down Expand Up @@ -682,6 +684,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 @@ -1365,6 +1372,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
25 changes: 11 additions & 14 deletions packages/mesh-hydra/src/hydra-provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,30 @@ import { MeshTxBuilder, MeshWallet } from "@meshsdk/core";
import { HydraProvider } from "./hydra-provider";

describe("Hydra Provider", () => {
it('should make and submit a valid tx', async () => {
it("should make and submit a valid tx", async () => {
const provider = new HydraProvider({
url: 'http://localhost:4001',
wsUrl: 'ws://localhost:4001'
url: "http://localhost:4001",
wsUrl: "ws://localhost:4001",
});
await provider.connect();

const aliceWallet = {
addr: 'addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z',
key: '58205f9b911a636479ed83ba601ccfcba0ab9a558269dc19fdea910d27e5cdbb5fc8',
addr: "addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z",
key: "58205f9b911a636479ed83ba601ccfcba0ab9a558269dc19fdea910d27e5cdbb5fc8",
};

const wallet = new MeshWallet({
networkId: 0,
key: {
type: 'cli',
type: "cli",
payment: aliceWallet.key,
},
fetcher: provider,
submitter: provider,
});

const pp = await provider.fetchProtocolParameters();
const utxos = await wallet.getUtxos('enterprise');
const utxos = await wallet.getUtxos("enterprise");
console.log("utxos", utxos);
const changeAddress = aliceWallet.addr;

Expand All @@ -36,10 +36,7 @@ describe("Hydra Provider", () => {
});

const unsignedTx = await txBuilder
.txOut(
changeAddress,
[{ unit: 'lovelace', quantity: '1000000' }],
)
.txOut(changeAddress, [{ unit: "lovelace", quantity: "1000000" }])
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
Expand All @@ -48,7 +45,7 @@ describe("Hydra Provider", () => {
console.log(`signedTx`, signedTx);
const txHash = await wallet.submitTx(signedTx);
console.log(`txHash`, txHash);
await provider.close()
await provider.fanout()
await provider.close();
await provider.fanout();
});
})
});
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();
});
});