From ef467bcbf49c975e8c552f0c8e177365089126f4 Mon Sep 17 00:00:00 2001 From: Thomas van Dam Date: Tue, 24 Sep 2024 10:28:52 +0200 Subject: [PATCH] feat(dev-tools): add support for DR bundle methods --- libs/dev-tools/src/index.ts | 5 ++ .../services/dr/await-data-result-bundle.ts | 41 +++++++++++ .../src/lib/services/dr/await-data-result.ts | 2 +- .../dr/get-data-request-bundle-status.ts | 31 +++++++++ .../services/dr/get-data-request-status.ts | 6 +- .../lib/services/dr/get-data-result-bundle.ts | 28 ++++++++ .../src/lib/services/dr/get-data-result.ts | 4 +- .../dr/post-and-await-data-request-bundle.ts | 28 ++++++++ .../services/dr/post-data-request-bundle.ts | 68 +++++++++++++++++++ 9 files changed, 210 insertions(+), 3 deletions(-) create mode 100644 libs/dev-tools/src/lib/services/dr/await-data-result-bundle.ts create mode 100644 libs/dev-tools/src/lib/services/dr/get-data-request-bundle-status.ts create mode 100644 libs/dev-tools/src/lib/services/dr/get-data-result-bundle.ts create mode 100644 libs/dev-tools/src/lib/services/dr/post-and-await-data-request-bundle.ts create mode 100644 libs/dev-tools/src/lib/services/dr/post-data-request-bundle.ts diff --git a/libs/dev-tools/src/index.ts b/libs/dev-tools/src/index.ts index 8c1ff38..eeec7da 100644 --- a/libs/dev-tools/src/index.ts +++ b/libs/dev-tools/src/index.ts @@ -5,10 +5,15 @@ export { uploadWasmBinary } from "@dev-tools/services/wasm/upload-wasm-binary"; export { getWasmBinary } from "@dev-tools/services/wasm/get-wasm-binary"; export { postDataRequest } from "@dev-tools/services/dr/post-data-request"; +export { postDataRequestBundle } from "@dev-tools/services/dr/post-data-request-bundle"; export { getDataRequestStatus } from "@dev-tools/services/dr/get-data-request-status"; +export { getDataRequestBundleStatus } from "@dev-tools/services/dr/get-data-request-bundle-status"; export { awaitDataResult } from "@dev-tools/services/dr/await-data-result"; +export { awaitDataResultBundle } from "@dev-tools/services/dr/await-data-result-bundle"; export { getDataResult } from "@dev-tools/services/dr/get-data-result"; +export { getDataResultBundle } from "@dev-tools/services/dr/get-data-result-bundle"; export { postAndAwaitDataRequest } from "@dev-tools/services/dr/post-and-await-data-request"; +export { postAndAwaitDataRequestBundle } from "@dev-tools/services/dr/post-and-await-data-request-bundle"; export * from "./lib/testing/create-mock-reveal"; export * from "./lib/testing/create-mock-tally-args"; diff --git a/libs/dev-tools/src/lib/services/dr/await-data-result-bundle.ts b/libs/dev-tools/src/lib/services/dr/await-data-result-bundle.ts new file mode 100644 index 0000000..9e2d22b --- /dev/null +++ b/libs/dev-tools/src/lib/services/dr/await-data-result-bundle.ts @@ -0,0 +1,41 @@ +import type { ISigner } from "@dev-tools/services/signer"; +import { createSigningClient } from "@dev-tools/services/signing-client"; +import { tryAsync } from "@dev-tools/utils/try-async"; +import { getDataResultBundle } from "./get-data-result-bundle"; + +type Opts = { + /** Defaults to 60 seconds. */ + timeoutSeconds: number; + /** Defaults to 10 seconds */ + pollingIntervalSeconds: number; +}; + +export async function awaitDataResultBundle( + signer: ISigner, + drIds: string[], + opts: Opts = { timeoutSeconds: 60, pollingIntervalSeconds: 10 }, +) { + const sigingClientResult = await createSigningClient(signer); + if (sigingClientResult.isErr) { + throw sigingClientResult.error; + } + const timeoutTime = Date.now() + opts.timeoutSeconds * 1000; + + while (Date.now() < timeoutTime) { + const result = await tryAsync(async () => + getDataResultBundle(signer, drIds), + ); + if (!result.isErr && result.value.every((r) => r.result !== null)) { + return result.value; + } + await sleep(opts.pollingIntervalSeconds * 1000); + } + + throw new Error( + `Timeout: bundled data requests took longer than ${opts.timeoutSeconds} seconds to execute.`, + ); +} + +export function sleep(durationMs: number) { + return new Promise((resolve) => setTimeout(resolve, durationMs)); +} diff --git a/libs/dev-tools/src/lib/services/dr/await-data-result.ts b/libs/dev-tools/src/lib/services/dr/await-data-result.ts index 3a46fa3..57b5843 100644 --- a/libs/dev-tools/src/lib/services/dr/await-data-result.ts +++ b/libs/dev-tools/src/lib/services/dr/await-data-result.ts @@ -29,7 +29,7 @@ export async function awaitDataResult( await sleep(opts.pollingIntervalSeconds * 1000); } - return new Error( + throw new Error( `Timeout: DR "${drId}" took longer than ${opts.timeoutSeconds} seconds to execute.`, ); } diff --git a/libs/dev-tools/src/lib/services/dr/get-data-request-bundle-status.ts b/libs/dev-tools/src/lib/services/dr/get-data-request-bundle-status.ts new file mode 100644 index 0000000..ae1d87c --- /dev/null +++ b/libs/dev-tools/src/lib/services/dr/get-data-request-bundle-status.ts @@ -0,0 +1,31 @@ +import { tryAsync } from "@dev-tools/utils/try-async"; +import type { ISigner } from "../signer"; +import { + type DataRequestStatus, + getDataRequestStatus, +} from "./get-data-request-status"; + +type StatusBatchResponse = { + drId: string; + status: DataRequestStatus | null; + error?: string; +}; + +export async function getDataRequestBundleStatus( + signer: ISigner, + drIds: string[], +): Promise { + return Promise.all( + drIds.map(async (drId) => { + const status = await tryAsync(() => getDataRequestStatus(signer, drId)); + return status.mapOrElse( + (error) => { + return { drId, status: null, error }; + }, + ({ status }) => { + return { drId, status, error: undefined }; + }, + ); + }), + ); +} diff --git a/libs/dev-tools/src/lib/services/dr/get-data-request-status.ts b/libs/dev-tools/src/lib/services/dr/get-data-request-status.ts index bf07d3f..13385f2 100644 --- a/libs/dev-tools/src/lib/services/dr/get-data-request-status.ts +++ b/libs/dev-tools/src/lib/services/dr/get-data-request-status.ts @@ -2,7 +2,11 @@ import assert from "node:assert"; import type { ISigner } from "../signer"; import { createSigningClient } from "../signing-client"; -type DataRequestStatus = "pending" | "committing" | "revealing" | "resolved"; +export type DataRequestStatus = + | "pending" + | "committing" + | "revealing" + | "resolved"; export async function getDataRequestStatus( signer: ISigner, diff --git a/libs/dev-tools/src/lib/services/dr/get-data-result-bundle.ts b/libs/dev-tools/src/lib/services/dr/get-data-result-bundle.ts new file mode 100644 index 0000000..ab5ae69 --- /dev/null +++ b/libs/dev-tools/src/lib/services/dr/get-data-result-bundle.ts @@ -0,0 +1,28 @@ +import { tryAsync } from "@dev-tools/utils/try-async"; +import type { ISigner } from "../signer"; +import { type DataRequestResult, getDataResult } from "./get-data-result"; + +type ResultBatchResponse = { + drId: string; + result: DataRequestResult | null; + error?: string; +}; + +export async function getDataResultBundle( + signer: ISigner, + drIds: string[], +): Promise { + return Promise.all( + drIds.map(async (drId) => { + const status = await tryAsync(() => getDataResult(signer, drId)); + return status.mapOrElse( + (error) => { + return { drId, result: null, error }; + }, + (result) => { + return { drId, result, error: undefined }; + }, + ); + }), + ); +} diff --git a/libs/dev-tools/src/lib/services/dr/get-data-result.ts b/libs/dev-tools/src/lib/services/dr/get-data-result.ts index 46b26ad..a920d64 100644 --- a/libs/dev-tools/src/lib/services/dr/get-data-result.ts +++ b/libs/dev-tools/src/lib/services/dr/get-data-result.ts @@ -41,10 +41,12 @@ const DataResultSchema = pipe( }), ); +export type DataRequestResult = InferOutput; + export async function getDataResult( signer: ISigner, drId: string, -): Promise> { +): Promise { const sigingClientResult = await createSigningClient(signer); if (sigingClientResult.isErr) { throw sigingClientResult.error; diff --git a/libs/dev-tools/src/lib/services/dr/post-and-await-data-request-bundle.ts b/libs/dev-tools/src/lib/services/dr/post-and-await-data-request-bundle.ts new file mode 100644 index 0000000..6a37f4b --- /dev/null +++ b/libs/dev-tools/src/lib/services/dr/post-and-await-data-request-bundle.ts @@ -0,0 +1,28 @@ +import type { GasOptions } from "@dev-tools/services/gas-options"; +import type { ISigner } from "@dev-tools/services/signer"; +import { awaitDataResultBundle } from "./await-data-result-bundle"; +import type { PostDataRequestInput } from "./create-dr-input"; +import { postDataRequestBundle } from "./post-data-request-bundle"; + +type AwaitOptions = Parameters["2"]; + +export async function postAndAwaitDataRequestBundle( + signer: ISigner, + dataRequestInputs: PostDataRequestInput[], + gasOptions?: GasOptions, + awaitOptions?: AwaitOptions, +) { + const postDrResponse = await postDataRequestBundle( + signer, + dataRequestInputs, + gasOptions, + ); + + const dataResults = await awaitDataResultBundle( + signer, + postDrResponse.drIds, + awaitOptions, + ); + + return dataResults; +} diff --git a/libs/dev-tools/src/lib/services/dr/post-data-request-bundle.ts b/libs/dev-tools/src/lib/services/dr/post-data-request-bundle.ts new file mode 100644 index 0000000..1a31a34 --- /dev/null +++ b/libs/dev-tools/src/lib/services/dr/post-data-request-bundle.ts @@ -0,0 +1,68 @@ +import type { GasOptions } from "../gas-options"; +import { signAndSendTx } from "../sign-and-send-tx"; +import type { ISigner } from "../signer"; +import { createSigningClient } from "../signing-client"; +import { + type PostDataRequestInput, + createPostedDataRequest, +} from "./create-dr-input"; + +export async function postDataRequestBundle( + signer: ISigner, + dataRequestInputs: PostDataRequestInput[], + gasOptions?: GasOptions, +): Promise<{ tx: string; drIds: string[] }> { + const sigingClientResult = await createSigningClient(signer); + if (sigingClientResult.isErr) { + throw sigingClientResult.error; + } + + const contract = signer.getCoreContractAddress(); + + const { client: sigingClient, address } = sigingClientResult.value; + + const messages = dataRequestInputs.map((dataRequestInput) => { + return { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: { + sender: address, + contract, + msg: Buffer.from( + JSON.stringify({ + post_data_request: { + seda_payload: Buffer.from([]).toString("base64"), + payback_address: Buffer.from("seda_sdk").toString("base64"), + posted_dr: createPostedDataRequest(dataRequestInput), + }, + }), + ), + }, + }; + }); + + const response = await signAndSendTx( + sigingClient, + address, + messages, + gasOptions, + ); + + if (response.isErr) { + throw response.error; + } + + if (response.value.code === 1) { + throw new Error(`TX failed: "${response.value.transactionHash}"`); + } + + const drIds = response.value.msgResponses.map((messageResponseRaw) => { + const messageResponse = sigingClient.registry.decode(messageResponseRaw); + + return JSON.parse(Buffer.from(messageResponse.data).toString()); + }); + + return { + tx: response.value.transactionHash, + drIds, + }; +}