diff --git a/blueprint.ts b/blueprint.ts index b295e5b..25b1125 100644 --- a/blueprint.ts +++ b/blueprint.ts @@ -29,21 +29,22 @@ type Blueprint = { compiledCode: string; hash: string; }[]; - definitions: Record; + definitions: Record< + string, + { + title: string; + schema: { + $ref: string; + }; + } + >; }; export async function parseBlueprint(blueprint: string, plutusTs: string) { - const plutusJson: Blueprint = JSON.parse( - await Bun.file(blueprint).text(), - ); + const plutusJson: Blueprint = JSON.parse(await Bun.file(blueprint).text()); - const plutusVersion = "Plutus" + - plutusJson.preamble.plutusVersion.toUpperCase(); + const plutusVersion = + "Plutus" + plutusJson.preamble.plutusVersion.toUpperCase(); const definitions = plutusJson.definitions; @@ -69,25 +70,28 @@ import { applyParamsToScript, Data, Validator } from "../translucent/index.ts"`; dataType: "list", items: params.map((param) => resolveSchema(param.schema, definitions)), }; - const paramsArgs = params.map(( - param, - index, - ) => [snakeToCamel(param.title), schemaToType(paramsSchema.items[index])]); + const paramsArgs = params.map((param, index) => [ + snakeToCamel(param.title), + schemaToType(paramsSchema.items[index]), + ]); const script = validator.compiledCode; return `export interface ${name} { - new (${paramsArgs.map((param) => param.join(":")).join(",")}): Validator;${datum ? `\n${datumTitle}: ${schemaToType(datumSchema)};` : "" - } + new (${paramsArgs.map((param) => param.join(":")).join(",")}): Validator;${ + datum ? `\n${datumTitle}: ${schemaToType(datumSchema)};` : "" + } ${redeemerTitle}: ${schemaToType(redeemerSchema)}; }; export const ${name} = Object.assign( - function (${paramsArgs.map((param) => param.join(":")).join(",")}) {${paramsArgs.length > 0 - ? `return { type: "${plutusVersion}", script: applyParamsToScript("${script}", [${paramsArgs.map((param) => param[0]).join(",") - }], ${JSON.stringify(paramsSchema)} as any) };` + function (${paramsArgs.map((param) => param.join(":")).join(",")}) {${ + paramsArgs.length > 0 + ? `return { type: "${plutusVersion}", script: applyParamsToScript("${script}", [${paramsArgs + .map((param) => param[0]) + .join(",")}], ${JSON.stringify(paramsSchema)} as any) };` : `return {type: "${plutusVersion}", script: "${script}"};` - }}, + }}, ${datum ? `{${datumTitle}: ${JSON.stringify(datumSchema)}},` : ""} {${redeemerTitle}: ${JSON.stringify(redeemerSchema)}}, ) as unknown as ${name};`; @@ -95,7 +99,6 @@ import { applyParamsToScript, Data, Validator } from "../translucent/index.ts"`; const plutus = imports + "\n\n" + validators.join("\n\n"); - await Bun.write(plutusTs, plutus); console.log( @@ -110,7 +113,7 @@ import { applyParamsToScript, Data, Validator } from "../translucent/index.ts"`; return { ...schema, items: schema.items.map((item: any) => - resolveSchema(item, definitions) + resolveSchema(item, definitions), ), }; } else { @@ -138,14 +141,19 @@ import { applyParamsToScript, Data, Validator } from "../translucent/index.ts"`; }; } else { if (schema["$ref"]) { - const refKey = - schema["$ref"].replaceAll("~1", "/").split("#/definitions/")[1]; + const refKey = schema["$ref"] + .replaceAll("~1", "/") + .split("#/definitions/")[1]; if (refKey === refName) { return schema; } else { - refName = refKey - const resolved = resolveSchema(definitions[refKey], definitions, refName); + refName = refKey; + const resolved = resolveSchema( + definitions[refKey], + definitions, + refName, + ); return resolved; } } else { @@ -169,10 +177,12 @@ import { applyParamsToScript, Data, Validator } from "../translucent/index.ts"`; if (isVoid(schema)) { return "undefined"; } else { - return `{${schema.fields.map((field: any) => - `${field.title || "wrapper"}:${schemaToType(field)}` - ).join(";") - }}`; + return `{${schema.fields + .map( + (field: any) => + `${field.title || "wrapper"}:${schemaToType(field)}`, + ) + .join(";")}}`; } } case "enum": { @@ -186,30 +196,37 @@ import { applyParamsToScript, Data, Validator } from "../translucent/index.ts"`; if (isNullable(schema)) { return `${schemaToType(schema.anyOf[0].fields[0])} | null`; } - return schema.anyOf.map((entry: any) => - entry.fields.length === 0 - ? `"${entry.title}"` - : `{${entry.title}: ${entry.fields[0].title - ? `{${entry.fields.map((field: any) => - [field.title, schemaToType(field)].join(":") - ).join(",") - }}}` - : `[${entry.fields.map((field: any) => schemaToType(field)).join(",") - }]}` - }` - ).join(" | "); + return schema.anyOf + .map((entry: any) => + entry.fields.length === 0 + ? `"${entry.title}"` + : `{${entry.title}: ${ + entry.fields[0].title + ? `{${entry.fields + .map((field: any) => + [field.title, schemaToType(field)].join(":"), + ) + .join(",")}}}` + : `[${entry.fields + .map((field: any) => schemaToType(field)) + .join(",")}]}` + }`, + ) + .join(" | "); } case "list": { if (schema.items instanceof Array) { - return `[${schema.items.map((item: any) => schemaToType(item)).join(",") - }]`; + return `[${schema.items + .map((item: any) => schemaToType(item)) + .join(",")}]`; } else { return `Array<${schemaToType(schema.items)}>`; } } case "map": { - return `Map<${schemaToType(schema.keys)}, ${schemaToType(schema.values) - }>`; + return `Map<${schemaToType(schema.keys)}, ${schemaToType( + schema.values, + )}>`; } case undefined: { return "Data"; @@ -219,8 +236,11 @@ import { applyParamsToScript, Data, Validator } from "../translucent/index.ts"`; } function isBoolean(shape: any): boolean { - return shape.anyOf && shape.anyOf[0]?.title === "False" && - shape.anyOf[1]?.title === "True"; + return ( + shape.anyOf && + shape.anyOf[0]?.title === "False" && + shape.anyOf[1]?.title === "True" + ); } function isVoid(shape: any): boolean { @@ -228,29 +248,33 @@ import { applyParamsToScript, Data, Validator } from "../translucent/index.ts"`; } function isNullable(shape: any): boolean { - return shape.anyOf && shape.anyOf[0]?.title === "Some" && - shape.anyOf[1]?.title === "None"; + return ( + shape.anyOf && + shape.anyOf[0]?.title === "Some" && + shape.anyOf[1]?.title === "None" + ); } function snakeToCamel(s: string): string { const withUnderscore = s.charAt(0) === "_" ? s.charAt(0) : ""; - return withUnderscore + - (withUnderscore ? s.slice(1) : s).toLowerCase().replace( - /([-_][a-z])/g, - (group) => - group - .toUpperCase() - .replace("-", "") - .replace("_", ""), - ); + return ( + withUnderscore + + (withUnderscore ? s.slice(1) : s) + .toLowerCase() + .replace(/([-_][a-z])/g, (group) => + group.toUpperCase().replace("-", "").replace("_", ""), + ) + ); } function upperFirst(s: string): string { const withUnderscore = s.charAt(0) === "_" ? s.charAt(0) : ""; - return withUnderscore + + return ( + withUnderscore + s.charAt(withUnderscore ? 1 : 0).toUpperCase() + - s.slice((withUnderscore ? 1 : 0) + 1); + s.slice((withUnderscore ? 1 : 0) + 1) + ); } } -parseBlueprint("../../../on-chain/aiken/plutus.json", "plutus.ts") \ No newline at end of file +parseBlueprint("../../../on-chain/aiken/plutus.json", "plutus.ts"); diff --git a/bun.lockb b/bun.lockb index 3251467..f2043d5 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/src/provider/blockfrost.ts b/src/provider/blockfrost.ts index 4eee294..e8aed1d 100644 --- a/src/provider/blockfrost.ts +++ b/src/provider/blockfrost.ts @@ -1,5 +1,5 @@ import { C } from "../core/mod.ts"; -import * as mathjs from "mathjs" +import * as mathjs from "mathjs"; import { applyDoubleCborEncoding, fromHex, toHex } from "../utils/mod.ts"; import { @@ -20,7 +20,6 @@ import { import packageJson from "../../package.json" assert { type: "json" }; import { M } from "translucent-cardano"; - export class Blockfrost implements Provider { url: string; projectId: string; @@ -31,16 +30,16 @@ export class Blockfrost implements Provider { } async getProtocolParameters(): Promise { - const result: Response | unknown = await fetch(`${this.url}/epochs/latest/parameters`, { - headers: { project_id: this.projectId, translucent }, - }).then((res) => res.json()); + const result: Response | unknown = await fetch( + `${this.url}/epochs/latest/parameters`, + { + headers: { project_id: this.projectId, translucent }, + }, + ).then((res) => res.json()); - const fraction = (str: string): [bigint, bigint] => { - const fr = mathjs.fraction(str) - return [ - BigInt(fr.n), - BigInt(fr.d), - ]; + const fraction = (str: string): [bigint, bigint] => { + const fr = mathjs.fraction(str); + return [BigInt(fr.n), BigInt(fr.d)]; }; return { minFeeA: parseInt(result.min_fee_a), @@ -63,13 +62,14 @@ export class Blockfrost implements Provider { async getUtxos(addressOrCredential: Address | Credential): Promise { const queryPredicate = (() => { if (typeof addressOrCredential === "string") return addressOrCredential; - const credentialBech32 = addressOrCredential.type === "Key" - ? C.Ed25519KeyHash.from_hex(addressOrCredential.hash).to_bech32( - "addr_vkh", - ) - : C.ScriptHash.from_hex(addressOrCredential.hash).to_bech32( - "addr_vkh", - ); // should be 'script' (CIP-0005) + const credentialBech32 = + addressOrCredential.type === "Key" + ? C.Ed25519KeyHash.from_hex(addressOrCredential.hash).to_bech32( + "addr_vkh", + ) + : C.ScriptHash.from_hex(addressOrCredential.hash).to_bech32( + "addr_vkh", + ); // should be 'script' (CIP-0005) return credentialBech32; })(); let result: BlockfrostUtxoResult = []; @@ -101,13 +101,14 @@ export class Blockfrost implements Provider { ): Promise { const queryPredicate = (() => { if (typeof addressOrCredential === "string") return addressOrCredential; - const credentialBech32 = addressOrCredential.type === "Key" - ? C.Ed25519KeyHash.from_hex(addressOrCredential.hash).to_bech32( - "addr_vkh", - ) - : C.ScriptHash.from_hex(addressOrCredential.hash).to_bech32( - "addr_vkh", - ); // should be 'script' (CIP-0005) + const credentialBech32 = + addressOrCredential.type === "Key" + ? C.Ed25519KeyHash.from_hex(addressOrCredential.hash).to_bech32( + "addr_vkh", + ) + : C.ScriptHash.from_hex(addressOrCredential.hash).to_bech32( + "addr_vkh", + ); // should be 'script' (CIP-0005) return credentialBech32; })(); let result: BlockfrostUtxoResult = []; @@ -160,36 +161,42 @@ export class Blockfrost implements Provider { async getUtxosByOutRef(outRefs: OutRef[]): Promise { // TODO: Make sure old already spent UTxOs are not retrievable. const queryHashes = [...new Set(outRefs.map((outRef) => outRef.txHash))]; - const utxos = await Promise.all(queryHashes.map(async (txHash) => { - const result = await fetch( - `${this.url}/txs/${txHash}/utxos`, - { headers: { project_id: this.projectId, translucent } }, - ).then((res) => res.json()); - if (!result || result.error) { - return []; - } - const utxosResult: BlockfrostUtxoResult = result.outputs.map(( - // deno-lint-ignore no-explicit-any - r: any, - ) => ({ - ...r, - tx_hash: txHash, - })); - return this.blockfrostUtxosToUtxos(utxosResult); - })); - - return utxos.reduce((acc, utxos) => acc.concat(utxos), []).filter((utxo) => - outRefs.some((outRef) => - utxo.txHash === outRef.txHash && utxo.outputIndex === outRef.outputIndex - ) + const utxos = await Promise.all( + queryHashes.map(async (txHash) => { + const result = await fetch(`${this.url}/txs/${txHash}/utxos`, { + headers: { project_id: this.projectId, translucent }, + }).then((res) => res.json()); + if (!result || result.error) { + return []; + } + const utxosResult: BlockfrostUtxoResult = result.outputs.map( + ( + // deno-lint-ignore no-explicit-any + r: any, + ) => ({ + ...r, + tx_hash: txHash, + }), + ); + return this.blockfrostUtxosToUtxos(utxosResult); + }), ); + + return utxos + .reduce((acc, utxos) => acc.concat(utxos), []) + .filter((utxo) => + outRefs.some( + (outRef) => + utxo.txHash === outRef.txHash && + utxo.outputIndex === outRef.outputIndex, + ), + ); } async getDelegation(rewardAddress: RewardAddress): Promise { - const result = await fetch( - `${this.url}/accounts/${rewardAddress}`, - { headers: { project_id: this.projectId, translucent } }, - ).then((res) => res.json()); + const result = await fetch(`${this.url}/accounts/${rewardAddress}`, { + headers: { project_id: this.projectId, translucent }, + }).then((res) => res.json()); if (!result || result.error) { return { poolId: null, rewards: 0n }; } @@ -200,12 +207,9 @@ export class Blockfrost implements Provider { } async getDatum(datumHash: DatumHash): Promise { - const datum = await fetch( - `${this.url}/scripts/datum/${datumHash}/cbor`, - { - headers: { project_id: this.projectId, translucent }, - }, - ) + const datum = await fetch(`${this.url}/scripts/datum/${datumHash}/cbor`, { + headers: { project_id: this.projectId, translucent }, + }) .then((res) => res.json()) .then((res) => res.cbor); if (!datum || datum.error) { @@ -260,28 +264,26 @@ export class Blockfrost implements Provider { datumHash: (!r.inline_datum && r.data_hash) || undefined, datum: r.inline_datum || undefined, scriptRef: r.reference_script_hash - ? (await (async () => { - const { - type, - } = await fetch( - `${this.url}/scripts/${r.reference_script_hash}`, - { - headers: { project_id: this.projectId, translucent }, - }, - ).then((res) => res.json()); - // TODO: support native scripts - if (type === "Native" || type === "native") { - throw new Error("Native script ref not implemented!"); - } - const { cbor: script } = await fetch( - `${this.url}/scripts/${r.reference_script_hash}/cbor`, - { headers: { project_id: this.projectId, translucent } }, - ).then((res) => res.json()); - return { - type: type === "plutusV1" ? "PlutusV1" : "PlutusV2", - script: applyDoubleCborEncoding(script), - }; - })()) + ? await (async () => { + const { type } = await fetch( + `${this.url}/scripts/${r.reference_script_hash}`, + { + headers: { project_id: this.projectId, translucent }, + }, + ).then((res) => res.json()); + // TODO: support native scripts + if (type === "Native" || type === "native") { + throw new Error("Native script ref not implemented!"); + } + const { cbor: script } = await fetch( + `${this.url}/scripts/${r.reference_script_hash}/cbor`, + { headers: { project_id: this.projectId, translucent } }, + ).then((res) => res.json()); + return { + type: type === "plutusV1" ? "PlutusV1" : "PlutusV2", + script: applyDoubleCborEncoding(script), + }; + })() : undefined, })), )) as UTxO[]; @@ -353,4 +355,3 @@ type BlockfrostUtxoError = { }; const translucent = packageJson.version; // Translucent version -