diff --git a/apps/docs/src/pages/connect/wallet-interface.mdx b/apps/docs/src/pages/connect/wallet-interface.mdx index d6738f9..4bff5ac 100644 --- a/apps/docs/src/pages/connect/wallet-interface.mdx +++ b/apps/docs/src/pages/connect/wallet-interface.mdx @@ -6,6 +6,8 @@ To make your zkApp compatible with Mina wallets, we've created a strongly typed ### mina_accounts +Get accounts. If not authorized, the response is an empty array. + ```ts twoslash import { createStore } from '@mina-js/connect' @@ -14,8 +16,22 @@ const { provider } = store.getProviders()[0] const { result } = await provider.request<'mina_accounts'>({ method: 'mina_accounts' }) ``` +### mina_requestAccounts + +Acts like `mina_accounts` but with a prompt to authorize in case user didn't authorize yet. + +```ts twoslash +import { createStore } from '@mina-js/connect' + +const store = createStore() +const { provider } = store.getProviders()[0] +const { result } = await provider.request<'mina_requestAccounts'>({ method: 'mina_requestAccounts' }) +``` + ### mina_chainId +Get the chain ID. It's a string that represents the current network. Values are `mina:mainnet` or `mina:testnet`. + ```ts twoslash import { createStore } from '@mina-js/connect' @@ -26,6 +42,8 @@ const { result } = await provider.request<'mina_chainId'>({ method: 'mina_chainI ### mina_getBalance +Get the balance of the current account. The value is already parsed to Mina units. + ```ts twoslash import { createStore } from '@mina-js/connect' @@ -36,6 +54,8 @@ const { result } = await provider.request<'mina_getBalance'>({ method: 'mina_get ### mina_chainInformation +Get chain information. Similar to `mina_chainId`, but more detailed. It returns current network's RPC url, name, and slug (chain ID). + ```ts twoslash import { createStore } from '@mina-js/connect' @@ -44,6 +64,21 @@ const { provider } = store.getProviders()[0] const { result } = await provider.request<'mina_chainInformation'>({ method: 'mina_chainInformation' }) ``` +### mina_getState + +Returns filtered Public Credentials. + +```ts twoslash +import { createStore } from '@mina-js/connect' + +const store = createStore() +const { provider } = store.getProviders()[0] +const { result } = await provider.request<'mina_getState'>({ + method: 'mina_getState', + params: [{ issuer: "University of Example" }, []], +}) +``` + ## Commands ### mina_sign @@ -128,3 +163,74 @@ const { result } = await provider.request<'mina_createNullifier'>({ params: [['1', '2', '3']] }) ``` + +### mina_sendTransaction + +Send a signed transaction to the network. + +```ts twoslash +import { createStore } from '@mina-js/connect' + +const store = createStore() +const { provider } = store.getProviders()[0] +const { result } = await provider.request<'mina_sendTransaction'>({ + method: 'mina_sendTransaction', + params: [{ input: {}, signature: { field: 'xyz', scalar: 'xyz' } }, 'payment'] +}) +``` + +### mina_setState + +Saves a new Public Credential. + +```ts twoslash +import { createStore } from '@mina-js/connect' + +const store = createStore() +const { provider } = store.getProviders()[0] +const { result } = await provider.request<'mina_setState'>({ + method: "mina_setState", + params: [ + { + objectName: "Pallad Mock Credential", + object: {}, // DID Credential with a Kimchi proof + }, + ], +}) +``` + +### mina_switchChain + +Prompts user to switch to another network. It's useful for dApps that support multiple networks. + +```ts twoslash +import { createStore } from '@mina-js/connect' + +const store = createStore() +const { provider } = store.getProviders()[0] +await provider.request<'mina_switchChain'>({ + method: 'mina_switchChain', + params: ['mina:mainnet'] +}) +``` + +### mina_addChain + +Prompts user to add a new chain. + +```ts twoslash +import { createStore } from '@mina-js/connect' + +const store = createStore() +const { provider } = store.getProviders()[0] +await provider.request<'mina_addChain'>({ + method: 'mina_addChain', + params: [ + { + name: 'Mina Fakenet', + slug: 'mina:fakenet', + url: 'https://fakenet.example.com', + } + ] +}) +``` diff --git a/apps/klesia/src/methods/mina.ts b/apps/klesia/src/methods/mina.ts index 9c82c9b..ae6a7f4 100644 --- a/apps/klesia/src/methods/mina.ts +++ b/apps/klesia/src/methods/mina.ts @@ -1,6 +1,10 @@ +import { + SendTransactionBodySchema, + SendZkAppBodySchema, + type Sendable, +} from "@mina-js/utils"; import { gql } from "@urql/core"; import { match } from "ts-pattern"; -import { SendTransactionBodySchema, SendZkAppBodySchema } from "../schema"; import { getNodeClient } from "../utils/node"; export const PRIORITY = { @@ -83,8 +87,7 @@ const sendTransaction = async ({ signedTransaction, type, }: { - // biome-ignore lint/suspicious/noExplicitAny: TODO - signedTransaction: any; + signedTransaction: Sendable; type: "payment" | "delegation" | "zkapp"; }) => { const client = getNodeClient(); diff --git a/apps/klesia/src/schema.ts b/apps/klesia/src/schema.ts index d5a8a73..fc2e8bb 100644 --- a/apps/klesia/src/schema.ts +++ b/apps/klesia/src/schema.ts @@ -1,6 +1,5 @@ -import { PublicKeySchema, TransactionBodySchema } from "@mina-js/utils"; +import { PublicKeySchema } from "@mina-js/utils"; import { z } from "zod"; -import { SendZkappInput } from "./zkapp"; export const KlesiaNetwork = z.enum(["devnet", "mainnet", "zeko_devnet"]); export const PublicKeyParamsSchema = z.array(PublicKeySchema).length(1); @@ -11,17 +10,6 @@ export const SignatureSchema = z.union([ }), z.object({ field: z.string(), scalar: z.string() }), ]); -export const SendTransactionBodySchema = z.object({ - input: TransactionBodySchema, - signature: SignatureSchema, -}); -export const SendZkAppBodySchema = z.object({ - input: SendZkappInput, -}); -export const SendableSchema = z.union([ - SendTransactionBodySchema, - SendZkAppBodySchema, -]); export const SendTransactionSchema = z.tuple([ z.any(), z.enum(["payment", "delegation", "zkapp"]), diff --git a/apps/klesia/src/zkapp.ts b/apps/klesia/src/zkapp.ts deleted file mode 100644 index fa32013..0000000 --- a/apps/klesia/src/zkapp.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { z } from "zod"; - -// Helper schemas -const PublicKey = z.string(); -const Signature = z.string(); -const Field = z.string(); -const TokenId = z.string(); -const UInt32 = z.number().int(); -const UInt64 = z.number().int(); -const BooleanSchema = z.boolean(); -const AuthRequired = z.enum([ - "None", - "Proof", - "Signature", - "Either", - "Impossible", -]); -const Sign = z.enum(["Positive", "Negative"]); -const Memo = z.string(); -const ZkappProof = z.string(); - -// Complex nested schemas -const VerificationKeyWithHashInput = z.object({ - data: z.string(), - hash: Field, -}); - -const PermissionsInput = z.object({ - editState: AuthRequired, - access: AuthRequired, - send: AuthRequired, - receive: AuthRequired, - setDelegate: AuthRequired, - setPermissions: AuthRequired, - setVerificationKey: z.object({ - auth: AuthRequired, - txnVersion: UInt32, - }), - setZkappUri: AuthRequired, - editActionState: AuthRequired, - setTokenSymbol: AuthRequired, - incrementNonce: AuthRequired, - setVotingFor: AuthRequired, - setTiming: AuthRequired, -}); - -const TimingInput = z.object({ - initialMinimumBalance: UInt64, - cliffTime: UInt32, - cliffAmount: UInt64, - vestingPeriod: UInt32, - vestingIncrement: UInt64, -}); - -const AccountUpdateModificationInput = z.object({ - appState: z.array(Field).optional(), - delegate: PublicKey.optional(), - verificationKey: VerificationKeyWithHashInput.optional(), - permissions: PermissionsInput.optional(), - zkappUri: z.string().optional(), - tokenSymbol: z.string().optional(), - timing: TimingInput.optional(), - votingFor: Field.optional(), -}); - -const BalanceChangeInput = z.object({ - magnitude: UInt64, - sgn: Sign, -}); - -const CurrencyAmountIntervalInput = z.object({ - lower: UInt64, - upper: UInt64, -}); - -const LengthIntervalInput = z.object({ - lower: UInt32, - upper: UInt32, -}); - -const GlobalSlotSinceGenesisIntervalInput = z.object({ - lower: UInt32, - upper: UInt32, -}); - -const EpochLedgerPreconditionInput = z.object({ - hash: Field.optional(), - totalCurrency: CurrencyAmountIntervalInput.optional(), -}); - -const EpochDataPreconditionInput = z.object({ - ledger: EpochLedgerPreconditionInput, - seed: Field.optional(), - startCheckpoint: Field.optional(), - lockCheckpoint: Field.optional(), - epochLength: LengthIntervalInput.optional(), -}); - -const NetworkPreconditionInput = z.object({ - snarkedLedgerHash: Field.optional(), - blockchainLength: LengthIntervalInput.optional(), - minWindowDensity: LengthIntervalInput.optional(), - totalCurrency: CurrencyAmountIntervalInput.optional(), - globalSlotSinceGenesis: GlobalSlotSinceGenesisIntervalInput.optional(), - stakingEpochData: EpochDataPreconditionInput, - nextEpochData: EpochDataPreconditionInput, -}); - -const AccountPreconditionInput = z.object({ - balance: CurrencyAmountIntervalInput.optional(), - nonce: LengthIntervalInput.optional(), - receiptChainHash: Field.optional(), - delegate: PublicKey.optional(), - state: z.array(Field), - actionState: Field.optional(), - provedState: BooleanSchema.optional(), - isNew: BooleanSchema.optional(), -}); - -const PreconditionsInput = z.object({ - network: NetworkPreconditionInput, - account: AccountPreconditionInput, - validWhile: GlobalSlotSinceGenesisIntervalInput.optional(), -}); - -const MayUseTokenInput = z.object({ - parentsOwnToken: BooleanSchema, - inheritFromParent: BooleanSchema, -}); - -const AuthorizationKindStructuredInput = z.object({ - isSigned: BooleanSchema, - isProved: BooleanSchema, - verificationKeyHash: Field, -}); - -const AccountUpdateBodyInput = z.object({ - publicKey: PublicKey, - tokenId: TokenId, - update: AccountUpdateModificationInput, - balanceChange: BalanceChangeInput, - incrementNonce: BooleanSchema, - events: z.array(z.array(Field)), - actions: z.array(z.array(Field)), - callData: Field, - callDepth: z.number().int(), - preconditions: PreconditionsInput, - useFullCommitment: BooleanSchema, - implicitAccountCreationFee: BooleanSchema, - mayUseToken: MayUseTokenInput, - authorizationKind: AuthorizationKindStructuredInput, -}); - -const ControlInput = z.object({ - proof: ZkappProof.optional(), - signature: Signature.optional(), -}); - -const ZkappAccountUpdateInput = z.object({ - body: AccountUpdateBodyInput, - authorization: ControlInput, -}); - -const FeePayerBodyInput = z.object({ - publicKey: PublicKey, - fee: UInt64, - validUntil: UInt32.optional(), - nonce: UInt32, -}); - -const ZkappFeePayerInput = z.object({ - body: FeePayerBodyInput, - authorization: Signature, -}); - -const ZkappCommandInput = z.object({ - feePayer: ZkappFeePayerInput, - accountUpdates: z.array(ZkappAccountUpdateInput), - memo: Memo, -}); - -export const SendZkappInput = z.object({ - zkappCommand: ZkappCommandInput, -}); diff --git a/packages/providers/src/validation.ts b/packages/providers/src/validation.ts index 9ab78dc..a8834f7 100644 --- a/packages/providers/src/validation.ts +++ b/packages/providers/src/validation.ts @@ -1,8 +1,10 @@ -import { JsonSchema } from "@mina-js/utils"; import { FieldSchema, + JsonSchema, + NetworkId, NullifierSchema, PublicKeySchema, + SendableSchema, SignedFieldsSchema, SignedMessageSchema, SignedTransactionSchema, @@ -22,7 +24,7 @@ export const AddChainRequestParams = z .object({ url: z.string().url(), name: z.string(), - slug: z.string(), + slug: NetworkId, }) .strict(); @@ -61,7 +63,7 @@ export const SignTransactionRequestParamsSchema = RequestWithContext.extend({ }).strict(); export const SendTransactionRequestParamsSchema = RequestWithContext.extend({ method: z.literal("mina_sendTransaction"), - params: z.array(SignedTransactionSchema), + params: z.tuple([SendableSchema, z.enum(["payment", "delegation", "zkapp"])]), }).strict(); export const CreateNullifierRequestParamsSchema = RequestWithContext.extend({ method: z.literal("mina_createNullifier"), @@ -100,7 +102,7 @@ export const RequestAccountsRequestReturnSchema = z export const ChainIdRequestReturnSchema = z .object({ method: z.literal("mina_chainId"), - result: z.string(), + result: NetworkId, }) .strict(); export const ChainInformationRequestReturnSchema = z diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index c1160fe..c43f005 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -5,6 +5,7 @@ import type { PartialTransactionSchema, PrivateKeySchema, PublicKeySchema, + SendableSchema, SignedFieldsSchema, SignedMessageSchema, SignedTransactionSchema, @@ -27,6 +28,7 @@ export type TransactionPayload = z.infer; export type PartialTransaction = z.infer; export type ZkAppCommandBody = z.infer; export type ZkAppCommandProperties = z.infer; +export type Sendable = z.infer; /** * Return types diff --git a/packages/utils/src/validation.ts b/packages/utils/src/validation.ts index 1b14a21..3872518 100644 --- a/packages/utils/src/validation.ts +++ b/packages/utils/src/validation.ts @@ -1,6 +1,8 @@ import { z } from "zod"; import type { Json } from "./types"; +export const networkPattern = /^[^:]+:[^:]+$/; + /** * Data primitive schemas */ @@ -27,6 +29,8 @@ export const PublicKeySchema = z.string().length(55).startsWith("B62"); export const PrivateKeySchema = z.string().length(52); +export const NetworkId = z.string().regex(networkPattern); + export const FeePayerSchema = z .object({ feePayer: PublicKeySchema, @@ -127,3 +131,17 @@ export const TransactionReceiptSchema = z hash: z.string(), }) .strict(); + +export const SendTransactionBodySchema = z.object({ + input: TransactionBodySchema, + signature: SignatureSchema, +}); + +export const SendZkAppBodySchema = z.object({ + input: JsonSchema, +}); + +export const SendableSchema = z.union([ + SendTransactionBodySchema, + SendZkAppBodySchema, +]);