diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 195d4f4..d1985c4 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'
+ - run: bunx pkg-pr-new publish './packages/klesia-sdk' './packages/accounts' './packages/connect'
diff --git a/apps/docs/package.json b/apps/docs/package.json
index aa1f161..75816d2 100644
--- a/apps/docs/package.json
+++ b/apps/docs/package.json
@@ -18,6 +18,7 @@
},
"devDependencies": {
"@mina-js/klesia-sdk": "workspace:*",
+ "@mina-js/connect": "workspace:*",
"daisyui": "^4.12.10"
}
}
diff --git a/apps/docs/src/pages/accounts/getting-started.mdx b/apps/docs/src/pages/accounts/getting-started.mdx
index 3acac4f..7449a4d 100644
--- a/apps/docs/src/pages/accounts/getting-started.mdx
+++ b/apps/docs/src/pages/accounts/getting-started.mdx
@@ -12,6 +12,16 @@ MinaJS Accounts shares an API similar to [Viem](https://viem.sh/).
$ npm install @mina-js/accounts
```
+### Nightly builds
+
+```sh
+$ npm install https://pkg.pr.new/palladians/mina-js/@mina-js/accounts@main
+```
+
+:::warning
+For now there are only [nightly builds](/getting-started#nightly-builds) available. The stable version will be released soon™️.
+:::
+
## Utilities
### generateMnemonic
diff --git a/apps/docs/src/pages/connect/getting-started.mdx b/apps/docs/src/pages/connect/getting-started.mdx
index 6193338..21311e9 100644
--- a/apps/docs/src/pages/connect/getting-started.mdx
+++ b/apps/docs/src/pages/connect/getting-started.mdx
@@ -1 +1,31 @@
# Getting Started [Start with MinaJS Connect.]
+
+## Installation
+
+```sh
+$ npm install @mina-js/connect
+```
+
+### Nightly builds
+
+```sh
+$ npm install https://pkg.pr.new/palladians/mina-js/@mina-js/connect@main
+```
+
+:::warning
+For now there are only [nightly builds](/getting-started#nightly-builds) available. The stable version will be released soon™️.
+:::
+
+## Find Mina wallets
+
+To discover injected wallet providers, we've developed a Provider Discovery reactive store utility. [Check out the usage](/connect/provider-discovery).
+
+## Window Polyfill
+
+For your convenient TypeScript development, we've created a window polyfill. This will make sure you have an autocompletion for MinaJS compliant providers.
+
+```ts
+import '@mina-js/connect/window'
+
+const account = await window.mina.request({ method: 'mina_accounts' })
+```
diff --git a/apps/docs/src/pages/connect/provider-discovery.mdx b/apps/docs/src/pages/connect/provider-discovery.mdx
new file mode 100644
index 0000000..a1a9935
--- /dev/null
+++ b/apps/docs/src/pages/connect/provider-discovery.mdx
@@ -0,0 +1,56 @@
+# Provider Discovery [Discover injected wallet providers with ease.]
+
+For your convenience, MinaJS Connect provides a simple way to discover all Mina's injected wallet providers. This is done by an API similar to [Wevm's MIPD](https://github.com/wevm/mipd).
+
+## Vanilla TypeScript
+
+```ts twoslash
+import { createStore, type MinaProviderDetail } from '@mina-js/connect'
+
+const store = createStore()
+
+// Reactively, you can use MinaProviderDetail to type the array, but it fails in docs ffs.
+const providersReactively = []
+const unsubscribe = store.subscribe((provider) => providersReactively.push(provider))
+
+// Imperatively
+const providersImperatively = store.getProviders()
+```
+
+## React
+
+```tsx twoslash
+import { useSyncExternalStore } from 'react'
+import { createStore } from '@mina-js/connect'
+
+const store = createStore()
+
+function Example() {
+ const providers = useSyncExternalStore(store.subscribe, store.getProviders)
+}
+```
+
+## Svelte
+
+```svelte twoslash
+
+```
+
+## Vue
+
+```vue twoslash
+
+```
diff --git a/apps/docs/src/pages/getting-started.mdx b/apps/docs/src/pages/getting-started.mdx
index 845072a..3bb58d9 100644
--- a/apps/docs/src/pages/getting-started.mdx
+++ b/apps/docs/src/pages/getting-started.mdx
@@ -75,3 +75,9 @@ The libraries are available on npm. You can install them using npm, yarn, pnpm,
- [MinaJS Connect](/connect)
- [MinaJS Accounts](/accounts)
- [Klesia SDK](/klesia/sdk)
+
+## Nightly builds
+
+We have nightly builds available for the SDK. You can find the latest nightly versions of our packages on:
+
+https://nightly.akryum.dev/palladians/mina-js
diff --git a/apps/docs/src/pages/klesia/sdk.mdx b/apps/docs/src/pages/klesia/sdk.mdx
index a9e0ba0..878946f 100644
--- a/apps/docs/src/pages/klesia/sdk.mdx
+++ b/apps/docs/src/pages/klesia/sdk.mdx
@@ -8,8 +8,14 @@ Klesia has a TypeScript SDK to interact with the JSON-RPC API. The SDK provides
$ npm install @mina-js/klesia-sdk
```
+### Nightly builds
+
+```sh
+$ npm install https://pkg.pr.new/palladians/mina-js/@mina-js/klesia-sdk@main
+```
+
:::warning
-For now there are only [nightly builds](/klesia/sdk#nightly-builds) available. The stable version will be released soon™️.
+For now there are only [nightly builds](/getting-started#nightly-builds) available. The stable version will be released soon™️.
:::
## Client Options
@@ -34,9 +40,3 @@ const { result } = await client.request<'mina_getTransactionCount'>({
## Methods
Refer to the [RPC Methods](/klesia/rpc#rpc-methods) page for a complete list of methods available on Klesia.
-
-## Nightly Builds
-
-We have nightly builds available for the SDK. You can find the latest nightly versions of our packages on:
-
-https://nightly.akryum.dev/palladians/mina-js
diff --git a/apps/docs/vocs.config.ts b/apps/docs/vocs.config.ts
index d430cc6..6c55f37 100644
--- a/apps/docs/vocs.config.ts
+++ b/apps/docs/vocs.config.ts
@@ -85,6 +85,7 @@ export default defineConfig({
items: [
{ text: "Introduction", link: "/connect" },
{ text: "Getting Started", link: "/connect/getting-started" },
+ { text: "Provider Discovery", link: "/connect/provider-discovery" },
],
},
{
diff --git a/bun.lockb b/bun.lockb
index 05992da..e9a3dbf 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/packages/connect/package.json b/packages/connect/package.json
index 644cedf..fac1203 100644
--- a/packages/connect/package.json
+++ b/packages/connect/package.json
@@ -4,10 +4,15 @@
"module": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
+ "./window": {
+ "types": "./dist/window.d.ts",
+ "default": "./dist/window.js",
+ "import": "./dist/window.mjs"
+ },
".": {
"types": "./dist/index.d.ts",
- "default": "./dist/index.cjs",
- "import": "./dist/index.js"
+ "default": "./dist/index.js",
+ "import": "./dist/index.mjs"
}
},
"scripts": {
@@ -17,6 +22,7 @@
},
"dependencies": {
"@mina-js/providers": "workspace:*",
+ "ts-pattern": "^5.3.1",
"zod": "3.23.8"
},
"peerDependencies": {
diff --git a/packages/connect/src/__mocks__/provider.ts b/packages/connect/src/__mocks__/provider.ts
index ac54a6a..ec6cbb3 100644
--- a/packages/connect/src/__mocks__/provider.ts
+++ b/packages/connect/src/__mocks__/provider.ts
@@ -10,7 +10,7 @@ export const mockedProvider: MinaProviderDetail = {
},
provider: {
request: mock(),
- addListener: mock(),
+ on: mock(),
removeListener: mock(),
},
};
diff --git a/packages/connect/src/__snapshots__/store.spec.ts.snap b/packages/connect/src/__snapshots__/store.spec.ts.snap
new file mode 100644
index 0000000..0d7e124
--- /dev/null
+++ b/packages/connect/src/__snapshots__/store.spec.ts.snap
@@ -0,0 +1,13 @@
+// Bun Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should initialize the store 1`] = `
+{
+ "_listeners": [Function: _listeners],
+ "clear": [Function: clear],
+ "destroy": [Function: destroy],
+ "findProvider": [Function: findProvider],
+ "getProviders": [Function: getProviders],
+ "reset": [Function: reset],
+ "subscribe": [Function: subscribe],
+}
+`;
diff --git a/packages/connect/src/events.spec.ts b/packages/connect/src/events.spec.ts
index 485f555..2569313 100644
--- a/packages/connect/src/events.spec.ts
+++ b/packages/connect/src/events.spec.ts
@@ -5,7 +5,7 @@ import { announceProvider, requestProviders } from "./events";
type Resolver = (value: unknown) => void;
-it("announcec Mina Provider with window event", async () => {
+it("announces Mina Provider with window event", async () => {
const listener =
(resolve: Resolver) =>
({ detail }: MinaAnnounceProviderEvent) => {
diff --git a/packages/connect/src/events.ts b/packages/connect/src/events.ts
index 4de04ad..60cfc7f 100644
--- a/packages/connect/src/events.ts
+++ b/packages/connect/src/events.ts
@@ -43,9 +43,10 @@ export function requestProviders(
if (typeof window === "undefined") return;
const handler = (event: MinaAnnounceProviderEvent) => listener(event.detail);
- window.addEventListener("mina:announceProvider", handler);
+ window.addEventListener("mina:announceProvider", handler as never);
window.dispatchEvent(new CustomEvent("mina:requestProvider"));
- return () => window.removeEventListener("mina:announceProvider", handler);
+ return () =>
+ window.removeEventListener("mina:announceProvider", handler as never);
}
diff --git a/packages/connect/src/global-polyfill.ts b/packages/connect/src/global-polyfill.ts
new file mode 100644
index 0000000..7d6c183
--- /dev/null
+++ b/packages/connect/src/global-polyfill.ts
@@ -0,0 +1,15 @@
+import type {
+ MinaAnnounceProviderEvent,
+ MinaProviderClient,
+ MinaRequestProviderEvent,
+} from "@mina-js/providers";
+
+declare global {
+ interface WindowEventMap {
+ "mina:announceProvider": MinaAnnounceProviderEvent;
+ "mina:requestProvider": MinaRequestProviderEvent;
+ }
+ interface Window {
+ mina?: MinaProviderClient | undefined;
+ }
+}
diff --git a/packages/connect/src/index.ts b/packages/connect/src/index.ts
index 96c500c..25a72a1 100644
--- a/packages/connect/src/index.ts
+++ b/packages/connect/src/index.ts
@@ -1,18 +1,3 @@
-import type {
- MinaAnnounceProviderEvent,
- MinaProviderClient,
- MinaRequestProviderEvent,
-} from "@mina-js/providers";
-
-declare global {
- interface WindowEventMap {
- "mina:announceProvider": MinaAnnounceProviderEvent;
- "mina:requestProvider": MinaRequestProviderEvent;
- }
- interface Window {
- mina?: MinaProviderClient | undefined;
- }
-}
-
export * from "./store";
export * from "./events";
+export type { MinaProviderDetail } from "@mina-js/providers";
diff --git a/packages/connect/src/store.spec.ts b/packages/connect/src/store.spec.ts
new file mode 100644
index 0000000..5453340
--- /dev/null
+++ b/packages/connect/src/store.spec.ts
@@ -0,0 +1,38 @@
+import { expect, it, mock } from "bun:test";
+import { mockedProvider } from "./__mocks__/provider";
+import { announceProvider } from "./events";
+import { createStore } from "./store";
+
+it("should initialize the store", () => {
+ const store = createStore();
+ expect(store).toMatchSnapshot();
+});
+
+it("should update providers", () => {
+ const store = createStore();
+ const listener = mock();
+ store.subscribe(listener);
+ announceProvider(mockedProvider);
+ const provider = store.getProviders()[0];
+ expect(listener).toHaveBeenCalled();
+ expect(provider).toEqual(mockedProvider);
+});
+
+it("should unsubscribe from store", () => {
+ const store = createStore();
+ const listener = mock();
+ const unsub = store.subscribe(listener);
+ unsub();
+ announceProvider(mockedProvider);
+ expect(listener).not.toHaveBeenCalled();
+});
+
+it("should clear providers list", () => {
+ const store = createStore();
+ const listener = mock();
+ store.subscribe(listener);
+ announceProvider(mockedProvider);
+ store.clear();
+ const providers = store.getProviders();
+ expect(providers.length).toEqual(0);
+});
diff --git a/packages/connect/src/unito.ts b/packages/connect/src/unito.ts
new file mode 100644
index 0000000..c0395fc
--- /dev/null
+++ b/packages/connect/src/unito.ts
@@ -0,0 +1,16 @@
+import type { MinaProviderDetail } from "@mina-js/providers";
+import { match } from "ts-pattern";
+
+export const unito = (provider: MinaProviderDetail) => {
+ return match(provider)
+ .with({ info: { slug: "auro" } }, (provider) => {
+ return {
+ ...provider,
+ provider: {
+ ...provider.provider,
+ request: null,
+ },
+ };
+ })
+ .otherwise((provider) => provider);
+};
diff --git a/packages/connect/src/window.ts b/packages/connect/src/window.ts
new file mode 100644
index 0000000..e44d983
--- /dev/null
+++ b/packages/connect/src/window.ts
@@ -0,0 +1 @@
+import "./global-polyfill";
diff --git a/packages/connect/tsup.config.ts b/packages/connect/tsup.config.ts
index 6e9381b..39966ef 100644
--- a/packages/connect/tsup.config.ts
+++ b/packages/connect/tsup.config.ts
@@ -1,3 +1,7 @@
+import { defineConfig } from "tsup";
import sharedConfig from "../shared/tsup.config";
-export default sharedConfig;
+export default defineConfig({
+ ...sharedConfig,
+ entry: ["./src/index.ts", "./src/window.ts"],
+});
diff --git a/packages/providers/src/types.ts b/packages/providers/src/types.ts
index bf5a170..36cb12c 100644
--- a/packages/providers/src/types.ts
+++ b/packages/providers/src/types.ts
@@ -1,22 +1,17 @@
import type {
+ CreateNullifierParams,
Nullifier,
PublicKey,
+ SendTransactionParams,
+ SignFieldsParams,
+ SignMessageParams,
+ SignTransactionParams,
SignedFields,
SignedMessage,
+ SignedTransaction,
TransactionReceipt,
} from "@mina-js/shared";
-import type {
- AddChainData,
- CreateNullifierData,
- SendTransactionData,
- SignFieldsData,
- SignMessageData,
- SignTransactionData,
- SwitchChainData,
-} from "./validation";
-
-// biome-ignore lint/suspicious/noExplicitAny: Deal with it.
-type TODO = any;
+import type { AddChainData, SwitchChainData } from "./validation";
export type MinaProviderDetail = {
info: MinaProviderInfo;
@@ -74,38 +69,38 @@ export type GetBalanceRequest = (args: {
export type SignRequest = (args: {
method: "mina_sign";
- params: SignMessageData;
+ params: SignMessageParams;
}) => Promise;
export type SignFieldsRequest = (args: {
method: "mina_signFields";
- params: SignFieldsData;
+ params: SignFieldsParams;
}) => Promise;
export type SignTransactionRequest = (args: {
method: "mina_signTransaction";
- params: SignTransactionData;
-}) => Promise;
+ params: SignTransactionParams;
+}) => Promise;
export type SendTransactionRequest = (args: {
method: "mina_sendTransaction";
- params: SendTransactionData;
+ params: SendTransactionParams;
}) => Promise;
export type CreateNullifierRequest = (args: {
method: "mina_createNullifier";
- params: CreateNullifierData;
+ params: CreateNullifierParams;
}) => Promise;
export type SwitchChainRequest = (args: {
method: "mina_switchChain";
params: SwitchChainData;
-}) => Promise;
+}) => Promise;
export type AddChainRequest = (args: {
method: "mina_addChain";
params: AddChainData;
-}) => Promise;
+}) => Promise;
export type ProviderRequest =
| AccountsRequest
diff --git a/packages/providers/src/validation.ts b/packages/providers/src/validation.ts
index 024a985..4664b47 100644
--- a/packages/providers/src/validation.ts
+++ b/packages/providers/src/validation.ts
@@ -1,63 +1,5 @@
import { z } from "zod";
-export const signFieldsRequestSchema = z.object({
- fields: z.array(z.coerce.number()),
-});
-
-export type SignFieldsData = z.infer;
-
-export const signMessageRequestSchema = z.object({
- message: z.string(),
-});
-
-export type SignMessageData = z.infer;
-
-export const createNullifierRequestSchema = z.object({
- message: z.array(z.coerce.number()),
-});
-
-export type CreateNullifierData = z.infer;
-
-export const publicKeySchema = z.string().length(55);
-
-export const transactionSchema = z
- .object({
- to: publicKeySchema,
- from: publicKeySchema,
- fee: z.coerce.number(),
- nonce: z.coerce.number(),
- memo: z.string().optional(),
- validUntil: z.coerce.number().optional(),
- amount: z.coerce.number().optional(),
- })
- .strict();
-
-export const signatureSchema = z
- .object({
- field: z.string(),
- scalar: z.string(),
- })
- .strict();
-
-export const signedTransactionSchema = z.object({
- data: transactionSchema,
- publicKey: publicKeySchema,
- signature: signatureSchema,
-});
-
-export const signTransactionRequestSchema = z.object({
- transaction: transactionSchema.strict(),
-});
-
-export type SignTransactionData = z.infer;
-
-export const sendTransactionRequestSchema = z.object({
- signedTransaction: signedTransactionSchema.strict(),
- transactionType: z.enum(["payment", "delegation", "zkapp"]),
-});
-
-export type SendTransactionData = z.infer;
-
export const switchChainRequestSchema = z.object({
chainId: z.string(),
});
diff --git a/packages/shared/src/validation.ts b/packages/shared/src/validation.ts
index 40cb096..4233045 100644
--- a/packages/shared/src/validation.ts
+++ b/packages/shared/src/validation.ts
@@ -16,48 +16,62 @@ const JsonSchema: z.ZodType = z.lazy(() =>
export const FieldSchema = z.coerce.bigint();
-export const GroupSchema = z.object({
- x: FieldSchema,
- y: FieldSchema,
-});
+export const GroupSchema = z
+ .object({
+ x: FieldSchema,
+ y: FieldSchema,
+ })
+ .strict();
export const PublicKeySchema = z.string().length(55).startsWith("B62");
export const PrivateKeySchema = z.string().length(52);
-export const TransactionPayload = z.object({
- from: PublicKeySchema,
- to: PublicKeySchema,
- memo: z.string().optional(),
- fee: z.coerce.bigint(),
- amount: z.coerce.bigint(),
- nonce: z.coerce.bigint(),
- validUntil: z.coerce.bigint().optional(),
-});
+export const TransactionPayload = z
+ .object({
+ from: PublicKeySchema,
+ to: PublicKeySchema,
+ memo: z.string().optional(),
+ fee: z.coerce.bigint(),
+ amount: z.coerce.bigint(),
+ nonce: z.coerce.bigint(),
+ validUntil: z.coerce.bigint().optional(),
+ })
+ .strict();
/**
* Parameter schemas
*/
-export const SignFieldsParamsSchema = z.object({
- fields: z.array(z.coerce.bigint()),
-});
+export const SignFieldsParamsSchema = z
+ .object({
+ fields: z.array(FieldSchema),
+ })
+ .strict();
-export const SignMessageParamsSchema = z.object({
- message: z.string(),
-});
+export const SignMessageParamsSchema = z
+ .object({
+ message: z.string(),
+ })
+ .strict();
-export const CreateNullifierParamsSchema = z.object({
- message: z.array(z.coerce.bigint()),
-});
+export const CreateNullifierParamsSchema = z
+ .object({
+ message: z.array(FieldSchema),
+ })
+ .strict();
-export const SignTransactionParamsSchema = z.object({
- transaction: TransactionPayload,
-});
+export const SignTransactionParamsSchema = z
+ .object({
+ transaction: TransactionPayload,
+ })
+ .strict();
-export const SendTransactionParamsSchema = z.object({
- signedTransaction: TransactionPayload.strict(),
- transactionType: z.enum(["payment", "delegation", "zkapp"]),
-});
+export const SendTransactionParamsSchema = z
+ .object({
+ signedTransaction: TransactionPayload.strict(),
+ transactionType: z.enum(["payment", "delegation", "zkapp"]),
+ })
+ .strict();
/**
* Return type schemas
@@ -77,31 +91,39 @@ export const SignedMessageSchema = z
})
.strict();
-export const SignedFieldsSchema = z.object({
- data: z.array(FieldSchema),
- publicKey: PublicKeySchema,
- signature: z.string(),
-});
-
-export const NullifierSchema = z.object({
- publicKey: GroupSchema,
- public: z.object({
- nullifier: GroupSchema,
- s: FieldSchema,
- }),
- private: z.object({
- c: FieldSchema,
- g_r: GroupSchema,
- h_m_pk_r: GroupSchema,
- }),
-});
-
-export const SignedTransactionSchema = z.object({
- signature: SignatureSchema,
- publicKey: PublicKeySchema,
- data: TransactionPayload,
-});
-
-export const TransactionReceiptSchema = z.object({
- hash: z.string(),
-});
+export const SignedFieldsSchema = z
+ .object({
+ data: z.array(FieldSchema),
+ publicKey: PublicKeySchema,
+ signature: z.string(),
+ })
+ .strict();
+
+export const NullifierSchema = z
+ .object({
+ publicKey: GroupSchema,
+ public: z.object({
+ nullifier: GroupSchema,
+ s: FieldSchema,
+ }),
+ private: z.object({
+ c: FieldSchema,
+ g_r: GroupSchema,
+ h_m_pk_r: GroupSchema,
+ }),
+ })
+ .strict();
+
+export const SignedTransactionSchema = z
+ .object({
+ signature: SignatureSchema,
+ publicKey: PublicKeySchema,
+ data: TransactionPayload,
+ })
+ .strict();
+
+export const TransactionReceiptSchema = z
+ .object({
+ hash: z.string(),
+ })
+ .strict();