From 8fdaf93aed23b2e44ef17040e4f08586bb8daf74 Mon Sep 17 00:00:00 2001 From: Tomek Marciniak Date: Sun, 8 Sep 2024 23:14:53 +0200 Subject: [PATCH] feat(klesia-sdk): better types for results and errors --- apps/docs/src/pages/klesia/rpc.mdx | 10 +++ apps/docs/src/pages/klesia/sdk.mdx | 10 ++- apps/klesia/.env.example | 1 + apps/klesia/src/index.spec.ts | 15 ++-- apps/klesia/src/index.ts | 91 ++++++++++++++++++------ apps/klesia/src/methods/mina.spec.ts | 2 +- apps/klesia/src/methods/mina.ts | 2 +- apps/klesia/src/schema.ts | 72 ++++++++++++++++--- apps/klesia/src/utils/build-response.ts | 20 ++++-- apps/klesia/src/utils/node.ts | 9 ++- bun.lockb | Bin 325568 -> 325544 bytes packages/klesia-sdk/package.json | 3 +- packages/klesia-sdk/src/client.spec.ts | 4 +- packages/klesia-sdk/src/client.ts | 48 ++++++++++--- 14 files changed, 228 insertions(+), 59 deletions(-) diff --git a/apps/docs/src/pages/klesia/rpc.mdx b/apps/docs/src/pages/klesia/rpc.mdx index 846c60e..392c0f4 100644 --- a/apps/docs/src/pages/klesia/rpc.mdx +++ b/apps/docs/src/pages/klesia/rpc.mdx @@ -18,6 +18,12 @@ https://mainnet.klesia.palladians.xyz/api https://devnet.klesia.palladians.xyz/api ``` +### Zeko Devnet (soon™️) + +``` +https://zeko-devnet.klesia.palladians.xyz/api +``` + ## RPC Methods Below you can find the complete list of RPC methods available on Klesia. @@ -50,6 +56,10 @@ Array of strings: Returns the hash of the most recent block. +:::warning +Not supported on Zeko. +::: + --- ### mina_chainId diff --git a/apps/docs/src/pages/klesia/sdk.mdx b/apps/docs/src/pages/klesia/sdk.mdx index 11ce818..a9e0ba0 100644 --- a/apps/docs/src/pages/klesia/sdk.mdx +++ b/apps/docs/src/pages/klesia/sdk.mdx @@ -12,14 +12,20 @@ $ npm install @mina-js/klesia-sdk For now there are only [nightly builds](/klesia/sdk#nightly-builds) available. The stable version will be released soon™️. ::: +## Client Options + +- `network`: The network to connect to. One of: `mainnet`, `devnet`, `zeko-devnet`. +- `customUrl`: A custom URL to connect to in case of self-hosted RPCs. +- `throwable`: If `true`, the client will throw an error if the response contains an error. Default is `true`. + ## Usage -```typescript +```typescript twoslash import { createClient } from '@mina-js/klesia-sdk' const client = createClient({ network: 'devnet' }) -const { result } = await client.request({ +const { result } = await client.request<'mina_getTransactionCount'>({ method: 'mina_getTransactionCount', params: ['B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS'] }) diff --git a/apps/klesia/.env.example b/apps/klesia/.env.example index d45fa30..bfbef12 100644 --- a/apps/klesia/.env.example +++ b/apps/klesia/.env.example @@ -1,3 +1,4 @@ MINA_NETWORK=devnet NODE_API_DEVNET=https://api.minascan.io/node/devnet/v1/graphql NODE_API_MAINNET=https://api.minascan.io/node/mainnet/v1/graphql +NODE_API_ZEKO_DEVNET=https://devnet.zeko.io/graphql diff --git a/apps/klesia/src/index.spec.ts b/apps/klesia/src/index.spec.ts index bd2cd7f..6ff6ad2 100644 --- a/apps/klesia/src/index.spec.ts +++ b/apps/klesia/src/index.spec.ts @@ -11,8 +11,8 @@ it("returns result for mina_getTransactionCount", async () => { params: ["B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS"], }, }); - const { result } = await response.json(); - expect(result).toBeGreaterThan(0); + const { result } = (await response.json()) as { result: string }; + expect(BigInt(result)).toBeGreaterThan(0); }); it("returns result for mina_getBalance", async () => { @@ -22,7 +22,7 @@ it("returns result for mina_getBalance", async () => { params: ["B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS"], }, }); - const { result } = await response.json(); + const { result } = (await response.json()) as { result: string }; expect(BigInt(String(result))).toBeGreaterThan(0); }); @@ -30,15 +30,15 @@ it("returns result for mina_blockHash", async () => { const response = await client.api.$post({ json: { method: "mina_blockHash" }, }); - const { result } = await response.json(); - expect((result as unknown as string).length).toBeGreaterThan(0); + const { result } = (await response.json()) as { result: string }; + expect(result.length).toBeGreaterThan(0); }); it("returns result for mina_chainId", async () => { const response = await client.api.$post({ json: { method: "mina_chainId" }, }); - const { result } = await response.json(); + const { result } = (await response.json()) as { result: string }; expect(result.length).toBeGreaterThan(0); }); @@ -49,7 +49,8 @@ it("returns result for mina_getAccount", async () => { params: ["B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS"], }, }); - const { result } = await response.json(); + // biome-ignore lint/suspicious/noExplicitAny: TODO + const { result } = (await response.json()) as any; expect(BigInt(result.nonce)).toBeGreaterThanOrEqual(0); expect(BigInt(result.balance)).toBeGreaterThanOrEqual(0); }); diff --git a/apps/klesia/src/index.ts b/apps/klesia/src/index.ts index 293636e..7c95eb2 100644 --- a/apps/klesia/src/index.ts +++ b/apps/klesia/src/index.ts @@ -11,7 +11,7 @@ import { match } from "ts-pattern"; import mainDocs from "../docs/index.txt"; import rpcDocs from "../docs/rpc.txt"; import { mina } from "./methods/mina"; -import { RpcMethodSchema, RpcResponseSchema } from "./schema"; +import { RpcMethod, RpcMethodSchema, RpcResponseSchema } from "./schema"; import { buildResponse } from "./utils/build-response"; export const api = new OpenAPIHono(); @@ -63,39 +63,81 @@ const rpcRoute = createRoute({ export const klesiaRpcRoute = api.openapi(rpcRoute, async ({ req, json }) => { const body = req.valid("json"); return match(body) - .with({ method: "mina_getTransactionCount" }, async ({ params }) => { - const [publicKey] = params; - const result = await mina.getTransactionCount({ - publicKey: PublicKeySchema.parse(publicKey), - }); - return json(buildResponse(result), 200); - }) - .with({ method: "mina_getBalance" }, async ({ params }) => { + .with( + { method: RpcMethod.enum.mina_getTransactionCount }, + async ({ params }) => { + const [publicKey] = params; + const result = await mina.getTransactionCount({ + publicKey: PublicKeySchema.parse(publicKey), + }); + return json( + buildResponse({ + method: RpcMethod.enum.mina_getTransactionCount, + result, + }), + 200, + ); + }, + ) + .with({ method: RpcMethod.enum.mina_getBalance }, async ({ params }) => { const [publicKey] = params; const result = await mina.getBalance({ publicKey: PublicKeySchema.parse(publicKey), }); - return json(buildResponse(result), 200); + return json( + buildResponse({ method: RpcMethod.enum.mina_getBalance, result }), + 200, + ); }) - .with({ method: "mina_blockHash" }, async () => { + .with({ method: RpcMethod.enum.mina_blockHash }, async () => { + if (process.env.MINA_NETWORK === "zeko_devnet") { + return json( + buildResponse({ + method: RpcMethod.enum.mina_blockHash, + error: { + code: -32600, + message: "Network not supported.", + }, + }), + 200, + ); + } const result = await mina.blockHash(); - return json(buildResponse(result), 200); + return json( + buildResponse({ method: RpcMethod.enum.mina_blockHash, result }), + 200, + ); }) - .with({ method: "mina_chainId" }, async () => { + .with({ method: RpcMethod.enum.mina_chainId }, async () => { const result = await mina.chainId(); - return json(buildResponse(result), 200); - }) - .with({ method: "mina_sendTransaction" }, async ({ params }) => { - const [signedTransaction, type] = params; - const result = await mina.sendTransaction({ signedTransaction, type }); - return json(buildResponse(result), 200); + return json( + buildResponse({ method: RpcMethod.enum.mina_chainId, result }), + 200, + ); }) - .with({ method: "mina_getAccount" }, async ({ params }) => { + .with( + { method: RpcMethod.enum.mina_sendTransaction }, + async ({ params }) => { + const [signedTransaction, type] = params; + const result = await mina.sendTransaction({ signedTransaction, type }); + return json( + buildResponse({ + method: RpcMethod.enum.mina_sendTransaction, + result, + }), + 200, + ); + }, + ) + .with({ method: RpcMethod.enum.mina_getAccount }, async ({ params }) => { const [publicKey] = params; const result = await mina.getAccount({ publicKey: PublicKeySchema.parse(publicKey), }); - return json(buildResponse(result), 200); + return json( + buildResponse({ method: RpcMethod.enum.mina_getAccount, result }), + 200, + ); }) .exhaustive(); }); @@ -103,3 +145,10 @@ export const klesiaRpcRoute = api.openapi(rpcRoute, async ({ req, json }) => { serve(api); export type KlesiaRpc = typeof klesiaRpcRoute; +export { + KlesiaNetwork, + RpcMethod, + type RpcMethodType, + type RpcResponseType, + type RpcErrorType, +} from "./schema"; diff --git a/apps/klesia/src/methods/mina.spec.ts b/apps/klesia/src/methods/mina.spec.ts index 8672c67..b012ee1 100644 --- a/apps/klesia/src/methods/mina.spec.ts +++ b/apps/klesia/src/methods/mina.spec.ts @@ -5,7 +5,7 @@ const TEST_PKEY = "B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS"; it("should return transactions count", async () => { const result = await mina.getTransactionCount({ publicKey: TEST_PKEY }); - expect(result).toBeGreaterThan(0); + expect(BigInt(result)).toBeGreaterThan(0); }); it("should return balance", async () => { diff --git a/apps/klesia/src/methods/mina.ts b/apps/klesia/src/methods/mina.ts index a33a86d..b343506 100644 --- a/apps/klesia/src/methods/mina.ts +++ b/apps/klesia/src/methods/mina.ts @@ -15,7 +15,7 @@ const getTransactionCount = async ({ publicKey }: { publicKey: string }) => { `, { publicKey }, ); - return Number.parseInt(data.account.nonce); + return data.account.nonce; }; const getBalance = async ({ publicKey }: { publicKey: string }) => { diff --git a/apps/klesia/src/schema.ts b/apps/klesia/src/schema.ts index 3608c63..4b07c83 100644 --- a/apps/klesia/src/schema.ts +++ b/apps/klesia/src/schema.ts @@ -1,38 +1,94 @@ import { PublicKeySchema } from "@mina-js/shared"; import { z } from "zod"; +export const KlesiaNetwork = z.enum(["devnet", "mainnet", "zeko_devnet"]); export const PublicKeyParamsSchema = z.array(PublicKeySchema).length(1); export const EmptyParamsSchema = z.array(z.string()).length(0).optional(); export const SendTransactionSchema = z.array(z.any(), z.string()).length(2); +export const RpcMethod = z.enum([ + "mina_getTransactionCount", + "mina_getBalance", + "mina_blockHash", + "mina_chainId", + "mina_sendTransaction", + "mina_getAccount", +]); +export type RpcMethodType = z.infer; + export const RpcMethodSchema = z.discriminatedUnion("method", [ z.object({ - method: z.literal("mina_getTransactionCount"), + method: z.literal(RpcMethod.enum.mina_getTransactionCount), params: PublicKeyParamsSchema, }), z.object({ - method: z.literal("mina_getBalance"), + method: z.literal(RpcMethod.enum.mina_getBalance), params: PublicKeyParamsSchema, }), z.object({ - method: z.literal("mina_blockHash"), + method: z.literal(RpcMethod.enum.mina_blockHash), params: EmptyParamsSchema, }), z.object({ - method: z.literal("mina_chainId"), + method: z.literal(RpcMethod.enum.mina_chainId), params: EmptyParamsSchema, }), z.object({ - method: z.literal("mina_sendTransaction"), + method: z.literal(RpcMethod.enum.mina_sendTransaction), params: SendTransactionSchema, }), z.object({ - method: z.literal("mina_getAccount"), + method: z.literal(RpcMethod.enum.mina_getAccount), params: PublicKeyParamsSchema, }), ]); -export const RpcResponseSchema = z.object({ +export const JsonRpcResponse = z.object({ jsonrpc: z.literal("2.0"), - result: z.any(), }); + +export const RpcError = z.object({ + code: z.number(), + message: z.string(), +}); + +export type RpcErrorType = z.infer; + +export const ErrorSchema = JsonRpcResponse.extend({ + error: RpcError, +}); + +export const RpcResponseSchema = z.union([ + z.discriminatedUnion("method", [ + JsonRpcResponse.extend({ + method: z.literal(RpcMethod.enum.mina_getTransactionCount), + result: z.string(), + }), + JsonRpcResponse.extend({ + method: z.literal(RpcMethod.enum.mina_getBalance), + result: z.string(), + }), + JsonRpcResponse.extend({ + method: z.literal(RpcMethod.enum.mina_blockHash), + result: z.string(), + }), + JsonRpcResponse.extend({ + method: z.literal(RpcMethod.enum.mina_chainId), + result: z.string(), + }), + JsonRpcResponse.extend({ + method: z.literal(RpcMethod.enum.mina_sendTransaction), + result: z.string(), + }), + JsonRpcResponse.extend({ + method: z.literal(RpcMethod.enum.mina_getAccount), + result: z.object({ + nonce: z.string(), + balance: z.string(), + }), + }), + ]), + ErrorSchema, +]); + +export type RpcResponseType = z.infer; diff --git a/apps/klesia/src/utils/build-response.ts b/apps/klesia/src/utils/build-response.ts index 48af077..69914a0 100644 --- a/apps/klesia/src/utils/build-response.ts +++ b/apps/klesia/src/utils/build-response.ts @@ -1,4 +1,16 @@ -export const buildResponse = (data: unknown) => ({ - jsonrpc: "2.0", - result: data, -}); +import type { RpcErrorType, RpcMethodType } from "../schema"; + +export const buildResponse = ({ + result, + error, + method, +}: { result?: unknown; error?: RpcErrorType; method: RpcMethodType }) => { + if (error) { + return { + jsonrpc: "2.0", + error, + method, + }; + } + return { jsonrpc: "2.0", result, method }; +}; diff --git a/apps/klesia/src/utils/node.ts b/apps/klesia/src/utils/node.ts index e2ad5a5..0125a87 100644 --- a/apps/klesia/src/utils/node.ts +++ b/apps/klesia/src/utils/node.ts @@ -1,10 +1,9 @@ import { Client, cacheExchange, fetchExchange } from "@urql/core"; import { match } from "ts-pattern"; import { z } from "zod"; +import { KlesiaNetwork } from "../schema"; -const NetworkMatcher = z.enum(["devnet", "mainnet"]); - -const MINA_NETWORK = NetworkMatcher.parse(process.env.MINA_NETWORK ?? "devnet"); +const MINA_NETWORK = KlesiaNetwork.parse(process.env.MINA_NETWORK ?? "devnet"); const NODE_API_DEVNET = z .string() .parse( @@ -17,11 +16,15 @@ const NODE_API_MAINNET = z process.env.NODE_API_MAINNET ?? "https://api.minascan.io/node/mainnet/v1/graphql", ); +const NODE_API_ZEKO_DEVNET = z + .string() + .parse(process.env.NODE_API_ZEKO_DEVNET ?? "https://devnet.zeko.io/graphql"); export const getNodeApiUrl = () => { return match(MINA_NETWORK) .with("devnet", () => NODE_API_DEVNET) .with("mainnet", () => NODE_API_MAINNET) + .with("zeko_devnet", () => NODE_API_ZEKO_DEVNET) .exhaustive(); }; diff --git a/bun.lockb b/bun.lockb index 1538fb9e494df8acb0f39914c60e3b7865dfe7d8..5662d731092f45130e245a659cda7fc2bdaea343 100755 GIT binary patch delta 11829 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 g<{9 delta 11755 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 diff --git a/packages/klesia-sdk/package.json b/packages/klesia-sdk/package.json index 4f7220b..7cc9e07 100644 --- a/packages/klesia-sdk/package.json +++ b/packages/klesia-sdk/package.json @@ -25,7 +25,6 @@ }, "dependencies": { "hono": "^4.5.10", - "ts-pattern": "^5.3.1", - "zod": "^3.23.8" + "ts-pattern": "^5.3.1" } } diff --git a/packages/klesia-sdk/src/client.spec.ts b/packages/klesia-sdk/src/client.spec.ts index acb2051..109e0c2 100644 --- a/packages/klesia-sdk/src/client.spec.ts +++ b/packages/klesia-sdk/src/client.spec.ts @@ -3,9 +3,9 @@ import { createClient } from "./client"; it("fetches transaction count", async () => { const client = createClient({ network: "devnet" }); - const { result } = await client.request({ + const { result } = await client.request<"mina_getTransactionCount">({ method: "mina_getTransactionCount", params: ["B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS"], }); - expect(result).toBeGreaterThan(0); + expect(BigInt(result)).toBeGreaterThan(0); }); diff --git a/packages/klesia-sdk/src/client.ts b/packages/klesia-sdk/src/client.ts index d93493b..e63c364 100644 --- a/packages/klesia-sdk/src/client.ts +++ b/packages/klesia-sdk/src/client.ts @@ -1,25 +1,57 @@ -import type { KlesiaRpc } from "@mina-js/klesia"; +import { + KlesiaNetwork, + type KlesiaRpc, + type RpcErrorType, + type RpcResponseType, +} from "@mina-js/klesia"; import { hc } from "hono/client"; import { match } from "ts-pattern"; -import { z } from "zod"; -const NetworkMatcher = z.enum(["mainnet", "devnet"]); +type CreateClientProps = { + network: "mainnet" | "devnet" | "zeko_devnet"; + customUrl?: string; + throwable?: boolean; +}; -type CreateClientProps = { network: "mainnet" | "devnet"; customUrl?: string }; +const throwRpcError = ({ + code, + message, +}: { code: number; message: string }) => { + throw new Error(`${code} - ${message}`); +}; -export const createClient = ({ network, customUrl }: CreateClientProps) => { - const baseClient = match(NetworkMatcher.parse(network)) +export const createClient = ({ + network, + customUrl, + throwable = true, +}: CreateClientProps) => { + const baseClient = match(KlesiaNetwork.parse(network)) .with("devnet", () => hc(customUrl ?? "https://devnet.klesia.palladians.xyz"), ) .with("mainnet", () => hc(customUrl ?? "https://mainnet.klesia.palladians.xyz"), ) + .with("zeko_devnet", () => + hc(customUrl ?? "https://zeko-devnet.klesia.palladians.xyz"), + ) .exhaustive(); const rpcHandler = baseClient.api.$post; type RpcRequest = Parameters[0]; - const request = async (req: RpcRequest["json"]) => { - return (await baseClient.api.$post({ json: req })).json(); + const request = async (req: RpcRequest["json"]) => { + const json = (await ( + await baseClient.api.$post({ json: req }) + ).json()) as Extract & { + error?: RpcErrorType; + }; + if (!throwable) { + return json; + } + if (json?.error) { + const { code, message } = json.error; + return throwRpcError({ code, message }); + } + return json; }; return { request,