Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature offline evaluator #349

Merged
merged 24 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
6 changes: 2 additions & 4 deletions apps/playground/src/components/site/metatags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,15 @@ export default function Metatags({
}

if (title === undefined) {
title = "Mesh JS - Cardano Web3 TypeScript SDK & Off-Chain Framework";
} else {
title = title + " - Mesh JS";
title = "Cardano Web3 TypeScript SDK & Off-Chain Framework";
}

return (
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charSet="utf-8" />

{title && <title>{title}</title>}
{title && <title>{`${title} - Mesh JS`}</title>}
<meta name="description" content={description} />
<meta name="keywords" content={keywords} />

Expand Down
4 changes: 4 additions & 0 deletions apps/playground/src/data/cardano.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export const oneTimeMintingPolicy =
export const demoTransactionCbor =
"84a70081825820859d3b4fd3a4c012b43ee1bbbc99240aec1827c3b8a74b867d10a7f4759149bc00018382583900e4cfbbc317c718f78d137b6535d8940618cc3d2ac04f1f35acf78e53a1521c2cea3cc79762d575581e47ea60b8eaa03430716cfd6140c796821a0011b0dea1581c67dd133868f14107b25772f3c5abaa1e0549f4b400b5e0e3a1136152a149000643b0546573743101a300581d7067dd133868f14107b25772f3c5abaa1e0549f4b400b5e0e3a113615201821a001ad510a1581c67dd133868f14107b25772f3c5abaa1e0549f4b400b5e0e3a1136152a149000de140546573743101028201d8185882d8799fa4446e616d6545546573743145696d6167655835697066733a2f2f516d527a6963705265757477436b4d36616f74754b6a4572464355443231334470775071364279757a4d4a617561496d656469615479706549696d6167652f6a70674b6465736372697074696f6e5348656c6c6f20776f726c64202d20434950363802ff825839003659ed2a30abb32e97589f2a01c8500ce8fc4897b868ebe42fbf4a8aa1521c2cea3cc79762d575581e47ea60b8eaa03430716cfd6140c7961a00134249021a000c830909a1581c67dd133868f14107b25772f3c5abaa1e0549f4b400b5e0e3a1136152a249000643b054657374310149000de1405465737431010b58207ae25a8a9286347cc1e0444a0de75e07432a6ed243591ef673fd837bb5235a670d82825820859d3b4fd3a4c012b43ee1bbbc99240aec1827c3b8a74b867d10a7f4759149bc00825820859d3b4fd3a4c012b43ee1bbbc99240aec1827c3b8a74b867d10a7f4759149bc050e81581ce4cfbbc317c718f78d137b6535d8940618cc3d2ac04f1f35acf78e53a206815883588101000032323232323232322232533300632323232533300a3370e9000000899b8f375c601c601000e911046d6573680014a0601000260180026018002600800429309b2b19299980319b87480000044c8c94ccc02cc03400852616375c601600260080062c60080044600a6ea80048c00cdd5000ab9a5573aaae7955cfaba157450581840100d8799f446d657368ff821a006acfc01ab2d05e00f5f6";

// from content ownership mint policy
export const demoTransactionCborScript =
"84a600d901028182582056ae6e2d8c419ab279ecd308040b8f56025cd4a287c75c182f8ccc0fd4115b0700018182583900556f3a70b8a68081cf36c918dd9933abdca34f20fc534499c817182b8cfb40854d41392b624b678012443d61015f5575627a467c450396c9821a008c2c8fa1581c8251cfa433b84ed4357143657f1c33740caa9e953c5f266c86ecfaf5a14001021a000c69f109a1581c8251cfa433b84ed4357143657f1c33740caa9e953c5f266c86ecfaf5a140010b58204fb018afc3952620696789649a8447595f9895ec48bda9c2596f0507e9b43f360dd9010281825820e8b1c89936d747185cf77c3eb42a37749cb4bdfee36d8763cfffad264ecbe0b900a207d90102815901cc5901c901010033232323232323222533300332323232325332330093001300a37540042646464a66601860080022a66601e601c6ea8018540085854ccc030cdc3a40040022a66601e601c6ea8018540085858c030dd50028992999805980198061baa0051533300b3003300c375464660020026eb0c044c038dd50041129998080008a6103d87a800013232533300f3375e01c600a60226ea80084cdd2a40006602600497ae01330040040013014002301200114a229404c8cc004004c8cc004004dd59809180998099809980998079baa00922533301100114bd70099199911191980080080191299980b80088018991980c9ba733019375200c66032602c00266032602e00297ae033003003301b0023019001375c60200026eacc044004cc00c00cc054008c04c004894ccc040004528899299980719299980799b8f375c600a00200c266e20dd6980a180a980a800a40002944dd618098010998018018008a50301300123010001375c601c60166ea8008dc3a40002c6018601a004601600260160046012002600a6ea8004526136565734aae7555cf2ab9f5740ae855d1260127d8799f582056ae6e2d8c419ab279ecd308040b8f56025cd4a287c75c182f8ccc0fd4115b0700ff000105a182010082d87980821a006acfc01ab2d05e00f5f6";

export const yaci = {
mnemonic: [
"test",
Expand Down
12 changes: 10 additions & 2 deletions apps/playground/src/data/links-providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,14 @@ export const metaOfflineFetcher = {
title: "Offline Fetcher",
link: "/providers/offline-fetcher",
desc: "Provider for testing, development and offline scenarios",
thumbnail: "/providers/hydra.svg",
thumbnail: "/logo-mesh/mesh.png",
};

export const metaOfflineEvaluator = {
title: "Offline Evaluator",
link: "/providers/offline-evaluator",
desc: "An offline Plutus script evaluator for testing and validation.",
thumbnail: "/logo-mesh/mesh.png",
};

export const linksProviders: MenuItem[] = [
Expand All @@ -66,7 +73,8 @@ export const linksProviders: MenuItem[] = [
metaOgmios,
metaU5c,
metaYaci,
metaOfflineFetcher
metaOfflineFetcher,
metaOfflineEvaluator,
];

export const metaProviders = {
Expand Down
20 changes: 17 additions & 3 deletions apps/playground/src/pages/providers/evaluators/evaluate-tx.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { useState } from "react";

import { OfflineEvaluator } from "@meshsdk/core-csl";

import { getProvider } from "~/components/cardano/mesh-wallet";
import Input from "~/components/form/input";
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 { demoTransactionCbor } from "~/data/cardano";
import { demoTransactionCborScript } from "~/data/cardano";
import { SupportedEvaluators } from ".";

export default function EvaluatorEvaluateTransaction({
Expand All @@ -15,7 +18,7 @@ export default function EvaluatorEvaluateTransaction({
blockchainProvider: SupportedEvaluators;
provider: string;
}) {
const [userInput, setUserInput] = useState<string>(demoTransactionCbor);
const [userInput, setUserInput] = useState<string>(demoTransactionCborScript);

return (
<TwoColumnsScroll
Expand Down Expand Up @@ -91,17 +94,28 @@ function Right(
provider: string,
) {
async function runDemo() {
const evaluateTx = await blockchainProvider.evaluateTx(userInput);
const blockchainProvider = getProvider();
const offlineeval = new OfflineEvaluator(blockchainProvider, "preprod");
const evaluateTx = await offlineeval.evaluateTx(userInput);
return evaluateTx;
}

let code = ``;
code += `import { BlockfrostProvider } from "@meshsdk/core";\n`;
code += `import { OfflineEvaluator } from "@meshsdk/core-csl";\n`;
code += `\n`;
code += `const blockchainProvider = new BlockfrostProvider('<Your-API-Key>');\n`;
code += `const offlineeval = new OfflineEvaluator(blockchainProvider, "preprod");\n\n`;
code += `const evaluateTx = await offlineeval.evaluateTx('<UNSIGNED-TX-HEX>');\n`;

return (
<LiveCodeDemo
title="Evaluate Transaction"
subtitle="Evaluate the resources required to execute a transaction"
runCodeFunction={runDemo}
runDemoShowProviderInit={true}
runDemoProvider={provider}
code={code}
>
<InputTable
listInputs={[
Expand Down
4 changes: 3 additions & 1 deletion apps/playground/src/pages/providers/evaluators/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from "@meshsdk/core";

import EvaluatorEvaluateTransaction from "./evaluate-tx";
import {OfflineEvaluator} from "@meshsdk/core-csl";

export default function ProviderEvaluators({
blockchainProvider,
Expand All @@ -30,4 +31,5 @@ export type SupportedEvaluators =
| YaciProvider
| MaestroProvider
| OgmiosProvider
| U5CProvider;
| U5CProvider
| OfflineEvaluator;
125 changes: 125 additions & 0 deletions apps/playground/src/pages/providers/offline-evaluator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import type { NextPage } from "next";

import { OfflineFetcher } from "@meshsdk/core";
import { OfflineEvaluator } from "@meshsdk/core-csl";

import ButtonFloatDocumentation from "~/components/button/button-float-documentation";
import SidebarFullwidth from "~/components/layouts/sidebar-fullwidth";
import Link from "~/components/link";
import TitleIconDescriptionBody from "~/components/sections/title-icon-description-body";
import Metatags from "~/components/site/metatags";
import Codeblock from "~/components/text/codeblock";
import { metaOfflineEvaluator } from "~/data/links-providers";
import ProviderEvaluators from "./evaluators";

const ReactPage: NextPage = () => {
const sidebarItems = [{ label: "Evaluate Transaction", to: "evaluateTx" }];

let code1 = `import { OfflineEvaluator } from "@meshsdk/core-csl";\n\n`;
code1 += `import { OfflineFetcher } from "@meshsdk/core";\n\n`;
code1 += `// Create fetcher for resolving UTXOs\n`;
code1 += `const fetcher = new OfflineFetcher();\n\n`;
code1 += `// Add UTXOs required for script evaluation\n`;
code1 += `fetcher.addUTxOs([\n`;
code1 += ` {\n`;
code1 += ` input: { \n`;
code1 += ` txHash: "5de23a2...", \n`;
code1 += ` outputIndex: 0 \n`;
code1 += ` },\n`;
code1 += ` output: {\n`;
code1 += ` address: "addr1...",\n`;
code1 += ` amount: [{ unit: "lovelace", quantity: "1000000" }],\n`;
code1 += ` scriptHash: "32b7e3d..." // For script UTXOs\n`;
code1 += ` }\n`;
code1 += ` }\n`;
code1 += `]);\n\n`;
code1 += `// Create evaluator for the desired network\n`;
code1 += `const evaluator = new OfflineEvaluator(fetcher, "preprod");\n`;

let code2 = `// Evaluate Plutus scripts in a transaction\n`;
code2 += `try {\n`;
code2 += ` const actions = await evaluator.evaluateTx(transactionCbor);\n`;
code2 += ` // Example result:\n`;
code2 += ` // [{\n`;
code2 += ` // index: 0,\n`;
code2 += ` // tag: "MINT",\n`;
code2 += ` // budget: {\n`;
code2 += ` // mem: 508703, // Memory units used\n`;
code2 += ` // steps: 164980381 // CPU steps used\n`;
code2 += ` // }\n`;
code2 += ` // }]\n`;
code2 += `} catch (error) {\n`;
code2 += ` console.error('Script evaluation failed:', error);\n`;
code2 += `}\n`;

let code3 = `// In your test file\n`;
code3 += `describe("Plutus Script Tests", () => {\n`;
code3 += ` let evaluator: OfflineEvaluator;\n`;
code3 += ` let fetcher: OfflineFetcher;\n\n`;
code3 += ` beforeEach(() => {\n`;
code3 += ` fetcher = new OfflineFetcher();\n`;
code3 += ` evaluator = new OfflineEvaluator(fetcher, "preprod");\n\n`;
code3 += ` // Add test UTXOs\n`;
code3 += ` fetcher.addUTxOs([...]);\n`;
code3 += ` });\n\n`;
code3 += ` it("should evaluate minting policy", async () => {\n`;
code3 += ` const result = await evaluator.evaluateTx(txCbor);\n`;
code3 += ` expect(result[0].tag).toBe("MINT");\n`;
code3 += ` expect(result[0].budget.mem).toBeLessThan(600000);\n`;
code3 += ` });\n`;
code3 += `});\n`;

const fetcher = new OfflineFetcher();
const evaluator = new OfflineEvaluator(fetcher, "preprod");

return (
<>
<Metatags
title={metaOfflineEvaluator.title}
description={metaOfflineEvaluator.desc}
/>
<SidebarFullwidth sidebarItems={sidebarItems}>
<TitleIconDescriptionBody
title={metaOfflineEvaluator.title}
description={metaOfflineEvaluator.desc}
>
<p>
The OfflineEvaluator calculates execution costs (memory and CPU
steps) for Plutus scripts in transactions without requiring network
connectivity. It works with an{" "}
<Link href="/providers/offline-fetcher">OfflineFetcher</Link> to
resolve the UTXOs needed for script validation. This is also
compatible with any other fetchers to provide online data fetching.
</p>

<p>Get started:</p>

<Codeblock data={code1} />

<p>
Once initialized, you can evaluate Plutus scripts in transactions:
</p>
<Codeblock data={code2} />

<p>
The evaluator is particularly useful for testing Plutus scripts,
ensuring they execute within memory and CPU limits:
</p>
<Codeblock data={code3} />

<p>
The evaluation results include memory units and CPU steps required
for each script execution, helping you optimize your scripts and
ensure they meet protocol constraints.
</p>
</TitleIconDescriptionBody>

<ButtonFloatDocumentation href="https://docs.meshjs.dev/providers/classes/OfflineEvaluator" />

<ProviderEvaluators blockchainProvider={evaluator} provider="offline" />
</SidebarFullwidth>
</>
);
};

export default ReactPage;
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ const ReactPage: NextPage = () => {
];

let example = ``;

example += `import { MeshContentOwnershipContract } from "@meshsdk/contract";\n`;
example += `import { MeshTxBuilder, BlockfrostProvider } from "@meshsdk/core";\n`;
example += `\n`;
Expand Down
Loading