diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4271f3..fecb90c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,4 +12,4 @@ jobs: - run: bun i --no-save - run: bun run build - run: bun run test - - run: bunx pkg-pr-new publish './packages/klesia-sdk' './packages/accounts' './packages/connect' './packages/providers' './packages/utils' './apps/klesia' + - run: bunx pkg-pr-new publish './packages/klesia-sdk' './packages/accounts' './packages/connect' './packages/providers' './packages/utils' diff --git a/apps/klesia/docs/index.txt b/apps/klesia/docs/index.txt deleted file mode 100644 index d26e16d..0000000 --- a/apps/klesia/docs/index.txt +++ /dev/null @@ -1,5 +0,0 @@ -Klesia RPC is a JSON-RPC 2.0 wrapper over common Mina Protocol tools and services. The intention is to provide an outstanding Developer Experience for Mina Protocol's zkApp Developers. - -## TypeScript SDK - -There is a TypeScript SDK available for Klesia RPC. It is available on npm as `@mina-js/klesia-sdk`. diff --git a/apps/klesia/docs/rpc.txt b/apps/klesia/docs/rpc.txt deleted file mode 100644 index d332161..0000000 --- a/apps/klesia/docs/rpc.txt +++ /dev/null @@ -1,45 +0,0 @@ -## Methods - -### mina_getTransactionCount - -Returns the number of transactions sent from an address. Usually you may want to use this number to determine the nonce for the next transaction. - -#### Parameters - -Array of strings: -- `publicKey` - Address to check for transaction count. - ---- - -### mina_getBalance - -Returns the balance of the account of given address. - -#### Parameters - -Array of strings: -- `publicKey` - Address to check for transaction count. - ---- - -### mina_blockHash - -Returns the hash of the most recent block. - ---- - -### mina_chainId - -Returns the currently configured chain ID. - ---- - -### mina_sendTransaction - -Broadcasts a signed transaction to the network. - -#### Parameters - -Array of strings: -- `input` - Signed transaction or zkApp input. -- `type` - Transaction type. Can be `payment`, `delegation`, or `zkapp`. diff --git a/apps/klesia/package.json b/apps/klesia/package.json index 82b894b..b89a720 100644 --- a/apps/klesia/package.json +++ b/apps/klesia/package.json @@ -5,10 +5,6 @@ "module": "dist/index.js", "types": "dist/index.d.ts", "exports": { - "./schema": { - "types": "./dist/schema.d.ts", - "default": "./dist/schema.js" - }, ".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" @@ -21,15 +17,17 @@ "test": "bun test --rerun-each 3", "cleanup": "rimraf dist .turbo node_modules" }, + "devDependencies": { + "@mina-js/utils": "workspace:*" + }, "dependencies": { "@hono/node-server": "^1.12.2", "@hono/zod-openapi": "^0.16.0", - "@mina-js/utils": "workspace:*", "@urql/core": "^5.0.6", "bigint-quantile": "^0.0.2", "dayjs": "^1.11.13", "dotenv": "^16.4.5", - "hono": "^4.5.10", + "hono": "4.6.9", "hono-rate-limiter": "^0.4.0", "nanoid": "^5.0.7", "ofetch": "^1.3.4", diff --git a/apps/klesia/src/index.ts b/apps/klesia/src/index.ts index dcc199f..6d8d1ad 100644 --- a/apps/klesia/src/index.ts +++ b/apps/klesia/src/index.ts @@ -1,15 +1,17 @@ import { getConnInfo } from "@hono/node-server/conninfo"; import { OpenAPIHono, createRoute } from "@hono/zod-openapi"; -import { PublicKeySchema } from "@mina-js/utils"; +import { + KlesiaRpcMethod, + KlesiaRpcMethodSchema, + KlesiaRpcResponseSchema, + PublicKeySchema, +} from "@mina-js/utils"; import { rateLimiter } from "hono-rate-limiter"; import { cors } from "hono/cors"; import { logger } from "hono/logger"; import { nanoid } from "nanoid"; import { match } from "ts-pattern"; -import mainDocs from "../docs/index.txt"; -import rpcDocs from "../docs/rpc.txt"; import { mina } from "./methods/mina"; -import { RpcMethod, RpcMethodSchema, RpcResponseSchema } from "./schema"; import { buildResponse } from "./utils/build-response"; export const api = new OpenAPIHono(); @@ -35,22 +37,22 @@ api.doc("/api/openapi", { info: { version: "1.0.0", title: "Klesia RPC", - description: mainDocs, }, }); const rpcRoute = createRoute({ method: "post", path: "/api", - description: rpcDocs, request: { - body: { content: { "application/json": { schema: RpcMethodSchema } } }, + body: { + content: { "application/json": { schema: KlesiaRpcMethodSchema } }, + }, }, responses: { 200: { content: { "application/json": { - schema: RpcResponseSchema, + schema: KlesiaRpcResponseSchema, }, }, description: "JSON-RPC response.", @@ -59,10 +61,10 @@ const rpcRoute = createRoute({ }); export const klesiaRpcRoute = api.openapi(rpcRoute, async ({ req, json }) => { - const body = req.valid("json"); + const body = KlesiaRpcMethodSchema.parse(await req.json()); return match(body) .with( - { method: RpcMethod.enum.mina_getTransactionCount }, + { method: KlesiaRpcMethod.enum.mina_getTransactionCount }, async ({ params }) => { const [publicKey] = params; const result = await mina.getTransactionCount({ @@ -76,14 +78,17 @@ export const klesiaRpcRoute = api.openapi(rpcRoute, async ({ req, json }) => { ); }, ) - .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); - }) - .with({ method: RpcMethod.enum.mina_blockHash }, async () => { + .with( + { method: KlesiaRpcMethod.enum.mina_getBalance }, + async ({ params }) => { + const [publicKey] = params; + const result = await mina.getBalance({ + publicKey: PublicKeySchema.parse(publicKey), + }); + return json(buildResponse({ result }), 200); + }, + ) + .with({ method: KlesiaRpcMethod.enum.mina_blockHash }, async () => { if (process.env.MINA_NETWORK === "zeko_devnet") { return json( buildResponse({ @@ -98,12 +103,12 @@ export const klesiaRpcRoute = api.openapi(rpcRoute, async ({ req, json }) => { const result = await mina.blockHash(); return json(buildResponse({ result }), 200); }) - .with({ method: RpcMethod.enum.mina_chainId }, async () => { + .with({ method: KlesiaRpcMethod.enum.mina_chainId }, async () => { const result = await mina.chainId(); return json(buildResponse({ result }), 200); }) .with( - { method: RpcMethod.enum.mina_sendTransaction }, + { method: KlesiaRpcMethod.enum.mina_sendTransaction }, async ({ params }) => { const [signedTransaction, type] = params; const result = await mina.sendTransaction({ signedTransaction, type }); @@ -115,21 +120,17 @@ export const klesiaRpcRoute = api.openapi(rpcRoute, async ({ req, json }) => { ); }, ) - .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); - }) + .with( + { method: KlesiaRpcMethod.enum.mina_getAccount }, + async ({ params }) => { + const [publicKey] = params; + const result = await mina.getAccount({ + publicKey: PublicKeySchema.parse(publicKey), + }); + return json(buildResponse({ result }), 200); + }, + ) .exhaustive(); }); export type KlesiaRpc = typeof klesiaRpcRoute; -export { - KlesiaNetwork, - RpcMethod, - type RpcMethodType, - type RpcResponseType, - type RpcErrorType, -} from "./schema"; diff --git a/apps/klesia/src/schema.ts b/apps/klesia/src/schema.ts deleted file mode 100644 index 9f2070a..0000000 --- a/apps/klesia/src/schema.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { PublicKeySchema } from "@mina-js/utils"; -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 SignatureSchema = z.union([ - z.object({ - rawSignature: z.string(), - }), - z.object({ field: z.string(), scalar: z.string() }), -]); -export const SendTransactionSchema = z.tuple([ - z.any(), - z.enum(["payment", "delegation", "zkapp"]), -]); - -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(RpcMethod.enum.mina_getTransactionCount), - params: PublicKeyParamsSchema, - }), - z.object({ - method: z.literal(RpcMethod.enum.mina_getBalance), - params: PublicKeyParamsSchema, - }), - z.object({ - method: z.literal(RpcMethod.enum.mina_blockHash), - params: EmptyParamsSchema, - }), - z.object({ - method: z.literal(RpcMethod.enum.mina_chainId), - params: EmptyParamsSchema, - }), - z.object({ - method: z.literal(RpcMethod.enum.mina_sendTransaction), - params: SendTransactionSchema, - }), - z.object({ - method: z.literal(RpcMethod.enum.mina_getAccount), - params: PublicKeyParamsSchema, - }), -]); - -export type RpcRequestType = z.infer; - -export const JsonRpcResponse = z.object({ - jsonrpc: z.literal("2.0"), -}); - -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/node.ts b/apps/klesia/src/utils/node.ts index 0125a87..889a62a 100644 --- a/apps/klesia/src/utils/node.ts +++ b/apps/klesia/src/utils/node.ts @@ -1,7 +1,7 @@ +import { KlesiaNetwork } from "@mina-js/utils"; import { Client, cacheExchange, fetchExchange } from "@urql/core"; import { match } from "ts-pattern"; import { z } from "zod"; -import { KlesiaNetwork } from "../schema"; const MINA_NETWORK = KlesiaNetwork.parse(process.env.MINA_NETWORK ?? "devnet"); const NODE_API_DEVNET = z diff --git a/bun.lockb b/bun.lockb index 4b2b59c..34fa9dc 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/packages/klesia-sdk/package.json b/packages/klesia-sdk/package.json index 10315e2..0d92844 100644 --- a/packages/klesia-sdk/package.json +++ b/packages/klesia-sdk/package.json @@ -19,8 +19,10 @@ "peerDependencies": { "typescript": "^5.0.0" }, + "devDependencies": { + "@mina-js/utils": "workspace:*" + }, "dependencies": { - "@mina-js/klesia": "workspace:*", "micro-ftch": "^0.4.0", "ts-pattern": "^5.3.1" } diff --git a/packages/klesia-sdk/src/client.ts b/packages/klesia-sdk/src/client.ts index 1727ce0..c93eb7c 100644 --- a/packages/klesia-sdk/src/client.ts +++ b/packages/klesia-sdk/src/client.ts @@ -1,8 +1,8 @@ import { KlesiaNetwork, - type RpcRequestType, - type RpcResponseType, -} from "@mina-js/klesia/schema"; + type KlesiaRpcRequestType, + type KlesiaRpcResponseType, +} from "@mina-js/utils"; import { ftch, jsonrpc } from "micro-ftch"; import { match } from "ts-pattern"; @@ -25,9 +25,9 @@ export const createClient = ({ network, customUrl }: CreateClientProps) => { ) .exhaustive(); const rpc = jsonrpc(net, baseUrl); - const request = async (req: RpcRequestType) => { + const request = async (req: KlesiaRpcRequestType) => { const params = req.params ?? []; - const json: Extract["result"] = + const json: Extract["result"] = await rpc.call(req.method, ...params); return json; }; diff --git a/packages/providers/package.json b/packages/providers/package.json index aeeef38..e5c9f36 100644 --- a/packages/providers/package.json +++ b/packages/providers/package.json @@ -16,9 +16,8 @@ "test": "bun test", "cleanup": "rimraf dist .turbo" }, - "dependencies": { - "@mina-js/utils": "workspace:*", - "zod": "3.23.8" + "devDependencies": { + "@mina-js/utils": "workspace:*" }, "peerDependencies": { "typescript": "^5.0.0" diff --git a/packages/providers/src/validation.ts b/packages/providers/src/validation.ts index a8834f7..9fe3175 100644 --- a/packages/providers/src/validation.ts +++ b/packages/providers/src/validation.ts @@ -4,12 +4,12 @@ import { NetworkId, NullifierSchema, PublicKeySchema, - SendableSchema, SignedFieldsSchema, SignedMessageSchema, SignedTransactionSchema, TransactionPayloadSchema, TransactionReceiptSchema, + TypedSendableSchema, ZkAppCommandPayload, } from "@mina-js/utils"; import { z } from "zod"; @@ -63,7 +63,7 @@ export const SignTransactionRequestParamsSchema = RequestWithContext.extend({ }).strict(); export const SendTransactionRequestParamsSchema = RequestWithContext.extend({ method: z.literal("mina_sendTransaction"), - params: z.tuple([SendableSchema, z.enum(["payment", "delegation", "zkapp"])]), + params: TypedSendableSchema, }).strict(); export const CreateNullifierRequestParamsSchema = RequestWithContext.extend({ method: z.literal("mina_createNullifier"), diff --git a/packages/utils/package.json b/packages/utils/package.json index 89e1d75..c363571 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -17,7 +17,9 @@ "cleanup": "rimraf dist .turbo" }, "dependencies": { - "mina-signer": "3.0.7", + "mina-signer": "3.0.7" + }, + "devDependencies": { "zod": "3.23.8" }, "peerDependencies": { diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index c43f005..b523f96 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -1,5 +1,8 @@ import type { z } from "zod"; import type { + KlesiaRpcMethod, + KlesiaRpcMethodSchema, + KlesiaRpcResponseSchema, LiteralSchema, NullifierSchema, PartialTransactionSchema, @@ -38,3 +41,10 @@ export type SignedFields = z.infer; export type Nullifier = z.infer; export type SignedTransaction = z.infer; export type TransactionReceipt = z.infer; + +/** + * Klesia RPC types + */ +export type KlesiaRpcMethodType = z.infer; +export type KlesiaRpcRequestType = z.infer; +export type KlesiaRpcResponseType = z.infer; diff --git a/packages/utils/src/validation.ts b/packages/utils/src/validation.ts index 3872518..64bc7b1 100644 --- a/packages/utils/src/validation.ts +++ b/packages/utils/src/validation.ts @@ -31,6 +31,8 @@ export const PrivateKeySchema = z.string().length(52); export const NetworkId = z.string().regex(networkPattern); +export const KlesiaNetwork = z.enum(["devnet", "mainnet", "zeko_devnet"]); + export const FeePayerSchema = z .object({ feePayer: PublicKeySchema, @@ -145,3 +147,96 @@ export const SendableSchema = z.union([ SendTransactionBodySchema, SendZkAppBodySchema, ]); + +export const TypedSendableSchema = z.tuple([ + SendableSchema, + z.enum(["payment", "delegation", "zkapp"]), +]); + +export const KlesiaRpcMethod = z.enum([ + "mina_getTransactionCount", + "mina_getBalance", + "mina_blockHash", + "mina_chainId", + "mina_sendTransaction", + "mina_getAccount", +]); + +export const PublicKeyParamsSchema = z.tuple([PublicKeySchema]); + +export const EmptyParamsSchema = z.tuple([]).optional(); + +export const KlesiaRpcMethodSchema = z.discriminatedUnion("method", [ + z.object({ + method: z.literal(KlesiaRpcMethod.enum.mina_getTransactionCount), + params: PublicKeyParamsSchema, + }), + z.object({ + method: z.literal(KlesiaRpcMethod.enum.mina_getBalance), + params: PublicKeyParamsSchema, + }), + z.object({ + method: z.literal(KlesiaRpcMethod.enum.mina_blockHash), + params: EmptyParamsSchema, + }), + z.object({ + method: z.literal(KlesiaRpcMethod.enum.mina_chainId), + params: EmptyParamsSchema, + }), + z.object({ + method: z.literal(KlesiaRpcMethod.enum.mina_sendTransaction), + params: TypedSendableSchema, + }), + z.object({ + method: z.literal(KlesiaRpcMethod.enum.mina_getAccount), + params: PublicKeyParamsSchema, + }), +]); + +export const JsonRpcResponse = z.object({ + jsonrpc: z.literal("2.0"), +}); + +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 KlesiaRpcResponseSchema = z.union([ + z.discriminatedUnion("method", [ + JsonRpcResponse.extend({ + method: z.literal(KlesiaRpcMethod.enum.mina_getTransactionCount), + result: z.string(), + }), + JsonRpcResponse.extend({ + method: z.literal(KlesiaRpcMethod.enum.mina_getBalance), + result: z.string(), + }), + JsonRpcResponse.extend({ + method: z.literal(KlesiaRpcMethod.enum.mina_blockHash), + result: z.string(), + }), + JsonRpcResponse.extend({ + method: z.literal(KlesiaRpcMethod.enum.mina_chainId), + result: z.string(), + }), + JsonRpcResponse.extend({ + method: z.literal(KlesiaRpcMethod.enum.mina_sendTransaction), + result: z.string(), + }), + JsonRpcResponse.extend({ + method: z.literal(KlesiaRpcMethod.enum.mina_getAccount), + result: z.object({ + nonce: z.string(), + balance: z.string(), + }), + }), + ]), + ErrorSchema, +]);