From 8100402f3b7ee3529ca1625166a2faf7513f4912 Mon Sep 17 00:00:00 2001 From: Tomek Marciniak Date: Thu, 1 Aug 2024 23:55:30 +0200 Subject: [PATCH] feat: init --- .gitignore | 177 ++++++++++++++++++++++ README.md | 19 +++ biome.json | 15 ++ bun.lockb | Bin 0 -> 67798 bytes package.json | 27 ++++ src/accounts/generate-mnemonic.spec.ts | 8 + src/accounts/generate-mnemonic.ts | 16 ++ src/accounts/generate-private-key.spec.ts | 12 ++ src/accounts/generate-private-key.ts | 11 ++ src/accounts/index.ts | 4 + src/index.ts | 1 + src/providers/index.ts | 2 + src/providers/types.ts | 170 +++++++++++++++++++++ src/providers/validation.ts | 79 ++++++++++ tsconfig.json | 3 + tsup.config.ts | 11 ++ 16 files changed, 555 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 biome.json create mode 100755 bun.lockb create mode 100644 package.json create mode 100644 src/accounts/generate-mnemonic.spec.ts create mode 100644 src/accounts/generate-mnemonic.ts create mode 100644 src/accounts/generate-private-key.spec.ts create mode 100644 src/accounts/generate-private-key.ts create mode 100644 src/accounts/index.ts create mode 100644 src/index.ts create mode 100644 src/providers/index.ts create mode 100644 src/providers/types.ts create mode 100644 src/providers/validation.ts create mode 100644 tsconfig.json create mode 100644 tsup.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d77462f --- /dev/null +++ b/.gitignore @@ -0,0 +1,177 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +dist + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..a72d553 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# mina-js + +The TypeScript interface for Mina Protocol. + +## Features + +- Interact with Mina Wallets such as [Pallad](https://pallad.co/). +- Browser native BigInt. +- TypeScript ready. + +## Acknowledgements + +- This is not a library to write provable programs. In that case head over to [o1js](https://github.com/o1-labs/o1js). +- Inspired heavily by [viem](https://github.com/wevm/viem). +- Great part of the code is based our work and code of [Pallad](https://pallad.co). + +## Authors + +- [@mrcnk](https://github.com/mrcnk) ([X.com](https://x.com/schoolboytom)) diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..88f3b31 --- /dev/null +++ b/biome.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", + "organizeImports": { + "enabled": true + }, + "files": { + "ignore": ["dist/**/*"] + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + } +} diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..887a7a257182b4b9a422b15ca1af72e33fde6e7c GIT binary patch literal 67798 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 pVrAN4*dW7L(K { + const mnemonic = generateMnemonic(english); + expect(mnemonic.split(" ").length).toBe(12); +}); diff --git a/src/accounts/generate-mnemonic.ts b/src/accounts/generate-mnemonic.ts new file mode 100644 index 0000000..9ed1305 --- /dev/null +++ b/src/accounts/generate-mnemonic.ts @@ -0,0 +1,16 @@ +import { generateMnemonic as generateMnemonic_ } from "@scure/bip39"; + +/** + * @description Generates a random mnemonic phrase with a given wordlist. + * + * @param wordlist The wordlist to use for generating the mnemonic phrase. + * @param strength mnemonic strength 128-256 bits + * + * @returns A randomly generated mnemonic phrase. + */ +export function generateMnemonic( + wordlist: string[], + strength?: number | undefined, +): string { + return generateMnemonic_(wordlist, strength); +} diff --git a/src/accounts/generate-private-key.spec.ts b/src/accounts/generate-private-key.spec.ts new file mode 100644 index 0000000..4ee9193 --- /dev/null +++ b/src/accounts/generate-private-key.spec.ts @@ -0,0 +1,12 @@ +import { expect, it } from "bun:test"; +import { generatePrivateKey } from "./generate-private-key"; + +it("returns a string", () => { + const privateKey = generatePrivateKey(); + expect(typeof privateKey).toBe("string"); +}); + +it("has a length of 64 characters", () => { + const privateKey = generatePrivateKey(); + expect(privateKey.length).toBe(52); +}); diff --git a/src/accounts/generate-private-key.ts b/src/accounts/generate-private-key.ts new file mode 100644 index 0000000..248abad --- /dev/null +++ b/src/accounts/generate-private-key.ts @@ -0,0 +1,11 @@ +import Client from "mina-signer"; + +/** + * @description Generates a random private key. + * + * @returns A randomly generated private key. + */ +export function generatePrivateKey(): string { + const client = new Client({ network: "mainnet" }); + return client.genKeys().privateKey; +} diff --git a/src/accounts/index.ts b/src/accounts/index.ts new file mode 100644 index 0000000..89aa83b --- /dev/null +++ b/src/accounts/index.ts @@ -0,0 +1,4 @@ +export { wordlist as english } from "@scure/bip39/wordlists/english"; + +export { generateMnemonic } from "./generate-mnemonic"; +export { generatePrivateKey } from "./generate-private-key"; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..6520f28 --- /dev/null +++ b/src/index.ts @@ -0,0 +1 @@ +import "./accounts"; diff --git a/src/providers/index.ts b/src/providers/index.ts new file mode 100644 index 0000000..0b70ea2 --- /dev/null +++ b/src/providers/index.ts @@ -0,0 +1,2 @@ +export * from "./types"; +export * from "./validation"; diff --git a/src/providers/types.ts b/src/providers/types.ts new file mode 100644 index 0000000..567b7bb --- /dev/null +++ b/src/providers/types.ts @@ -0,0 +1,170 @@ +import type { + AddChainData, + CreateNullifierData, + SendTransactionData, + SignFieldsData, + SignMessageData, + SignTransactionData, + SwitchChainData, +} from "./validation"; + +// biome-ignore lint/suspicious/noExplicitAny: Deal with it. +type TODO = any; + +export type Address = `b62${string}`; + +export type MinaProviderDetail = { + info: MinaProviderInfo; + provider: MinaProviderClient; +}; + +export type MinaProviderInfo = { + icon: `data:image/${string}`; + name: string; + rdns: string; + slug: string; +}; + +export interface MinaAnnounceProviderEvent + extends CustomEvent { + type: "mina:announceProvider"; +} + +export interface EIP6963RequestProviderEvent extends Event { + type: "mina:requestProvider"; +} + +export type ProviderRpcError = ( + | { message: "User Rejected Request"; code: 4001 } + | { message: "Unauthorized"; code: 4100 } + | { message: "Unsupported Method"; code: 4200 } + | { message: "Disconnected"; code: 4900 } + | { message: "Chain Disconnected"; code: 4901 } +) & + Error; + +export type ProviderRpcEvent = + | "connect" + | "disconnect" + | "chainChanged" + | "accountsChanged" + | "mina_message"; + +// Return types +type SignedMessage = { + publicKey: string; + data: string; + signature: { + field: string; + scalar: string; + }; +}; + +type SignedFieldsData = { + data: (string | number)[]; + publicKey: string; + signature: string; +}; + +// Request variants +export type AccountsRequest = (args: { + method: "mina_accounts"; +}) => Promise; + +export type ChainIdRequest = (args: { + method: "mina_chainId"; +}) => Promise; + +export type ChainInformationRequest = (args: { + method: "mina_chainInformation"; +}) => Promise<{ url: string; name: string }>; + +export type GetBalanceRequest = (args: { + method: "mina_getBalance"; +}) => Promise; + +export type SignRequest = (args: { + method: "mina_sign"; + params: SignMessageData; +}) => Promise; + +export type SignFieldsRequest = (args: { + method: "mina_signFields"; + params: SignFieldsData; +}) => Promise; + +export type SignTransactionRequest = (args: { + method: "mina_signTransaction"; + params: SignTransactionData; +}) => Promise; + +export type SendTransactionRequest = (args: { + method: "mina_sendTransaction"; + params: SendTransactionData; +}) => Promise; + +export type CreateNullifierRequest = (args: { + method: "mina_createNullifier"; + params: CreateNullifierData; +}) => Promise; + +export type SwitchChainRequest = (args: { + method: "mina_switchChain"; + params: SwitchChainData; +}) => Promise; + +export type AddChainRequest = (args: { + method: "mina_addChain"; + params: AddChainData; +}) => Promise; + +export type ProviderRequest = + | AccountsRequest + | ChainIdRequest + | ChainInformationRequest + | GetBalanceRequest + | SignRequest + | SignFieldsRequest + | SignTransactionRequest + | SendTransactionRequest + | CreateNullifierRequest + | SwitchChainRequest + | AddChainRequest; + +export type ConnectedListener = ( + event: "connected", + callback: (params: { chainId: string }) => void, +) => void; + +export type DisconnectedListener = ( + event: "disconnected", + callback: (params: { error: ProviderRpcError }) => void, +) => void; + +export type ChainChangedListener = ( + event: "chainChanged", + callback: (params: { chainId: string }) => void, +) => void; + +export type AccountsChangedListener = ( + event: "accountsChanged", + callback: (params: { accounts: Address[] }) => void, +) => void; + +export type MessageListener = ( + event: "mina_message", + callback: (params: { type: string; data: unknown }) => void, +) => void; + +export type ProviderListener = + | ConnectedListener + | DisconnectedListener + | ChainChangedListener + | AccountsChangedListener + | MessageListener; + +export type MinaProviderClient = { + request: ProviderRequest; + addListener: ProviderListener; + removeListener: ProviderListener; +}; diff --git a/src/providers/validation.ts b/src/providers/validation.ts new file mode 100644 index 0000000..1214287 --- /dev/null +++ b/src/providers/validation.ts @@ -0,0 +1,79 @@ +import { z } from "zod"; + +const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]); +type Literal = z.infer; +type Json = Literal | { [key: string]: Json } | Json[]; +const jsonSchema: z.ZodType = z.lazy(() => + z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)]), +); + +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(), +}); + +export type SwitchChainData = z.infer; + +export const addChainRequestSchema = z.object({ + url: z.string().url(), + name: z.string(), +}); + +export type AddChainData = z.infer; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..78e22f0 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@tsconfig/bun/tsconfig.json" +} diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 0000000..843abb8 --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/accounts/index.ts", "src/providers/index.ts"], + outDir: "./dist", + format: "esm", + sourcemap: true, + clean: true, + bundle: true, + dts: true, +});