From 093b6d5180d466d396fcd07582a52a1a00f34a11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Diamond?= <32074058+Andre-Diamond@users.noreply.github.com> Date: Fri, 24 Apr 2026 18:05:04 +0200 Subject: [PATCH] feat: update DRep certificate handling to compute anchor data hash and refactor anchor URL resolution --- scripts/ci/scenarios/steps/certificates.ts | 14 ++++++++++---- src/pages/api/v1/botDRepCertificate.ts | 20 +++++--------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/scripts/ci/scenarios/steps/certificates.ts b/scripts/ci/scenarios/steps/certificates.ts index d308500..267fdc0 100644 --- a/scripts/ci/scenarios/steps/certificates.ts +++ b/scripts/ci/scenarios/steps/certificates.ts @@ -6,6 +6,7 @@ import { getDefaultBot } from "../../framework/botContext"; import { authenticateBot } from "../../framework/botAuth"; import { stringifyRedacted } from "../../framework/redact"; import { boolFromEnv } from "../../framework/env"; +import { hashDrepAnchor } from "@meshsdk/core"; type ScriptUtxo = { input: { txHash: string; outputIndex: number }; @@ -165,7 +166,7 @@ function createCertPhaseSteps(args: { label: string; runtime: { transactionId?: string; spentUtxoRefs?: { txHash: string; outputIndex: number }[] }; requireBroadcastSuccess: boolean; - buildExtraBody?: (ctx: CIBootstrapContext) => Record; + buildExtraBody?: (ctx: CIBootstrapContext) => Promise> | Record; /** When true, each signing step uses the stake-cert flow (payment + stake witnesses). */ useStakeCertFlow?: boolean; }): RouteStep[] { @@ -192,13 +193,14 @@ function createCertPhaseSteps(args: { fresh: true, }); + const extraBody = args.buildExtraBody ? await args.buildExtraBody(ctx) : {}; const body: Record = { walletId: wallet.walletId, address: bot.paymentAddress, action, utxoRefs, description: label, - ...(args.buildExtraBody?.(ctx) ?? {}), + ...extraBody, }; const response = await requestJson<{ id?: string; error?: string }>({ @@ -504,12 +506,16 @@ export function createScenarioDRepCertificates(): Scenario { const sdkReg: { transactionId?: string; spentUtxoRefs?: { txHash: string; outputIndex: number }[] } = {}; const sdkRetire: { transactionId?: string; spentUtxoRefs?: { txHash: string; outputIndex: number }[] } = {}; - function buildDRepRegBody(): Record { + async function buildDRepRegBody(): Promise> { const anchorUrl = process.env.CI_DREP_ANCHOR_URL?.trim(); if (!anchorUrl) { throw new Error("CI_DREP_ANCHOR_URL is required for DRep registration"); } - return { anchorUrl }; + const res = await fetch(anchorUrl); + if (!res.ok) throw new Error(`Failed to fetch DRep anchor URL: HTTP ${res.status}`); + const json = await res.json() as object; + const anchorDataHash = hashDrepAnchor(json); + return { anchorUrl, anchorDataHash }; } return { diff --git a/src/pages/api/v1/botDRepCertificate.ts b/src/pages/api/v1/botDRepCertificate.ts index 795d677..9eb2104 100644 --- a/src/pages/api/v1/botDRepCertificate.ts +++ b/src/pages/api/v1/botDRepCertificate.ts @@ -13,7 +13,6 @@ import { getTxBuilder } from "@/utils/get-tx-builder"; import { resolveWalletScriptAddress } from "@/lib/server/walletScriptAddress"; import { resolveUtxoRefsFromChain } from "@/lib/server/resolveUtxoRefsFromChain"; import { createPendingMultisigTransaction } from "@/lib/server/createPendingMultisigTransaction"; -import { resolveDRepAnchorFromUrl } from "@/lib/server/resolveDRepAnchorFromUrl"; import type { DbWalletWithLegacy } from "@/types/wallet"; import type { Wallet as AppWallet } from "@/types/wallet"; import type { MultisigWallet } from "@/utils/multisigSDK"; @@ -177,19 +176,10 @@ export default async function handler( if (!anchorUrl) { return res.status(400).json({ error: "anchorUrl is required for register" }); } - let resolvedAnchorUrl: string; - let anchorDataHash: string; - try { - const r = await resolveDRepAnchorFromUrl( - anchorUrl, - typeof body.anchorDataHash === "string" ? body.anchorDataHash : undefined, - ); - resolvedAnchorUrl = r.anchorUrl; - anchorDataHash = r.anchorDataHash; - } catch (e) { - return res.status(400).json({ - error: e instanceof Error ? e.message : "Failed to resolve anchor", - }); + const anchorDataHash = + typeof body.anchorDataHash === "string" ? body.anchorDataHash.trim() : ""; + if (!anchorDataHash) { + return res.status(400).json({ error: "anchorDataHash is required for register — compute it from the anchor JSON before calling this endpoint" }); } for (const utxo of utxos) { @@ -204,7 +194,7 @@ export default async function handler( txBuilder .drepRegistrationCertificate(dRepId, { - anchorUrl: resolvedAnchorUrl, + anchorUrl, anchorDataHash, }) .certificateScript(drepCbor)