shared: opt-in CIP-33 reference-scripts for script-spending txs
When ESCROW_REF_UTXO / ADVERT_REF_UTXO env vars are set (format <txhash>#<idx>), Claim/Submit/Accept/Reclaim/RetireAdvert call .readFrom([refUtxo]) instead of inlining the validator CBOR via .attach.SpendingValidator(). Lucid-evolution 0.4.30 auto-extracts utxo.scriptRef into the script-witness map (dist/index.js:1903–1913), so no separate attach call is needed. Default (env unset) is unchanged. Adds buildLiveTxForPublishReferenceScripts (a one-shot tx that publishes both validators as scriptRef-bearing UTxOs at a caller- supplied address — typically the all-zero-pkh enterprise burn address) and the supplier tx:publish-reference-scripts CLI that wraps it. Targeted at testnet trial; mainnet rollout is a separate flip after verification. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,15 @@ BUYER_PORT=8070
|
||||
# See ARCHITECTURE.md §9 follow-up — naming drift to be reconciled.)
|
||||
NETWORK_ID=0
|
||||
|
||||
# ─── Optional: CIP-33 reference scripts ───────────────────────────────────
|
||||
# When set, buyer-signed script-spending txs (Accept, Reclaim) read the
|
||||
# escrow validator from an on-chain reference UTxO instead of inlining
|
||||
# its CBOR — saving witness-set fees. Must match the values published
|
||||
# by `supplier tx:publish-reference-scripts` (printed to stdout by that
|
||||
# CLI). Leave unset to use inlined scripts (current behavior).
|
||||
# ESCROW_REF_UTXO=<txhash>#0
|
||||
# ADVERT_REF_UTXO=<txhash>#1 # not used by buyer; kept for env-file symmetry
|
||||
|
||||
# ─── Reserved for M1-F ────────────────────────────────────────────────────
|
||||
# INDEXER_TIMEOUT_MS is referenced in deploy/README.md but is NOT yet read
|
||||
# by buyer/src/config.ts. M1-F should add it; see follow-ups.
|
||||
|
||||
@@ -10,6 +10,14 @@ export type { Signer } from "./signer.js";
|
||||
export { MockSigner } from "./signer.js";
|
||||
export type { Blueprint } from "./blueprint.js";
|
||||
export { loadBlueprint } from "./blueprint.js";
|
||||
// Script-bytes loaders (used by the publish-reference-scripts CLI).
|
||||
export { loadEscrowScript, loadAdvertScript } from "./internal/liveCbor.js";
|
||||
export { pkhToEnterpriseAddress } from "./internal/pkhAddress.js";
|
||||
export type {
|
||||
LivePublishRefScriptsParams,
|
||||
PublishRefScriptsBuildResult,
|
||||
} from "./internal/publishReferenceScripts.js";
|
||||
export { buildLiveTxForPublishReferenceScripts } from "./internal/publishReferenceScripts.js";
|
||||
|
||||
// Advert builders
|
||||
export type { PostAdvertParams } from "./advert/postAdvert.js";
|
||||
|
||||
@@ -20,7 +20,7 @@ import { readFileSync } from "fs";
|
||||
import { dirname, resolve } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
import type { UTxO as LucidUTxO, Script } from "@lucid-evolution/lucid";
|
||||
import type { LucidEvolution, UTxO as LucidUTxO, Script } from "@lucid-evolution/lucid";
|
||||
|
||||
import type { OutputReference, Utxo } from "../../chain/ChainProvider.js";
|
||||
import type { LiveOgmiosProvider } from "../../chain/LiveOgmiosProvider.js";
|
||||
@@ -167,8 +167,10 @@ function buildLucidProvider(chain: LiveOgmiosProvider): OgmiosLucidProvider {
|
||||
});
|
||||
}
|
||||
|
||||
/** Load the escrow spending validator script bytes from the blueprint. */
|
||||
function loadEscrowScript(): { script: Script; address: string } {
|
||||
/** Load the escrow spending validator script bytes from the blueprint.
|
||||
* Exported so the publish-reference-scripts CLI can publish the same bytes
|
||||
* under a CIP-33 scriptRef without duplicating plutus.json reading. */
|
||||
export function loadEscrowScript(): { script: Script; address: string } {
|
||||
const blueprint = loadBlueprint();
|
||||
// Read plutus.json directly to grab compiledCode (the blueprint loader only
|
||||
// exposes the hash). We piggyback on the same path resolution.
|
||||
@@ -199,8 +201,9 @@ function readEscrowCompiledCode(): string {
|
||||
throw new Error("loadEscrowScript: plutus.json missing escrow validator compiledCode");
|
||||
}
|
||||
|
||||
/** Load the advert spending validator script bytes from the blueprint. */
|
||||
function loadAdvertScript(): { script: Script; address: string } {
|
||||
/** Load the advert spending validator script bytes from the blueprint.
|
||||
* Exported for the same reason as loadEscrowScript above. */
|
||||
export function loadAdvertScript(): { script: Script; address: string } {
|
||||
const blueprint = loadBlueprint();
|
||||
const compiled = readAdvertCompiledCode();
|
||||
const script: Script = { type: "PlutusV3", script: compiled };
|
||||
@@ -229,6 +232,48 @@ function readAdvertCompiledCode(): string {
|
||||
throw new Error("loadAdvertScript: plutus.json missing advert validator compiledCode");
|
||||
}
|
||||
|
||||
/** Parse a `<txhash>#<idx>` env var into an OutRef. Returns null if unset. */
|
||||
function parseRefUtxoEnv(envKey: string): { txHash: string; outputIndex: number } | null {
|
||||
const v = process.env[envKey];
|
||||
if (!v) return null;
|
||||
const [txHash, idxStr] = v.split("#");
|
||||
if (!txHash || !idxStr) {
|
||||
throw new Error(`${envKey} must be <txhash>#<idx>, got: ${v}`);
|
||||
}
|
||||
const outputIndex = Number(idxStr);
|
||||
if (!Number.isInteger(outputIndex) || outputIndex < 0) {
|
||||
throw new Error(`${envKey} index must be a non-negative integer, got: ${idxStr}`);
|
||||
}
|
||||
return { txHash, outputIndex };
|
||||
}
|
||||
|
||||
/** Resolve script-witness mode for a spending tx.
|
||||
* - If `<envKey>` is set, fetch the referenced UTxO and use CIP-33 reference-script mode.
|
||||
* The caller threads the UTxO into `tx.readFrom([refUtxo])`; lucid-evolution 0.4.30
|
||||
* auto-extracts `refUtxo.scriptRef` and registers it as a witness
|
||||
* (dist/index.js:1903–1913). No `attach.SpendingValidator` call is needed.
|
||||
* - Otherwise, fall back to the inlined script (current behavior). */
|
||||
async function resolveSpendCtx(
|
||||
lucid: LucidEvolution,
|
||||
envKey: "ESCROW_REF_UTXO" | "ADVERT_REF_UTXO",
|
||||
inlineScript: Script,
|
||||
): Promise<{ kind: "ref"; refUtxo: LucidUTxO } | { kind: "inline"; script: Script }> {
|
||||
const refSpec = parseRefUtxoEnv(envKey);
|
||||
if (!refSpec) return { kind: "inline", script: inlineScript };
|
||||
const [refUtxo] = await lucid.utxosByOutRef([refSpec]);
|
||||
if (!refUtxo) {
|
||||
throw new Error(
|
||||
`${envKey} ${refSpec.txHash}#${refSpec.outputIndex} not found on chain`,
|
||||
);
|
||||
}
|
||||
if (!refUtxo.scriptRef) {
|
||||
throw new Error(
|
||||
`${envKey} ${refSpec.txHash}#${refSpec.outputIndex} has no scriptRef attached`,
|
||||
);
|
||||
}
|
||||
return { kind: "ref", refUtxo };
|
||||
}
|
||||
|
||||
/** Build a lucid-shaped UTxO from our chain Utxo, swapping in the live
|
||||
* blueprint's mainnet-flavoured script address so output validation passes
|
||||
* on Mainnet network even when the test fixture used a testnet header. */
|
||||
@@ -375,6 +420,7 @@ async function buildSpendOpenOrClaimedTx(opts: {
|
||||
}, { usePresetProtocolParameters: true });
|
||||
|
||||
const { script, address: escrowAddress } = loadEscrowScript();
|
||||
const spendCtx = await resolveSpendCtx(lucid, "ESCROW_REF_UTXO", script);
|
||||
const datumHex = encodeEscrowDatum(opts.newDatum);
|
||||
const redeemerHex = opts.redeemerHex;
|
||||
const lucidInput = chainUtxoToLucidScriptInput(opts.escrowUtxo, escrowAddress);
|
||||
@@ -403,9 +449,11 @@ async function buildSpendOpenOrClaimedTx(opts: {
|
||||
|
||||
let signed;
|
||||
try {
|
||||
const txBuilder = lucid
|
||||
.newTx()
|
||||
.attach.SpendingValidator(script)
|
||||
const txBuilder = (
|
||||
spendCtx.kind === "ref"
|
||||
? lucid.newTx().readFrom([spendCtx.refUtxo])
|
||||
: lucid.newTx().attach.SpendingValidator(spendCtx.script)
|
||||
)
|
||||
.collectFrom([lucidInput], redeemerHex)
|
||||
.pay.ToAddressWithData(
|
||||
escrowAddress,
|
||||
@@ -525,6 +573,7 @@ export async function buildLiveTxForAccept(params: LiveAcceptParams): Promise<Bu
|
||||
}, { usePresetProtocolParameters: true });
|
||||
|
||||
const { script, address: escrowAddress } = loadEscrowScript();
|
||||
const spendCtx = await resolveSpendCtx(lucid, "ESCROW_REF_UTXO", script);
|
||||
const lucidInput = chainUtxoToLucidScriptInput(params.escrowUtxo, escrowAddress);
|
||||
const acceptRedeemerHex = encodeNullaryConstrHex(REDEEMER_ACCEPT);
|
||||
|
||||
@@ -548,9 +597,11 @@ export async function buildLiveTxForAccept(params: LiveAcceptParams): Promise<Bu
|
||||
|
||||
let signed;
|
||||
try {
|
||||
const txBuilder = lucid
|
||||
.newTx()
|
||||
.attach.SpendingValidator(script)
|
||||
const txBuilder = (
|
||||
spendCtx.kind === "ref"
|
||||
? lucid.newTx().readFrom([spendCtx.refUtxo])
|
||||
: lucid.newTx().attach.SpendingValidator(spendCtx.script)
|
||||
)
|
||||
.collectFrom([lucidInput], acceptRedeemerHex)
|
||||
.pay.ToAddress(supplierAddress, { lovelace: supplierLovelace })
|
||||
.pay.ToAddress(buyerAddress, { lovelace: buyerLovelace })
|
||||
@@ -594,6 +645,7 @@ export async function buildLiveTxForReclaim(params: LiveReclaimParams): Promise<
|
||||
}, { usePresetProtocolParameters: true });
|
||||
|
||||
const { script, address: escrowAddress } = loadEscrowScript();
|
||||
const spendCtx = await resolveSpendCtx(lucid, "ESCROW_REF_UTXO", script);
|
||||
const lucidInput = chainUtxoToLucidScriptInput(params.escrowUtxo, escrowAddress);
|
||||
const reclaimRedeemerHex = encodeNullaryConstrHex(REDEEMER_RECLAIM);
|
||||
|
||||
@@ -610,9 +662,11 @@ export async function buildLiveTxForReclaim(params: LiveReclaimParams): Promise<
|
||||
|
||||
let signed;
|
||||
try {
|
||||
const txBuilder = lucid
|
||||
.newTx()
|
||||
.attach.SpendingValidator(script)
|
||||
const txBuilder = (
|
||||
spendCtx.kind === "ref"
|
||||
? lucid.newTx().readFrom([spendCtx.refUtxo])
|
||||
: lucid.newTx().attach.SpendingValidator(spendCtx.script)
|
||||
)
|
||||
.collectFrom([lucidInput], reclaimRedeemerHex)
|
||||
.pay.ToAddress(buyerAddress, { lovelace: buyerLovelace })
|
||||
.addSignerKey(params.buyerKey.pubKeyHash)
|
||||
@@ -717,6 +771,7 @@ export async function buildLiveTxForRetireAdvert(
|
||||
}, { usePresetProtocolParameters: true });
|
||||
|
||||
const { script, address: advertAddress } = loadAdvertScript();
|
||||
const spendCtx = await resolveSpendCtx(lucid, "ADVERT_REF_UTXO", script);
|
||||
const lucidInput = chainUtxoToLucidScriptInput(advertUtxo, advertAddress);
|
||||
const retireRedeemerHex = encodeNullaryConstrHex(REDEEMER_RETIRE_ADVERT);
|
||||
|
||||
@@ -732,9 +787,11 @@ export async function buildLiveTxForRetireAdvert(
|
||||
let signed;
|
||||
try {
|
||||
const nowMs = Date.now();
|
||||
const txBuilder = lucid
|
||||
.newTx()
|
||||
.attach.SpendingValidator(script)
|
||||
const txBuilder = (
|
||||
spendCtx.kind === "ref"
|
||||
? lucid.newTx().readFrom([spendCtx.refUtxo])
|
||||
: lucid.newTx().attach.SpendingValidator(spendCtx.script)
|
||||
)
|
||||
.collectFrom([lucidInput], retireRedeemerHex)
|
||||
.pay.ToAddress(supplierAddress, { lovelace: refundLovelace })
|
||||
.addSignerKey(walletKey.pubKeyHash)
|
||||
|
||||
107
packages/shared/src/tx/internal/publishReferenceScripts.ts
Normal file
107
packages/shared/src/tx/internal/publishReferenceScripts.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* publishReferenceScripts.ts — one-shot tx builder that publishes the escrow
|
||||
* and advert validator scripts as CIP-33 reference UTxOs.
|
||||
*
|
||||
* The two outputs are paid to a caller-supplied address (typically a known
|
||||
* burn address such as the all-zero-pkh enterprise address) with the script
|
||||
* bytes attached as `scriptRef`. Subsequent script-spending txs reference
|
||||
* these UTxOs via env (ESCROW_REF_UTXO, ADVERT_REF_UTXO) and skip inlining
|
||||
* the script bytes — saving ~5 KB per script-spending tx.
|
||||
*
|
||||
* One-shot operational tool. Invoke via supplier/src/cli/publish-reference-scripts.ts.
|
||||
*/
|
||||
|
||||
import type { Script } from "@lucid-evolution/lucid";
|
||||
import type { LiveOgmiosProvider } from "../../chain/LiveOgmiosProvider.js";
|
||||
import type { BuildResult, WalletKey } from "../types.js";
|
||||
import { TxConstructionError } from "../types.js";
|
||||
import { OgmiosLucidProvider } from "../../chain/OgmiosLucidProvider.js";
|
||||
import { createLucidContext } from "./lucidContext.js";
|
||||
|
||||
export interface LivePublishRefScriptsParams {
|
||||
chain: LiveOgmiosProvider;
|
||||
walletKey: WalletKey;
|
||||
/** Address to publish the two reference-script UTxOs to. Typically a known
|
||||
* burn address (cryptographically unspendable) so refs cannot be spent. */
|
||||
burnAddr: string;
|
||||
escrowScript: Script;
|
||||
advertScript: Script;
|
||||
/** Lovelace per output. Default 30_000_000n (30 AP3X) — covers min-UTxO for
|
||||
* a ~5 KB script (4310 lovelace/byte × 5_140 bytes ≈ 22.2 AP3X) with headroom. */
|
||||
lovelacePerOutput?: bigint;
|
||||
}
|
||||
|
||||
export interface PublishRefScriptsBuildResult extends BuildResult {
|
||||
/** Output index 0 — escrow script reference. */
|
||||
escrowRef: { txHash: string; outputIndex: number };
|
||||
/** Output index 1 — advert script reference. */
|
||||
advertRef: { txHash: string; outputIndex: number };
|
||||
formattedEscrowRef: string;
|
||||
formattedAdvertRef: string;
|
||||
}
|
||||
|
||||
const DEFAULT_LOVELACE_PER_OUTPUT = 30_000_000n;
|
||||
|
||||
export async function buildLiveTxForPublishReferenceScripts(
|
||||
params: LivePublishRefScriptsParams,
|
||||
): Promise<PublishRefScriptsBuildResult> {
|
||||
const { chain, walletKey, burnAddr, escrowScript, advertScript } = params;
|
||||
const lovelacePerOutput = params.lovelacePerOutput ?? DEFAULT_LOVELACE_PER_OUTPUT;
|
||||
|
||||
const provider = new OgmiosLucidProvider({
|
||||
ogmiosUrl: chain.url,
|
||||
fetch: chain.fetchImpl,
|
||||
});
|
||||
const { lucid } = await createLucidContext(provider, walletKey, {
|
||||
networkId: 1,
|
||||
systemStartUnix: 0,
|
||||
slotLengthMs: 1000,
|
||||
}, { usePresetProtocolParameters: true });
|
||||
|
||||
const realWalletUtxos = await lucid.wallet().getUtxos();
|
||||
|
||||
let signed;
|
||||
try {
|
||||
const txBuilder = lucid
|
||||
.newTx()
|
||||
// Output 0: escrow script reference.
|
||||
.pay.ToAddressWithData(
|
||||
burnAddr,
|
||||
undefined,
|
||||
{ lovelace: lovelacePerOutput },
|
||||
escrowScript,
|
||||
)
|
||||
// Output 1: advert script reference.
|
||||
.pay.ToAddressWithData(
|
||||
burnAddr,
|
||||
undefined,
|
||||
{ lovelace: lovelacePerOutput },
|
||||
advertScript,
|
||||
)
|
||||
.addSignerKey(walletKey.pubKeyHash)
|
||||
.setMinFee(500_000n);
|
||||
|
||||
const completed = await txBuilder.complete({
|
||||
presetWalletInputs: realWalletUtxos,
|
||||
localUPLCEval: false,
|
||||
});
|
||||
signed = await completed.sign.withWallet().complete();
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
throw new TxConstructionError(`publish-reference-scripts build failed: ${msg}`);
|
||||
}
|
||||
|
||||
const txCborHex = signed.toCBOR();
|
||||
const expectedTxHash = signed.toHash();
|
||||
|
||||
await chain.submitTx(txCborHex);
|
||||
|
||||
return {
|
||||
txCborHex,
|
||||
expectedTxHash,
|
||||
escrowRef: { txHash: expectedTxHash, outputIndex: 0 },
|
||||
advertRef: { txHash: expectedTxHash, outputIndex: 1 },
|
||||
formattedEscrowRef: `${expectedTxHash}#0`,
|
||||
formattedAdvertRef: `${expectedTxHash}#1`,
|
||||
};
|
||||
}
|
||||
@@ -96,3 +96,19 @@ ADVERT_DETAIL_URI=
|
||||
# If empty, defaults to sha256("") =
|
||||
# e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||
ADVERT_DETAIL_HASH=
|
||||
|
||||
# ─── Optional: CIP-33 reference scripts ───────────────────────────────────
|
||||
# Saves witness-set fee on script-spending txs (Claim, Submit, Accept,
|
||||
# Reclaim, RetireAdvert) by referencing on-chain script UTxOs instead of
|
||||
# inlining the full validator CBOR. Leave unset to use inlined scripts
|
||||
# (current behavior).
|
||||
#
|
||||
# Publish the two reference UTxOs (one-shot; uses SUPPLIER_PRIV_KEY_HEX +
|
||||
# OGMIOS_URL + NETWORK_ID from this env file):
|
||||
# docker compose -f deploy/testnet/docker-compose.supplier.yml run --rm \
|
||||
# --entrypoint sh supplier \
|
||||
# -c "node_modules/.bin/tsx supplier/src/cli/publish-reference-scripts.ts"
|
||||
# Paste the two printed refs here AND into buyer/.env, then
|
||||
# `docker compose down && up -d` to apply.
|
||||
# ESCROW_REF_UTXO=<txhash>#0
|
||||
# ADVERT_REF_UTXO=<txhash>#1
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit",
|
||||
"tx:post-advert": "tsx src/cli/post-advert.ts",
|
||||
"tx:retire-advert": "tsx src/cli/retire-advert.ts"
|
||||
"tx:retire-advert": "tsx src/cli/retire-advert.ts",
|
||||
"tx:publish-reference-scripts": "tsx src/cli/publish-reference-scripts.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@marketplace/shared": "workspace:*",
|
||||
|
||||
135
supplier/src/cli/publish-reference-scripts.ts
Normal file
135
supplier/src/cli/publish-reference-scripts.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* publish-reference-scripts.ts — CLI entry point for
|
||||
* `pnpm --filter @marketplace/supplier tx:publish-reference-scripts`.
|
||||
*
|
||||
* Publishes the escrow + advert validator scripts as CIP-33 reference UTxOs
|
||||
* at a known burn address (header 0x60 testnet / 0x61 mainnet, all-zero pkh).
|
||||
* After this tx confirms, set the printed refs in supplier/.env + buyer/.env:
|
||||
* ESCROW_REF_UTXO=<txhash>#0
|
||||
* ADVERT_REF_UTXO=<txhash>#1
|
||||
*
|
||||
* Reads env (no flags):
|
||||
* SUPPLIER_PRIV_KEY_HEX 64-hex Ed25519 private key
|
||||
* OGMIOS_URL https:// or wss:// endpoint (HTTPS preferred)
|
||||
* NETWORK_ID "0" (testnet) or "1" (mainnet)
|
||||
* VECTOR_ZERO_TIME_MS optional, mainnet override; defaults to testnet genesis
|
||||
*
|
||||
* stdout (success):
|
||||
* ESCROW_REF_UTXO=<txhash>#0
|
||||
* ADVERT_REF_UTXO=<txhash>#1
|
||||
* stderr: progress lines.
|
||||
*/
|
||||
|
||||
import { createHash } from "crypto";
|
||||
import * as ed from "@noble/ed25519";
|
||||
import { blake2b } from "@noble/hashes/blake2b";
|
||||
import { bech32 } from "bech32";
|
||||
import { LiveOgmiosProvider } from "@marketplace/shared/chain";
|
||||
import {
|
||||
buildLiveTxForPublishReferenceScripts,
|
||||
loadEscrowScript,
|
||||
loadAdvertScript,
|
||||
pkhToEnterpriseAddress,
|
||||
} from "@marketplace/shared/tx";
|
||||
import type { WalletKey } from "@marketplace/shared/tx";
|
||||
|
||||
ed.etc.sha512Sync = (...messages: Uint8Array[]): Uint8Array => {
|
||||
const h = createHash("sha512");
|
||||
for (const m of messages) h.update(m);
|
||||
return new Uint8Array(h.digest());
|
||||
};
|
||||
|
||||
const HEX64_RE = /^[0-9a-f]{64}$/;
|
||||
const ZERO_PKH_HEX = "0".repeat(56);
|
||||
|
||||
function hexToBytes(hex: string): Uint8Array {
|
||||
const bytes = new Uint8Array(hex.length / 2);
|
||||
for (let i = 0; i < hex.length; i += 2) {
|
||||
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function bytesToHex(b: Uint8Array): string {
|
||||
let out = "";
|
||||
for (let i = 0; i < b.length; i++) out += b[i].toString(16).padStart(2, "0");
|
||||
return out;
|
||||
}
|
||||
|
||||
function deriveWalletKey(privHex: string, networkId: 0 | 1): WalletKey {
|
||||
const priv = hexToBytes(privHex);
|
||||
const pub = ed.getPublicKey(priv);
|
||||
const pubHex = bytesToHex(pub);
|
||||
const pkh = blake2b(pub, { dkLen: 28 });
|
||||
const pkhHex = bytesToHex(pkh);
|
||||
const header = networkId === 0 ? 0x60 : 0x61;
|
||||
const payload = new Uint8Array(29);
|
||||
payload[0] = header;
|
||||
payload.set(pkh, 1);
|
||||
const hrp = networkId === 0 ? "addr_test" : "addr";
|
||||
const addr = bech32.encode(hrp, bech32.toWords(payload), 1023);
|
||||
return {
|
||||
pubKeyHash: pkhHex,
|
||||
pubKeyHex: pubHex,
|
||||
privateKeyHex: privHex,
|
||||
address: addr,
|
||||
};
|
||||
}
|
||||
|
||||
export async function main(env: Record<string, string | undefined>): Promise<number> {
|
||||
const privHex = (env.SUPPLIER_PRIV_KEY_HEX ?? "").toLowerCase();
|
||||
const ogmiosUrl = env.OGMIOS_URL ?? "";
|
||||
const networkIdStr = env.NETWORK_ID ?? "";
|
||||
|
||||
if (!HEX64_RE.test(privHex)) {
|
||||
process.stderr.write("error: SUPPLIER_PRIV_KEY_HEX must be 64 lowercase hex chars\n");
|
||||
return 1;
|
||||
}
|
||||
if (!ogmiosUrl) {
|
||||
process.stderr.write("error: OGMIOS_URL is required\n");
|
||||
return 1;
|
||||
}
|
||||
if (networkIdStr !== "0" && networkIdStr !== "1") {
|
||||
process.stderr.write(`error: NETWORK_ID must be "0" or "1", got: ${networkIdStr}\n`);
|
||||
return 1;
|
||||
}
|
||||
const networkId: 0 | 1 = networkIdStr === "1" ? 1 : 0;
|
||||
|
||||
const walletKey = deriveWalletKey(privHex, networkId);
|
||||
const burnAddr = pkhToEnterpriseAddress(ZERO_PKH_HEX, networkId);
|
||||
const { script: escrowScript } = loadEscrowScript();
|
||||
const { script: advertScript } = loadAdvertScript();
|
||||
|
||||
process.stderr.write(`wallet: ${walletKey.address}\n`);
|
||||
process.stderr.write(`burn addr: ${burnAddr}\n`);
|
||||
process.stderr.write(`ogmios: ${ogmiosUrl}\n`);
|
||||
process.stderr.write(`network: ${networkId === 0 ? "testnet" : "mainnet"}\n`);
|
||||
|
||||
const chain = new LiveOgmiosProvider({ ogmiosUrl });
|
||||
|
||||
try {
|
||||
process.stderr.write(`building publish tx...\n`);
|
||||
const result = await buildLiveTxForPublishReferenceScripts({
|
||||
chain,
|
||||
walletKey,
|
||||
burnAddr,
|
||||
escrowScript,
|
||||
advertScript,
|
||||
});
|
||||
process.stderr.write(`submitted: ${result.expectedTxHash}\n`);
|
||||
process.stdout.write(`ESCROW_REF_UTXO=${result.formattedEscrowRef}\n`);
|
||||
process.stdout.write(`ADVERT_REF_UTXO=${result.formattedAdvertRef}\n`);
|
||||
return 0;
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
process.stderr.write(`error: ${msg}\n`);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
process.argv[1]?.endsWith("publish-reference-scripts.ts") ||
|
||||
process.argv[1]?.endsWith("publish-reference-scripts.js")
|
||||
) {
|
||||
main(process.env).then((code) => process.exit(code));
|
||||
}
|
||||
Reference in New Issue
Block a user