diff --git a/packages/mesh-core-cst/src/index.ts b/packages/mesh-core-cst/src/index.ts index 87ab5ab1f..ceb4c0b95 100644 --- a/packages/mesh-core-cst/src/index.ts +++ b/packages/mesh-core-cst/src/index.ts @@ -6,6 +6,7 @@ export * from "./resolvers"; export * from "./serializer"; export * from "./stricahq"; export * from "./utils"; +export * from "./plutus-tools"; export * as CardanoSDKUtil from "@cardano-sdk/util"; export * as Crypto from "@cardano-sdk/crypto"; diff --git a/packages/mesh-core-cst/src/plutus-tools/index.ts b/packages/mesh-core-cst/src/plutus-tools/index.ts index cdd60d4d0..709f4ada2 100644 --- a/packages/mesh-core-cst/src/plutus-tools/index.ts +++ b/packages/mesh-core-cst/src/plutus-tools/index.ts @@ -1,18 +1,18 @@ /** * MIT License - * + * * Copyright (c) 2024 Evgenii Lisitskii - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -61,7 +61,7 @@ export type OutputEncoding = "SingleCBOR" | "DoubleCBOR" | "PurePlutusScriptByte * - This function modifies the structure of the Plutus script by applying arguments, which can change its behavior when executed. * - The function assumes that the input arguments are in a compatible format (CBOR-encoded) and that the program is a valid Plutus script. */ -export const applyArgsToPlutusScript = (args: Uint8Array[], program: Uint8Array, outputEncoding: OutputEncoding): Uint8Array => { +const applyArgsToPlutusScript = (args: Uint8Array[], program: Uint8Array, outputEncoding: OutputEncoding): Uint8Array => { const purePlutusBytes = getPurePlutusBytes(program); const parsedProgram = parseUPLC(purePlutusBytes, "flat"); const decodedArgs = args.map((arg) => dataFromCbor(arg)); @@ -86,16 +86,18 @@ export const applyArgsToPlutusScript = (args: Uint8Array[], program: Uint8Array, * * @description * This function performs the following steps: - * 1. Extracts the pure Plutus bytes from the input script. + * 1. Extracts the pure Plutus bytes in hex from the input script. * 2. Applies the specified encoding to the pure Plutus bytes. * * @note * - This function is useful for standardizing the format of Plutus scripts, ensuring they are in a consistent state for further processing or comparison. * - The normalization process does not modify the logical content of the script, only its representation. */ -export const normalizePlutusScript = (plutusScript: Uint8Array, encoding: OutputEncoding): Uint8Array => { - const purePlutusBytes = getPurePlutusBytes(plutusScript); - return applyEncoding(purePlutusBytes, encoding); +export const normalizePlutusScript = (plutusScript: string, encoding: OutputEncoding): string => { + const bytes = Buffer.from(plutusScript, "hex"); + const purePlutusBytes = getPurePlutusBytes(bytes); + const normalizedBytes = applyEncoding(purePlutusBytes, encoding); + return Buffer.from(normalizedBytes).toString("hex"); } const hasSupportedPlutusVersion = (plutusScript: Uint8Array): boolean => { @@ -112,8 +114,10 @@ const hasSupportedPlutusVersion = (plutusScript: Uint8Array): boolean => { const getPurePlutusBytes = (plutusScript: Uint8Array): Uint8Array => { let unwrappedScript = plutusScript; + let length = 0; try { - while (unwrappedScript.length >= 3) { + while (unwrappedScript.length >= 3 && length != unwrappedScript.length) { + length = unwrappedScript.length; if (hasSupportedPlutusVersion(unwrappedScript)) { return unwrappedScript; } @@ -148,4 +152,4 @@ const applyEncoding = (plutusScript: Uint8Array, outputEncoding: OutputEncoding) default: return applyCborEncoding(plutusScript); } -} \ No newline at end of file +} diff --git a/packages/mesh-provider/src/blockfrost.ts b/packages/mesh-provider/src/blockfrost.ts index 2a662109a..961ba86c1 100644 --- a/packages/mesh-provider/src/blockfrost.ts +++ b/packages/mesh-provider/src/blockfrost.ts @@ -21,7 +21,11 @@ import { TransactionInfo, UTxO, } from "@meshsdk/common"; -import { resolveRewardAddress, toScriptRef } from "@meshsdk/core-cst"; +import { + resolveRewardAddress, + toScriptRef, + normalizePlutusScript, +} from "@meshsdk/core-cst"; import { utxosToAssets } from "./common/utxos-to-assets"; import { BlockfrostAsset, BlockfrostUTxO } from "./types"; @@ -489,7 +493,7 @@ export class BlockfrostProvider if(index !== undefined) { return outputs.filter((utxo) => utxo.input.outputIndex === index); } - + return outputs; } throw parseHttpError(data); @@ -602,12 +606,17 @@ export class BlockfrostProvider ); if (status === 200 || status == 202) { - const script = data.type.startsWith("plutus") - ? { - code: await this.fetchPlutusScriptCBOR(scriptHash), - version: data.type.replace("plutus", ""), - } - : await this.fetchNativeScriptJSON(scriptHash); + let script; + if (data.type.startsWith("plutus")) { + const plutusScript = await this.fetchPlutusScriptCBOR(scriptHash); + const normalized = normalizePlutusScript(plutusScript, "DoubleCBOR"); + script = { + version: data.type.replace("plutus", ""), + code: normalized + }; + } else { + script = await this.fetchNativeScriptJSON(scriptHash); + } return toScriptRef(script).toCbor().toString(); } diff --git a/packages/mesh-provider/src/koios.ts b/packages/mesh-provider/src/koios.ts index 1d5792c05..b89ef04b0 100644 --- a/packages/mesh-provider/src/koios.ts +++ b/packages/mesh-provider/src/koios.ts @@ -20,7 +20,7 @@ import { } from "@meshsdk/common"; import { deserializeNativeScript, - fromNativeScript, + fromNativeScript, normalizePlutusScript, resolveRewardAddress, toScriptRef, } from "@meshsdk/core-cst"; @@ -500,12 +500,16 @@ export class KoiosProvider implements IFetcher, IListener, ISubmitter { kScriptRef: KoiosReferenceScript | undefined, ): string | undefined => { if (kScriptRef) { - const script = kScriptRef.type.startsWith("plutus") - ? { - code: kScriptRef.bytes, - version: kScriptRef.type.replace("plutus", ""), - } - : fromNativeScript(deserializeNativeScript(kScriptRef.bytes)); + let script; + if(kScriptRef.type.startsWith("plutus")) { + const normalized = normalizePlutusScript(kScriptRef.bytes, "DoubleCBOR"); + script = { + code: normalized, + version: kScriptRef.type.replace("plutus", ""), + } + } else { + script = fromNativeScript(deserializeNativeScript(kScriptRef.bytes)); + } if (script) return toScriptRef(script).toCbor().toString(); } diff --git a/packages/mesh-provider/src/maestro.ts b/packages/mesh-provider/src/maestro.ts index 8e55ba430..b8dd1dfe3 100644 --- a/packages/mesh-provider/src/maestro.ts +++ b/packages/mesh-provider/src/maestro.ts @@ -22,6 +22,7 @@ import { UTxO, } from "@meshsdk/common"; import { + normalizePlutusScript, resolveRewardAddress, toScriptRef, VrfVkBech32, @@ -515,13 +516,21 @@ export class MaestroProvider private resolveScript = (utxo: MaestroUTxO) => { if (utxo.reference_script) { - const script = - utxo.reference_script.type === "native" - ? utxo.reference_script.json - : { - code: utxo.reference_script.bytes, - version: utxo.reference_script.type.replace("plutusv", "V"), - }; + let script; + if (utxo.reference_script.type === "native") { + script = utxo.reference_script.json; + } else { + const scriptBytes = utxo.reference_script.bytes; + if(scriptBytes) { + const normalized = normalizePlutusScript(scriptBytes, "DoubleCBOR"); + script = { + code: normalized, + version: utxo.reference_script.type.replace("plutusv", "V"), + }; + } else { + throw new Error("Script bytes not found"); + } + } return toScriptRef(script).toCbor().toString(); } else return undefined; }; diff --git a/packages/mesh-provider/src/yaci.ts b/packages/mesh-provider/src/yaci.ts index 2de7a61c2..53022e9fd 100644 --- a/packages/mesh-provider/src/yaci.ts +++ b/packages/mesh-provider/src/yaci.ts @@ -21,7 +21,11 @@ import { Unit, UTxO, } from "@meshsdk/common"; -import { resolveRewardAddress, toScriptRef } from "@meshsdk/core-cst"; +import { + normalizePlutusScript, + resolveRewardAddress, + toScriptRef +} from "@meshsdk/core-cst"; import { utxosToAssets } from "./common/utxos-to-assets"; import { parseHttpError } from "./utils"; @@ -94,12 +98,17 @@ export class YaciProvider ); if (status === 200) { - const script = data.type.startsWith("plutus") - ? { - code: await this.fetchPlutusScriptCBOR(scriptHash), - version: data.type.replace("plutus", ""), - } - : await this.fetchNativeScriptJSON(scriptHash); + let script; + if (data.type.startsWith("plutus")) { + const plutusScript = await this.fetchPlutusScriptCBOR(scriptHash); + const normalized = normalizePlutusScript(plutusScript, "DoubleCBOR"); + script = { + version: data.type.replace("plutus", ""), + code: normalized + }; + } else { + script = await this.fetchNativeScriptJSON(scriptHash); + } return toScriptRef(script).toCbor(); }