diff --git a/apps/extension/package.json b/apps/extension/package.json index cfad4bf3..cfc0deea 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -15,16 +15,17 @@ "test:e2e:ui": "playwright test --ui" }, "dependencies": { + "@mina-js/providers": "https://pkg.pr.new/palladians/mina-js/@mina-js/providers@7fe60e8", "@palladxyz/common": "workspace:*", "@palladxyz/features": "workspace:*", "@palladxyz/key-management": "workspace:*", - "@palladxyz/web-provider": "workspace:*", "@palladxyz/vault": "workspace:*", + "@palladxyz/web-provider": "workspace:*", "@plasmohq/messaging": "0.6.2", "buffer": "6.0.3", - "p-debounce": "4.0.0", "mitt": "3.0.1", "next-themes": "0.3.0", + "p-debounce": "4.0.0", "react": "18.3.1", "react-dom": "18.3.1", "serialize-error": "11.0.3", diff --git a/apps/extension/src/background/handlers/wallet.ts b/apps/extension/src/background/handlers/wallet.ts index 503eb9f7..b0a3e082 100644 --- a/apps/extension/src/background/handlers/wallet.ts +++ b/apps/extension/src/background/handlers/wallet.ts @@ -1,6 +1,6 @@ import { useVault } from "@palladxyz/vault" import type { NetworkName } from "@palladxyz/vault" -import { MinaProvider } from "@palladxyz/web-provider" +import { createMinaProvider } from "@palladxyz/web-provider" import { serializeError } from "serialize-error" import type { Handler } from "./" @@ -12,13 +12,13 @@ export const palladSidePanel: Handler = async ({ sender }) => { export const palladSwitchNetwork: Handler = async ({ data }) => { try { - const provider = await MinaProvider.getInstance() + const provider = await createMinaProvider() await useVault.persist.rehydrate() const { switchNetwork, getChainId } = useVault.getState() const network = data.network as NetworkName await switchNetwork(network) await useVault.persist.rehydrate() - const chainId = await getChainId() + const chainId = getChainId() provider.emit("pallad_event", { data: { chainId: chainId, @@ -32,10 +32,10 @@ export const palladSwitchNetwork: Handler = async ({ data }) => { export const palladConnected: Handler = async () => { try { - const provider = await MinaProvider.getInstance() + const provider = await createMinaProvider() await useVault.persist.rehydrate() const { getChainId } = useVault.getState() - const chainId = await getChainId() + const chainId = getChainId() provider.emit("pallad_event", { data: { chainId: chainId, diff --git a/apps/extension/src/background/handlers/web-provider.ts b/apps/extension/src/background/handlers/web-provider.ts index dc2f6bf7..06e71669 100644 --- a/apps/extension/src/background/handlers/web-provider.ts +++ b/apps/extension/src/background/handlers/web-provider.ts @@ -1,18 +1,33 @@ +import { + AccountsRequestParamsSchema, + ChainIdRequestParamsSchema, + ChainInformationRequestParamsSchema, + CreateNullifierRequestParamsSchema, + GetBalanceRequestParamsSchema, + GetStateRequestParamsSchema, + RequestAccountsRequestParamsSchema, + SendTransactionRequestParamsSchema, + SetStateRequestParamsSchema, + SignFieldsRequestParamsSchema, + SignRequestParamsSchema, + SignTransactionRequestParamsSchema, +} from "@mina-js/providers" import { serializeError } from "serialize-error" +import { z } from "zod" import type { Handler } from "." -import { - MinaProvider, - Validation, -} from "../../../../../packages/web-provider/src" +import { createMinaProvider } from "../../../../../packages/web-provider/src" + +export const OriginSchema = z.string().url() export const minaSetState: Handler = async ({ data }) => { try { - const provider = await MinaProvider.getInstance() - const params = Validation.setStateRequestSchema.parse(data) - return await provider.request({ + const provider = await createMinaProvider() + const payload = SetStateRequestParamsSchema.parse({ method: "mina_setState", - params, + params: data.params, + context: data.context, }) + return await provider.request(payload) } catch (error: unknown) { return { error: serializeError(error) } } @@ -24,12 +39,12 @@ export const minaAddChain = async () => { export const minaRequestNetwork: Handler = async ({ data }) => { try { - const provider = await MinaProvider.getInstance() - const params = Validation.requestSchema.parse(data) - return await provider.request({ - method: "mina_requestNetwork", - params, + const provider = await createMinaProvider() + const payload = ChainInformationRequestParamsSchema.parse({ + method: "mina_chainInformation", + context: data.context, }) + return await provider.request(payload) } catch (error: unknown) { return { error: serializeError(error) } } @@ -41,12 +56,13 @@ export const minaSwitchChain = async () => { export const minaGetState: Handler = async ({ data }) => { try { - const provider = await MinaProvider.getInstance() - const params = Validation.getStateRequestSchema.parse(data) - return await provider.request({ + const provider = await createMinaProvider() + const payload = GetStateRequestParamsSchema.parse({ method: "mina_getState", - params, + params: data.params, + context: data.context, }) + return await provider.request(payload) } catch (error: unknown) { return { error: serializeError(error) } } @@ -54,12 +70,12 @@ export const minaGetState: Handler = async ({ data }) => { export const minaChainId: Handler = async ({ data }) => { try { - const provider = await MinaProvider.getInstance() - const params = Validation.requestSchema.parse(data) - return await provider.request({ + const provider = await createMinaProvider() + const payload = ChainIdRequestParamsSchema.parse({ method: "mina_chainId", - params, + context: data.context, }) + return await provider.request(payload) } catch (error: unknown) { return { error: serializeError(error) } } @@ -67,12 +83,12 @@ export const minaChainId: Handler = async ({ data }) => { export const minaAccounts: Handler = async ({ data }) => { try { - const provider = await MinaProvider.getInstance() - const params = Validation.requestSchema.parse(data) - return await provider.request({ + const provider = await createMinaProvider() + const payload = AccountsRequestParamsSchema.parse({ method: "mina_accounts", - params, + context: data.context, }) + return await provider.request(payload) } catch (error: unknown) { return { error: serializeError(error) } } @@ -80,12 +96,12 @@ export const minaAccounts: Handler = async ({ data }) => { export const minaRequestAccounts: Handler = async ({ data }) => { try { - const provider = await MinaProvider.getInstance() - const params = Validation.requestSchema.parse(data) - return await provider.request({ + const provider = await createMinaProvider() + const payload = RequestAccountsRequestParamsSchema.parse({ method: "mina_requestAccounts", - params, + context: data.context, }) + return await provider.request(payload) } catch (error: unknown) { return { error: serializeError(error) } } @@ -93,12 +109,13 @@ export const minaRequestAccounts: Handler = async ({ data }) => { export const minaSign: Handler = async ({ data }) => { try { - const provider = await MinaProvider.getInstance() - const params = Validation.signMessageRequestSchema.parse(data) - return await provider.request({ + const provider = await createMinaProvider() + const payload = SignRequestParamsSchema.parse({ method: "mina_sign", - params, + params: data.params, + context: data.context, }) + return await provider.request(payload) } catch (error: unknown) { return { error: serializeError(error) } } @@ -106,12 +123,13 @@ export const minaSign: Handler = async ({ data }) => { export const minaSignFields: Handler = async ({ data }) => { try { - const provider = await MinaProvider.getInstance() - const params = Validation.signFieldsRequestSchema.parse(data) - return await provider.request({ + const provider = await createMinaProvider() + const payload = SignFieldsRequestParamsSchema.parse({ method: "mina_signFields", - params, + params: data.params, + context: data.context, }) + return await provider.request(payload) } catch (error: unknown) { return { error: serializeError(error) } } @@ -119,12 +137,13 @@ export const minaSignFields: Handler = async ({ data }) => { export const minaSignTransaction: Handler = async ({ data }) => { try { - const provider = await MinaProvider.getInstance() - const params = Validation.signTransactionRequestSchema.parse(data) - return await provider.request({ + const provider = await createMinaProvider() + const payload = SignTransactionRequestParamsSchema.parse({ method: "mina_signTransaction", - params, + params: data.params, + context: data.context, }) + return await provider.request(payload) } catch (error: unknown) { return { error: serializeError(error) } } @@ -132,12 +151,12 @@ export const minaSignTransaction: Handler = async ({ data }) => { export const minaGetBalance: Handler = async ({ data }) => { try { - const provider = await MinaProvider.getInstance() - const params = Validation.requestSchema.parse(data) - return await provider.request({ + const provider = await createMinaProvider() + const payload = GetBalanceRequestParamsSchema.parse({ method: "mina_getBalance", - params, + context: data.context, }) + return await provider.request(payload) } catch (error: unknown) { return { error: serializeError(error) } } @@ -145,12 +164,13 @@ export const minaGetBalance: Handler = async ({ data }) => { export const minaCreateNullifier: Handler = async ({ data }) => { try { - const provider = await MinaProvider.getInstance() - const params = Validation.createNullifierRequestSchema.parse(data) - return await provider.request({ + const provider = await createMinaProvider() + const payload = CreateNullifierRequestParamsSchema.parse({ method: "mina_createNullifier", - params, + params: data.params, + context: data.context, }) + return await provider.request(payload) } catch (error: unknown) { return { error: serializeError(error) } } @@ -158,22 +178,13 @@ export const minaCreateNullifier: Handler = async ({ data }) => { export const minaSendTransaction: Handler = async ({ data }) => { try { - const provider = await MinaProvider.getInstance() - const params = Validation.sendTransactionRequestSchema.parse(data) - return await provider.request({ + const provider = await createMinaProvider() + const payload = SendTransactionRequestParamsSchema.parse({ method: "mina_sendTransaction", - params, + params: data.params, + context: data.context, }) - } catch (error: unknown) { - return { error: serializeError(error) } - } -} - -export const palladIsConnected: Handler = async ({ data }) => { - try { - const provider = await MinaProvider.getInstance() - const { origin } = Validation.requestSchema.parse(data) - return await provider.isConnected({ origin }) + return await provider.request(payload) } catch (error: unknown) { return { error: serializeError(error) } } diff --git a/apps/extension/src/background/index.ts b/apps/extension/src/background/index.ts index 702a5a09..7d57059b 100644 --- a/apps/extension/src/background/index.ts +++ b/apps/extension/src/background/index.ts @@ -1,4 +1,4 @@ -import { MinaProvider } from "@palladxyz/web-provider" +import { createMinaProvider } from "@palladxyz/web-provider" import { onMessage, sendMessage } from "webext-bridge/background" import { runtime, tabs } from "webextension-polyfill" import { @@ -17,7 +17,6 @@ import { minaSignTransaction, minaSwitchChain, palladConnected, - palladIsConnected, palladSidePanel, palladSwitchNetwork, } from "./handlers" @@ -45,7 +44,6 @@ onMessage("mina_sendTransaction", minaSendTransaction) /** * Wallet handlers */ -onMessage("pallad_isConnected", palladIsConnected) onMessage("pallad_switchNetwork", palladSwitchNetwork) onMessage("pallad_connected", palladConnected) onMessage("pallad_sidePanel", palladSidePanel) @@ -64,13 +62,13 @@ runtime.onConnect.addListener(async (port) => { } }) runtime.onInstalled.addListener(async ({ reason }) => { - const provider = await MinaProvider.getInstance() + const provider = await createMinaProvider() await chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }) if (reason === "install") { if (!E2E_TESTING) await tabs.create({ url: "https://get.pallad.co/welcome" }) } - provider.on("pallad_event", async (data) => { + provider.on("pallad_event" as never, async (data: any) => { const { permissions } = await chrome.storage.local.get("permissions") const urls = Object.entries(permissions) .filter(([_, allowed]) => allowed === "ALLOWED") diff --git a/apps/extension/src/inject/index.ts b/apps/extension/src/inject/index.ts index 341b36e0..14daddcb 100644 --- a/apps/extension/src/inject/index.ts +++ b/apps/extension/src/inject/index.ts @@ -25,19 +25,19 @@ const inject = () => { try { const result: any = await sendMessage( data.method, - { ...data.payload, origin }, + { params: data.params, context: { origin } }, "background", ) if (result?.error) { - response = { jsonrpc: "1.0", error: deserializeError(result.error) } + response = { jsonrpc: "2.0", error: deserializeError(result.error) } } else { - response = { jsonrpc: "1.0", result } + response = { jsonrpc: "2.0", result } } return responseChannel.postMessage({ response, }) } catch (error) { - response = { jsonrpc: "1.0", error } + response = { jsonrpc: "2.0", error } return responseChannel.postMessage({ response, }) diff --git a/apps/extension/src/inject/rpc/provider.ts b/apps/extension/src/inject/rpc/provider.ts index bd67f27c..d8b6657a 100644 --- a/apps/extension/src/inject/rpc/provider.ts +++ b/apps/extension/src/inject/rpc/provider.ts @@ -26,7 +26,10 @@ export const provider = { request: async ({ method, params }: { method: string; params: any }) => await debouncedCall({ method, - payload: { ...params, origin: window.location.origin }, + params, + context: { + origin: window.location.origin, + }, }), isPallad: true, on: _events.on, diff --git a/apps/extension/src/inject/rpc/utils.ts b/apps/extension/src/inject/rpc/utils.ts index 5a5f1f5e..53a112a9 100644 --- a/apps/extension/src/inject/rpc/utils.ts +++ b/apps/extension/src/inject/rpc/utils.ts @@ -4,8 +4,13 @@ const BROADCAST_CHANNEL_ID = "pallad" const callPalladAsync = ({ method, - payload, -}: { method: string; payload: any }) => { + params, + context, +}: { + method: string + params: any + context: Record +}) => { return new Promise((resolve, reject) => { const privateChannelId = `private-${Math.random()}` const channel = new BroadcastChannel(BROADCAST_CHANNEL_ID) @@ -26,7 +31,8 @@ const callPalladAsync = ({ responseChannel.addEventListener("message", messageListener) channel.postMessage({ method, - payload, + params, + context, isPallad: true, respondAt: privateChannelId, }) diff --git a/package.json b/package.json index 86903629..cdd0d25e 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "@tsconfig/strictest": "2.0.5", "@turbo/gen": "2.0.9", "@vitest/coverage-v8": "2.0.5", + "@mina-js/utils": "https://pkg.pr.new/palladians/mina-js/@mina-js/utils@7fe60e8", "autoprefixer": "10.4.19", "happy-dom": "14.12.3", "husky": "9.1.4", diff --git a/packages/features/package.json b/packages/features/package.json index 6edb4f80..5585d5ee 100644 --- a/packages/features/package.json +++ b/packages/features/package.json @@ -77,7 +77,6 @@ "webextension-polyfill": "0.12.0", "xss": "1.0.15", "yaml": "2.5.0", - "zod": "3.23.8", "zustand": "4.5.4" }, "devDependencies": { @@ -103,7 +102,8 @@ "vite": "5.3.5", "vite-plugin-node-polyfills": "0.17.0", "vite-plugin-svgr": "4.2.0", - "vite-plugin-top-level-await": "1.4.2" + "vite-plugin-top-level-await": "1.4.2", + "zod": "3.23.8" }, "peerDependencies": { "@types/mocha": "10.0.1", diff --git a/packages/features/src/send/hooks/use-transaction-confirmation.tsx b/packages/features/src/send/hooks/use-transaction-confirmation.tsx index 893d1f4e..f29b6736 100644 --- a/packages/features/src/send/hooks/use-transaction-confirmation.tsx +++ b/packages/features/src/send/hooks/use-transaction-confirmation.tsx @@ -1,11 +1,7 @@ import type { ChainOperationArgs } from "@palladxyz/key-management" -import { Mina } from "@palladxyz/mina-core" +import { Mina, TransactionType } from "@palladxyz/mina-core" import { useVault } from "@palladxyz/vault" import dayjs from "dayjs" -import type { - Payment, - SignedLegacy, -} from "mina-signer/dist/node/mina-signer/src/TSTypes" import type { SubmitHandler, UseFormReturn } from "react-hook-form" import { useMixpanel } from "react-mixpanel-browser" import { useNavigate } from "react-router-dom" @@ -15,6 +11,7 @@ import { useAccount } from "@/common/hooks/use-account" import { useTransactionStore } from "@/common/store/transaction" import { usePendingTransactionStore } from "@palladxyz/vault" +import type { SignedTransaction, TransactionBody } from "@mina-js/utils" import { utf8ToBytes } from "@noble/hashes/utils" import type { ConfirmTransactionSchema } from "../components/confirm-transaction-form.schema" @@ -29,14 +26,11 @@ export const useTransactionConfirmation = ({ }: UseTransactionConfirmationProps) => { const mixpanel = useMixpanel() const navigate = useNavigate() - // can use - // const request = useVault((state) => state.request) for signing const sign = useVault((state) => state.sign) const submitTx = useVault((state) => state.submitTx) const constructTx = useVault((state) => state.constructTx) const syncWallet = useVault((state) => state._syncWallet) const currentNetworkName = useVault((state) => state.currentNetworkName) - //const currentWallet = useVault((state) => state.getCurrentWallet()) const { publicKey, data: accountProperties } = useAccount() const outgoingTransaction = useTransactionStore( (state) => state.outgoingTransaction, @@ -48,7 +42,7 @@ export const useTransactionConfirmation = ({ const rawFee = outgoingTransaction.fee || 0.001 const amount = BigInt(rawAmount * 1_000_000_000).toString() const fee = BigInt(rawFee * 1_000_000_000).toString() - const rawTransaction: Mina.TransactionBody = { + const rawTransaction = { to: outgoingTransaction.to, from: publicKey, memo: outgoingTransaction.memo, @@ -56,19 +50,11 @@ export const useTransactionConfirmation = ({ fee, amount, nonce: accountProperties?.inferredNonce ?? 0, // need a util for this whole `onSubmit` to remove Mina dependency - type, - } - const constructTransaction = () => { - const constructTxArgs = { - transaction: rawTransaction, - transactionType: type, - } - return constructTx(constructTxArgs) } const submitTransaction: SubmitHandler = async ( data, ) => { - const constructedTx = await constructTransaction() + const constructedTx = constructTx({ transaction: rawTransaction }) const getPassphrase = () => utf8ToBytes(data.spendingPassword) let signedTx // TODO: make chain agnostic depending on the currentWallet chain and it's corresponding operation e.g. 'eth_signTransaction' vs 'mina_signTransaction' @@ -77,8 +63,19 @@ export const useTransactionConfirmation = ({ network: "Mina", networkType: currentNetworkName === "Mainnet" ? "mainnet" : "testnet", } + let txBody: TransactionBody + if (type === TransactionType.STAKE_DELEGATION) { + const { amount, ...txRest } = constructedTx + txBody = txRest + } else { + txBody = constructedTx + } try { - signedTx = await sign(constructedTx as any, operationArgs, getPassphrase) + signedTx = (await sign( + { transaction: txBody }, + operationArgs, + getPassphrase, + )) as SignedTransaction } catch (error: unknown) { if (error instanceof Error) { if (error.name === "AuthenticationError") @@ -88,24 +85,11 @@ export const useTransactionConfirmation = ({ }) } } - // TODO: Make a util for this const submitTxArgs = { - signedTransaction: signedTx as unknown as SignedLegacy, + signedTransaction: signedTx, type, - transactionDetails: { - fee: rawTransaction.fee, - to: rawTransaction.to, - from: rawTransaction.from, - nonce: rawTransaction.nonce, - memo: rawTransaction.memo, - amount: rawTransaction.amount, - validUntil: rawTransaction.validUntil, - }, } - const submittedTx = (await submitTx(submitTxArgs as any)) as any - const hash = - submittedTx?.sendPayment?.payment?.hash ?? - submittedTx?.sendDelegation?.delegation?.hash + const hash = await submitTx(submitTxArgs as never) addPendingTransaction({ hash, expireAt: dayjs().add(8, "hours").toISOString(), diff --git a/packages/key-management/src/chains/Mina/guards.ts b/packages/key-management/src/chains/Mina/guards.ts index b892a272..9911e392 100644 --- a/packages/key-management/src/chains/Mina/guards.ts +++ b/packages/key-management/src/chains/Mina/guards.ts @@ -1,15 +1,9 @@ +import { TransactionBody } from "@mina-js/utils" import type { Mina } from "@palladxyz/mina-core" -export function isConstructedTransaction( - payload: any, -): payload is Mina.ConstructedTransaction { - return ( - payload && - typeof payload === "object" && - "type" in payload && - "to" in payload && - "from" in payload - ) +export function isConstructedTransaction(payload: any) { + const transaction = TransactionBody.parse(payload) + return !!transaction } export function isMessageBody(payload: any): payload is Mina.MessageBody { diff --git a/packages/key-management/src/chains/Mina/signingOperations.ts b/packages/key-management/src/chains/Mina/signingOperations.ts index 49a7c743..0e04f468 100644 --- a/packages/key-management/src/chains/Mina/signingOperations.ts +++ b/packages/key-management/src/chains/Mina/signingOperations.ts @@ -1,6 +1,6 @@ -import type { Mina } from "@palladxyz/mina-core" import Client from "mina-signer" +import { SignableData } from "../../../../mina-core/src/borrowed-types" import * as errors from "../../errors" import type { ChainOperationArgs } from "../../types" import * as util from "./guards" @@ -19,26 +19,18 @@ export function MinaSigningOperations( try { // Perform the specific signing action switch (args.operation) { + case "mina_sign": case "mina_signTransaction": { - if (util.isConstructedTransaction(payload)) { - return minaClient.signTransaction( - payload as Mina.ConstructedTransaction, - privateKey, - ) + if ("message" in payload) { + return minaClient.signTransaction(payload.message, privateKey) } - if (util.isZkAppTransaction(payload)) { - return minaClient.signZkappCommand(payload.command, privateKey) + if ("transaction" in payload) { + return minaClient.signTransaction(payload.transaction, privateKey) } - throw new Error( - `Invalid constructed transaction, the payload is: ${payload}`, - ) - } - - case "mina_sign": { - if (util.isMessageBody(payload)) { - return minaClient.signMessage(payload.message, privateKey) + if ("command" in payload) { + return minaClient.signTransaction(payload.command, privateKey) } - throw new Error("Invalid message body") + throw new Error("Invalid transaction payload") } case "mina_signFields": { diff --git a/packages/key-management/src/chains/Mina/types.ts b/packages/key-management/src/chains/Mina/types.ts index 05f6e3e0..6d5cd67a 100644 --- a/packages/key-management/src/chains/Mina/types.ts +++ b/packages/key-management/src/chains/Mina/types.ts @@ -1,6 +1,14 @@ -import type { BorrowedTypes, Mina } from "@palladxyz/mina-core" +import type { Mina } from "@palladxyz/mina-core" import { Network } from "@palladxyz/pallad-core" +import type { + Nullifier, + SignedFields, + SignedMessage, + SignedTransaction, + TransactionPayload, + ZkAppCommandProperties, +} from "@mina-js/utils" import type { ChainSpecificPayload, KeyPairDerivationOperations, @@ -9,11 +17,10 @@ import { deriveMinaPublicKey } from "./credentialDerivation" import { deriveMinaPrivateKey } from "./keyDerivation" export type MinaSignatureResult = - | Mina.SignedTransaction - | Mina.SignedMessage - | Mina.SignedFields - | Mina.SignedZkAppCommand - | BorrowedTypes.Nullifier + | SignedTransaction + | SignedMessage + | SignedFields + | Nullifier export type MinaSpecificPayload = { network: Network.Mina @@ -65,10 +72,10 @@ export type MinaSessionCredentials = { } export type MinaSignablePayload = - | Mina.ConstructedTransaction + | TransactionPayload + | ZkAppCommandProperties | Mina.MessageBody | Mina.SignableFields - | Mina.SignableZkAppCommand | Mina.CreatableNullifer export class MinaPayload implements ChainSpecificPayload { diff --git a/packages/key-management/src/types.ts b/packages/key-management/src/types.ts index 8a726eaf..4fa5af1d 100644 --- a/packages/key-management/src/types.ts +++ b/packages/key-management/src/types.ts @@ -10,7 +10,6 @@ import { type EthereumDerivationArgs, type EthereumGroupedCredentials, type EthereumSessionCredentials, - type EthereumSignablePayload, type EthereumSignatureResult, type EthereumSpecificArgs, ethKeyPairDerivationOperations, @@ -277,7 +276,7 @@ export type ChainSigningFunction = ( privateKey: ChainPrivateKey, ) => Promise -export type ChainSignablePayload = MinaSignablePayload | EthereumSignablePayload +export type ChainSignablePayload = MinaSignablePayload /*export const chainSigningOperations: Record = { 'Mina': MinaSigningOperations, diff --git a/packages/key-management/test/mina/in-memory-key-agent.test.ts b/packages/key-management/test/mina/in-memory-key-agent.test.ts index deca4a96..0231afb1 100644 --- a/packages/key-management/test/mina/in-memory-key-agent.test.ts +++ b/packages/key-management/test/mina/in-memory-key-agent.test.ts @@ -1,10 +1,11 @@ import { mnemonic } from "@palladxyz/common" -import { Mina } from "@palladxyz/mina-core" +import type { Mina } from "@palladxyz/mina-core" import { Network, constructTransaction } from "@palladxyz/pallad-core" import * as bip32 from "@scure/bip32" import Client from "mina-signer" import { expect } from "vitest" +import { SignedTransaction, TransactionBodySchema } from "@mina-js/utils" import { utf8ToBytes } from "@noble/hashes/utils" //import { emip3encrypt } from '../src/emip3' import { @@ -123,7 +124,7 @@ describe("Mina InMemoryKeyAgent", () => { getPassphrase, true, ) - const transaction: Mina.TransactionBody = { + const transaction = TransactionBodySchema.parse({ to: "B62qjsV6WQwTeEWrNrRRBP6VaaLvQhwWTnFi4WP4LQjGvpfZEumXzxb", from: "B62qjsV6WQwTeEWrNrRRBP6VaaLvQhwWTnFi4WP4LQjGvpfZEumXzxb", fee: 1, @@ -131,19 +132,18 @@ describe("Mina InMemoryKeyAgent", () => { nonce: 0, memo: "hello Bob", validUntil: 321, - type: "payment", - } - const constructedTx: Mina.ConstructedTransaction = constructTransaction( - transaction, - Mina.TransactionType.PAYMENT, - ) + }) try { - await agent.sign(groupedCredential, constructedTx, { - network: Network.Mina, - networkType: "testnet", - operation: "mina_signNotATransaction", - }) + await agent.sign( + groupedCredential, + { transaction }, + { + network: Network.Mina, + networkType: "testnet", + operation: "mina_signNotATransaction", + }, + ) fail("Expected an error but did not get one.") } catch (error) { expect(error.message).toContain("Unsupported private key operation") @@ -161,7 +161,7 @@ describe("Mina InMemoryKeyAgent", () => { getPassphrase, true, ) - const transaction: Mina.TransactionBody = { + const transaction = TransactionBodySchema.parse({ to: "B62qjsV6WQwTeEWrNrRRBP6VaaLvQhwWTnFi4WP4LQjGvpfZEumXzxb", from: "B62qjsV6WQwTeEWrNrRRBP6VaaLvQhwWTnFi4WP4LQjGvpfZEumXzxb", fee: 1, @@ -169,22 +169,19 @@ describe("Mina InMemoryKeyAgent", () => { nonce: 0, memo: "hello Bob", validUntil: 321, - type: "payment", - } - const constructedTx: Mina.ConstructedTransaction = constructTransaction( - transaction, - Mina.TransactionType.PAYMENT, - ) - - const signedTx = await agent.sign(groupedCredential, constructedTx, { - network: Network.Mina, - networkType: "testnet", - operation: "mina_signTransaction", }) - const minaClient = new Client({ network: "testnet" }) - const isVerified = minaClient.verifyTransaction( - signedTx as Mina.SignedTransaction, + + const signedTx = await agent.sign( + groupedCredential, + { transaction }, + { + network: Network.Mina, + networkType: "testnet", + operation: "mina_signTransaction", + }, ) + const minaClient = new Client({ network: "testnet" }) + const isVerified = minaClient.verifyTransaction(signedTx as never) expect(isVerified).toBeTruthy() }) }) diff --git a/packages/key-management/test/mina/key-agent-base.test.ts b/packages/key-management/test/mina/key-agent-base.test.ts index 0568ba33..84debedf 100644 --- a/packages/key-management/test/mina/key-agent-base.test.ts +++ b/packages/key-management/test/mina/key-agent-base.test.ts @@ -1,12 +1,12 @@ import { mnemonic } from "@palladxyz/common" -import { Mina } from "@palladxyz/mina-core" -import { constructTransaction } from "@palladxyz/pallad-core" +import type { Mina } from "@palladxyz/mina-core" import { Network } from "@palladxyz/pallad-core" import * as bip32 from "@scure/bip32" import Client from "mina-signer" import { expect } from "vitest" -import { hexToBytes, utf8ToBytes } from "@noble/hashes/utils" +import { TransactionBodySchema } from "@mina-js/utils" +import { utf8ToBytes } from "@noble/hashes/utils" import type { MinaDerivationArgs } from "../../dist" import { KeyAgentBase } from "../../src/KeyAgentBase" import { @@ -16,7 +16,6 @@ import { import { emip3encrypt } from "../../src/emip3" import { type ChainOperationArgs, - type GetPassphrase, KeyAgentType, type SerializableKeyAgentData, } from "../../src/types" @@ -300,7 +299,7 @@ describe("KeyAgentBase (Mina Functionality)", () => { expectedGroupedCredentials.address, ) - const transaction: Mina.TransactionBody = { + const transaction = TransactionBodySchema.parse({ to: groupedCredential.address, from: groupedCredential.address, fee: 1, @@ -308,17 +307,16 @@ describe("KeyAgentBase (Mina Functionality)", () => { nonce: 0, memo: "hello Bob", validUntil: 321, - type: "payment", - } - const constructedTx: Mina.ConstructedTransaction = constructTransaction( - transaction, - Mina.TransactionType.PAYMENT, - ) - const signedTx = await instance.sign(groupedCredential, constructedTx, { - network: Network.Mina, - networkType: "testnet", - operation: "mina_signTransaction", }) + const signedTx = await instance.sign( + groupedCredential, + { transaction }, + { + network: Network.Mina, + networkType: "testnet", + operation: "mina_signTransaction", + }, + ) const minaClient = new Client({ network: args.networkType }) const isVerified = minaClient.verifyTransaction( signedTx as Mina.SignedTransaction, diff --git a/packages/key-management/test/util/guards.test.ts b/packages/key-management/test/util/guards.test.ts index 060e1949..c733ab55 100644 --- a/packages/key-management/test/util/guards.test.ts +++ b/packages/key-management/test/util/guards.test.ts @@ -1,5 +1,4 @@ -import { Mina } from "@palladxyz/mina-core" -import { constructTransaction } from "@palladxyz/pallad-core" +import type { Mina } from "@palladxyz/mina-core" import { test } from "vitest" import { @@ -10,28 +9,6 @@ import { } from "../../src/chains/Mina/guards" describe("Guard functions tests", () => { - test("isConstructedTransaction", () => { - const transaction: Mina.TransactionBody = { - to: "B62qjsV6WQwTeEWrNrRRBP6VaaLvQhwWTnFi4WP4LQjGvpfZEumXzxb", - from: "B62qjsV6WQwTeEWrNrRRBP6VaaLvQhwWTnFi4WP4LQjGvpfZEumXzxb", - fee: 1, - amount: 100, - nonce: 0, - memo: "hello Bob", - validUntil: 321, - type: "payment", - } - const validPayload: Mina.ConstructedTransaction = constructTransaction( - transaction, - Mina.TransactionType.PAYMENT, - ) - - const invalidPayload = { message: "invalid_payload" } - - expect(isConstructedTransaction(validPayload)).toBeTruthy() - expect(isConstructedTransaction(invalidPayload)).not.toBeTruthy() - }) - test("isMessageBody", () => { const validPayload: Mina.MessageBody = { message: "anyMessage", diff --git a/packages/mina-core/src/Mina/Transaction.ts b/packages/mina-core/src/Mina/Transaction.ts index 43c673ba..f32e5f88 100644 --- a/packages/mina-core/src/Mina/Transaction.ts +++ b/packages/mina-core/src/Mina/Transaction.ts @@ -43,15 +43,6 @@ export type KeyPair = { */ export type NetworkType = "mainnet" | "testnet" -export type ConstructedTransaction = - | (BorrowedTypes.Payment & { type: TransactionType.PAYMENT }) - | (BorrowedTypes.StakeDelegation & { - type: TransactionType.STAKE_DELEGATION - }) - -export type SignedTransaction = - BorrowedTypes.SignedLegacy - /** * transaction hash as base64 string */ diff --git a/packages/mina-core/src/Providers/tx-submit-provider/types.ts b/packages/mina-core/src/Providers/tx-submit-provider/types.ts index 2d51f33d..251b4c65 100644 --- a/packages/mina-core/src/Providers/tx-submit-provider/types.ts +++ b/packages/mina-core/src/Providers/tx-submit-provider/types.ts @@ -1,5 +1,5 @@ import type { BorrowedTypes } from "../.." -import type { TransactionBody, TransactionType } from "../../Mina" +import type { TransactionType } from "../../Mina" import type { Provider } from "../Provider" export type SubmitTxArgs = { @@ -7,15 +7,6 @@ export type SubmitTxArgs = { | BorrowedTypes.SignedLegacy | BorrowedTypes.SignedLegacy type: TransactionType - transactionDetails: { - fee: TransactionBody["fee"] - to: TransactionBody["to"] - from: TransactionBody["from"] - nonce: TransactionBody["nonce"] - memo: TransactionBody["memo"] - validUntil: TransactionBody["validUntil"] - amount: TransactionBody["amount"] - } } interface TxResult { diff --git a/packages/pallad-core/src/Mina/Providers/tx-submit-provider/types.ts b/packages/pallad-core/src/Mina/Providers/tx-submit-provider/types.ts index cb9fea6e..dee20380 100644 --- a/packages/pallad-core/src/Mina/Providers/tx-submit-provider/types.ts +++ b/packages/pallad-core/src/Mina/Providers/tx-submit-provider/types.ts @@ -7,15 +7,6 @@ export type SubmitTxArgs = { | BorrowedTypes.SignedLegacy | BorrowedTypes.SignedLegacy type: Mina.TransactionType - transactionDetails: { - fee: Mina.TransactionBody["fee"] - to: Mina.TransactionBody["to"] - from: Mina.TransactionBody["from"] - nonce: Mina.TransactionBody["nonce"] - memo: Mina.TransactionBody["memo"] - validUntil: Mina.TransactionBody["validUntil"] - amount: Mina.TransactionBody["amount"] - } } interface TxResult { diff --git a/packages/pallad-core/src/Pallad/index.ts b/packages/pallad-core/src/Pallad/index.ts index 642be1bd..c495e2cf 100644 --- a/packages/pallad-core/src/Pallad/index.ts +++ b/packages/pallad-core/src/Pallad/index.ts @@ -1,3 +1,2 @@ export * from "./providers" -export * from "./transactions" export * from "./utils" diff --git a/packages/pallad-core/src/Pallad/providers/unified-provider.ts b/packages/pallad-core/src/Pallad/providers/unified-provider.ts index 0f139026..c9bcb786 100644 --- a/packages/pallad-core/src/Pallad/providers/unified-provider.ts +++ b/packages/pallad-core/src/Pallad/providers/unified-provider.ts @@ -6,7 +6,6 @@ import type { import type { NodeStatus } from "./node-status-provider" import type { HealthCheckResponse } from "./provider" import type { TxStatus, TxStatusArgs } from "./tx-status-provider" -import type { SubmitTxArgs, SubmitTxResult } from "./tx-submit-provider" import type { Tx } from "./types" export type UnifiedChainProviderConfig = { @@ -23,7 +22,6 @@ export interface UnifiedChainProviderType { args: AccountInfoArgs, ): Promise | undefined> getTransactionStatus?(args: TxStatusArgs): Promise - submitTransaction(args: SubmitTxArgs): Promise // Methods related to ProviderArchive getTransactions(args: TransactionsByAddressesArgs): Promise diff --git a/packages/pallad-core/src/Pallad/transactions/evm/index.ts b/packages/pallad-core/src/Pallad/transactions/evm/index.ts deleted file mode 100644 index 3391c1bf..00000000 --- a/packages/pallad-core/src/Pallad/transactions/evm/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./todo" diff --git a/packages/pallad-core/src/Pallad/transactions/evm/todo.ts b/packages/pallad-core/src/Pallad/transactions/evm/todo.ts deleted file mode 100644 index ed902097..00000000 --- a/packages/pallad-core/src/Pallad/transactions/evm/todo.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function example() { - return true -} diff --git a/packages/pallad-core/src/Pallad/transactions/index.ts b/packages/pallad-core/src/Pallad/transactions/index.ts deleted file mode 100644 index ad74d7bc..00000000 --- a/packages/pallad-core/src/Pallad/transactions/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./mina" diff --git a/packages/pallad-core/src/Pallad/transactions/mina/build-custom-token-tx.ts b/packages/pallad-core/src/Pallad/transactions/mina/build-custom-token-tx.ts deleted file mode 100644 index 5f37ded2..00000000 --- a/packages/pallad-core/src/Pallad/transactions/mina/build-custom-token-tx.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - -// NOTE: this does not work. experimental only. - -import { BorrowedTypes } from "@palladxyz/mina-core" -import { FungibleToken } from "mina-fungible-token" -import { PublicKey, Mina as Chain, UInt64, Transaction } from "o1js" - -export type paymentInfo = { - to: BorrowedTypes.PublicKey - from: BorrowedTypes.PublicKey - amount: BorrowedTypes.UInt64 - tokenAddress: BorrowedTypes.PublicKey - fee: BorrowedTypes.UInt64 -} - -export async function constructCustomTokenPaymentTx( - paymentInfo: paymentInfo - ): Promise { - const tokenAddress = new PublicKey(paymentInfo.tokenAddress) - const from = new PublicKey(paymentInfo.from) - const to = new PublicKey(paymentInfo.to) - const fee = new UInt64(paymentInfo.fee) - const amount = new UInt64(paymentInfo.amount) - await FungibleToken.compile() - const token = new FungibleToken(tokenAddress) - - const tx = await Chain.transaction({ sender: from, fee }, async () => { - await token.transfer(from, to, amount) - }) - - return tx - } - - }*/ diff --git a/packages/pallad-core/src/Pallad/transactions/mina/build-mina-tx.ts b/packages/pallad-core/src/Pallad/transactions/mina/build-mina-tx.ts deleted file mode 100644 index 2569cf58..00000000 --- a/packages/pallad-core/src/Pallad/transactions/mina/build-mina-tx.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { type BorrowedTypes, Mina } from "@palladxyz/mina-core" - -// Low-level Mina constructTx API for spending Mina only. - -/** - * Constructs a payment transaction object. - * @param payment The payment transaction body. - * @returns The constructed payment transaction object. - */ -export function constructPaymentTx( - payment: Mina.TransactionBody, -): BorrowedTypes.Payment { - const sendFee = BigInt(payment.fee) - const sendAmount = payment.amount ? BigInt(payment.amount) : BigInt(0) - const memo = payment.memo || "" - const validUntil = payment.validUntil - ? BigInt(payment.validUntil) - : BigInt(4294967295) // Mina Signer has a defaultValidUntil = '4294967295'; - - return { - to: payment.to, - from: payment.from, - amount: sendAmount, - fee: sendFee, - nonce: BigInt(payment.nonce), - memo: memo, - validUntil: validUntil, - } -} - -/** - * Constructs a delegation transaction object. - * @param delegation The delegation transaction body. - * @returns The constructed delegation transaction object. - */ -export function constructDelegationTx( - delegation: Mina.TransactionBody, -): BorrowedTypes.StakeDelegation { - const sendFee = BigInt(delegation.fee) - const memo = delegation.memo || "" - const validUntil = delegation.validUntil - ? BigInt(delegation.validUntil) - : BigInt(4294967295) // Mina Signer has a defaultValidUntil = '4294967295'; - - return { - to: delegation.to, - from: delegation.from, - fee: sendFee, - nonce: BigInt(delegation.nonce), - memo: memo, - validUntil: validUntil, - } -} - -/** - * Constructs a transaction object based on the kind of transaction. - * @param transaction The transaction body. - * @param transactionType The kind of transaction. - * @returns The constructed transaction object. - */ -export function constructTransaction( - transaction: Mina.TransactionBody, - transactionType: Mina.TransactionType, -): Mina.ConstructedTransaction { - switch (transactionType) { - case Mina.TransactionType.PAYMENT: - return { - ...constructPaymentTx(transaction), - type: Mina.TransactionType.PAYMENT, - } - case Mina.TransactionType.STAKE_DELEGATION: - return { - ...constructDelegationTx(transaction), - type: Mina.TransactionType.STAKE_DELEGATION, - } - default: - throw new Error("Unsupported transaction kind") - } -} diff --git a/packages/pallad-core/src/Pallad/transactions/mina/build-tx.ts b/packages/pallad-core/src/Pallad/transactions/mina/build-tx.ts deleted file mode 100644 index 1cd5ab58..00000000 --- a/packages/pallad-core/src/Pallad/transactions/mina/build-tx.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { Mina } from "@palladxyz/mina-core" - -import { constructTransaction } from "./build-mina-tx" - -// high-level Mina constructTx API -export type constructTxArgs = { - transaction: Mina.TransactionBody - transactionType: Mina.TransactionType -} - -export function constructTx(args: constructTxArgs) { - /* - TODO: there are three kinds of transactionType: - // from mina-core - export enum TransactionType { - PAYMENT = 'payment', - STAKE_DELEGATION = 'delegation', - ZK_APP = 'zkApp' - // Add other kinds of transactions as needed - } - - we need to consider the case where this is a custom token spend (i.e. ZK_APP kind) too. - */ - return constructTransaction(args.transaction, args.transactionType) -} diff --git a/packages/pallad-core/src/Pallad/transactions/mina/index.ts b/packages/pallad-core/src/Pallad/transactions/mina/index.ts deleted file mode 100644 index 15359503..00000000 --- a/packages/pallad-core/src/Pallad/transactions/mina/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./build-mina-tx" -export * from "./build-tx" diff --git a/packages/providers/src/mina-node/index.ts b/packages/providers/src/mina-node/index.ts index 6d88051d..ba1b953e 100644 --- a/packages/providers/src/mina-node/index.ts +++ b/packages/providers/src/mina-node/index.ts @@ -1,5 +1,4 @@ export * from "./account-info" export * from "./chain-history" export * from "./node-status" -export * from "./tx-submit" export * from "./types" diff --git a/packages/providers/src/mina-node/tx-submit/index.ts b/packages/providers/src/mina-node/tx-submit/index.ts deleted file mode 100644 index 02d7126e..00000000 --- a/packages/providers/src/mina-node/tx-submit/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./tx-submit-provider" diff --git a/packages/providers/src/mina-node/tx-submit/mutations.ts b/packages/providers/src/mina-node/tx-submit/mutations.ts deleted file mode 100644 index 46ea7385..00000000 --- a/packages/providers/src/mina-node/tx-submit/mutations.ts +++ /dev/null @@ -1,167 +0,0 @@ -export function getTxSend(isRawSignature: boolean): string { - if (isRawSignature) { - return ` - mutation sendTx($fee:UInt64!, $amount:UInt64!, - $to: PublicKey!, $from: PublicKey!, $nonce:UInt32, $memo: String, - $validUntil: UInt32, $rawSignature: String!) { - sendPayment( - input: { - fee: $fee, - amount: $amount, - to: $to, - from: $from, - memo: $memo, - nonce: $nonce, - validUntil: $validUntil - }, - signature: {rawSignature: $rawSignature}) { - payment { - __typename - id - hash - kind - nonce - source { - publicKey - } - receiver { - publicKey - } - feePayer { - publicKey - } - validUntil - token - amount - feeToken - fee - memo - } - } - } - ` - } - return ` - mutation sendTx($fee:UInt64!, $amount:UInt64!, - $to: PublicKey!, $from: PublicKey!, $nonce:UInt32, $memo: String, - $validUntil: UInt32, $scalar: String!, $field: String!) { - sendPayment( - input: { - fee: $fee, - amount: $amount, - to: $to, - from: $from, - memo: $memo, - nonce: $nonce, - validUntil: $validUntil - }, - signature: {field: $field, scalar: $scalar}) { - payment { - __typename - id - hash - kind - nonce - source { - publicKey - } - receiver { - publicKey - } - feePayer { - publicKey - } - validUntil - token - amount - feeToken - fee - memo - } - } - } - ` -} - -export function getStakeTxSend(isRawSignature: boolean): string { - if (isRawSignature) { - return ` - mutation stakeTx($fee:UInt64!, - $to: PublicKey!, $from: PublicKey!, $nonce:UInt32, $memo: String, - $validUntil: UInt32, $rawSignature: String!) { - sendDelegation( - input: { - fee: $fee, - to: $to, - from: $from, - memo: $memo, - nonce: $nonce, - validUntil: $validUntil - }, - signature: {rawSignature: $rawSignature}) { - delegation { - __typename - id - hash - kind - nonce - source { - publicKey - } - receiver { - publicKey - } - feePayer { - publicKey - } - validUntil - token - amount - feeToken - fee - memo - } - } - } - ` - } - return ` - mutation stakeTx($fee:UInt64!, - $to: PublicKey!, $from: PublicKey!, $nonce:UInt32, $memo: String, - $validUntil: UInt32, $scalar: String!, $field: String!) { - sendDelegation( - input: { - fee: $fee, - to: $to, - from: $from, - memo: $memo, - nonce: $nonce, - validUntil: $validUntil - }, - signature: {field: $field, scalar: $scalar}) { - delegation { - __typename - id - hash - kind - nonce - source { - publicKey - } - receiver { - publicKey - } - feePayer { - publicKey - } - validUntil - token - amount - feeToken - fee - memo - } - } - } - ` -} diff --git a/packages/providers/src/mina-node/tx-submit/tx-submit-provider.ts b/packages/providers/src/mina-node/tx-submit/tx-submit-provider.ts deleted file mode 100644 index 3746662c..00000000 --- a/packages/providers/src/mina-node/tx-submit/tx-submit-provider.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { - SubmitTxArgs, - SubmitTxResult, - TxSubmitProvider, -} from "@palladxyz/mina-core" - -import { createGraphQLRequest } from "../utils/fetch-utils" -import { healthCheck } from "../utils/health-check-utils" -import { getStakeTxSend, getTxSend } from "./mutations" - -export const createTxSubmitProvider = (url: string): TxSubmitProvider => { - const submitTx = async (args: SubmitTxArgs): Promise => { - const isRawSignature = typeof args.signedTransaction.signature === "string" - let mutation - if (args.type === "payment") { - mutation = ` - ${getTxSend(isRawSignature)} - ` - } else { - mutation = ` - ${getStakeTxSend(isRawSignature)} - ` - } - - const variables = { - ...args.transactionDetails, - field: args.signedTransaction.signature.field, - scalar: args.signedTransaction.signature.scalar, - } - - const fetchGraphQL = createGraphQLRequest(url) - const result = await fetchGraphQL(mutation, variables) - - if (!result.ok) { - throw new Error(result.message) - } - - const submissionResult = result.data - - return submissionResult - } - - return { - healthCheck: () => healthCheck(url), - submitTx, - } -} diff --git a/packages/providers/src/unified-providers/index.ts b/packages/providers/src/unified-providers/index.ts index ed2da738..8df71406 100644 --- a/packages/providers/src/unified-providers/index.ts +++ b/packages/providers/src/unified-providers/index.ts @@ -1,6 +1,5 @@ export * from "./account-info-provider" export * from "./chain-history-provider" export * from "./node-status-provider" -export * from "./tx-submit-provider" export * from "./types" export * from "./unified-provider" diff --git a/packages/providers/src/unified-providers/tx-submit-provider.ts b/packages/providers/src/unified-providers/tx-submit-provider.ts deleted file mode 100644 index b2fec9fe..00000000 --- a/packages/providers/src/unified-providers/tx-submit-provider.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { - HealthCheckResponse, - SubmitTxArgs, - SubmitTxResult, - TxSubmitProvider, -} from "@palladxyz/pallad-core" - -import { createTxSubmitProvider as mn } from "../mina-node" -import type { ProviderConfig } from "./types" - -export const createTxSubmitProvider = ( - config: ProviderConfig, -): TxSubmitProvider => { - // TODO: make the underlyingProvider creation a util function - const underlyingProvider = mn(config.nodeEndpoint.url) - - const submitTx = async (args: SubmitTxArgs): Promise => { - // Delegate the call to the underlying provider's getAccountInfo method - return await underlyingProvider.submitTx(args) - } - - const healthCheck = (): Promise => { - // Delegate the call to the underlying provider's healthCheck method - return underlyingProvider.healthCheck() - } - - return { - submitTx, - healthCheck, - } -} diff --git a/packages/providers/src/unified-providers/unified-provider.ts b/packages/providers/src/unified-providers/unified-provider.ts index 59fa0296..771e7a3c 100644 --- a/packages/providers/src/unified-providers/unified-provider.ts +++ b/packages/providers/src/unified-providers/unified-provider.ts @@ -2,7 +2,6 @@ import type { AccountInfo, AccountInfoArgs, HealthCheckResponse, - SubmitTxArgs, TransactionsByAddressesArgs, Tx, UnifiedChainProviderType, @@ -11,7 +10,6 @@ import type { import { createAccountInfoProvider } from "./account-info-provider" import { createChainHistoryProvider } from "./chain-history-provider" import { createNodeStatusProvider } from "./node-status-provider" -import { createTxSubmitProvider } from "./tx-submit-provider" import type { ProviderConfig } from "./types" export const createChainProvider = ( @@ -29,10 +27,6 @@ export const createChainProvider = ( )) as Tx[] } - const submitTransaction = async (args: SubmitTxArgs) => { - return await createTxSubmitProvider(config).submitTx(args) - } - const getNodeStatus = async () => { return await createNodeStatusProvider(config).getNodeStatus() } @@ -67,7 +61,6 @@ export const createChainProvider = ( return { getAccountInfo, getTransactions, - submitTransaction, getNodeStatus, healthCheck, } diff --git a/packages/vault/package.json b/packages/vault/package.json index c6df3d23..97aaa591 100644 --- a/packages/vault/package.json +++ b/packages/vault/package.json @@ -18,6 +18,7 @@ "cleanup": "rimraf node_modules dist .turbo" }, "dependencies": { + "@mina-js/klesia-sdk": "https://pkg.pr.new/palladians/mina-js/@mina-js/klesia-sdk@7fe60e8", "@noble/hashes": "1.4.0", "@palladxyz/key-management": "workspace:*", "@palladxyz/mina-core": "workspace:*", diff --git a/packages/vault/src/objects/objectsState.ts b/packages/vault/src/objects/objectsState.ts index ddaf233f..22e9b0c1 100644 --- a/packages/vault/src/objects/objectsState.ts +++ b/packages/vault/src/objects/objectsState.ts @@ -1,30 +1,21 @@ /** * @file Represents the state definitions related to objects (for example issued credentials). */ +import type { Json } from "@mina-js/utils" import type { SearchQuery } from "../utils/utils" export type ObjectName = string export type StoredObject = undefined | object -/** - * Type representing the basic state of an object. - * @typedef {Object} SingleObjectState - */ -// TODO: remove "ObjectName" from type it is redundant -export type SingleObjectState = { - objectName: ObjectName - object: StoredObject -} - /** * Constant representing the initial object state * @typedef {Object} */ -export const initialObjectState: SingleObjectState = { +export const initialObjectState: Json = { objectName: "", - object: undefined, + object: {}, } -export type ObjectsStore = Record +export type ObjectsStore = Record /** * Type representing the store's state and actions combined. * @typedef {Object} ObjectStore @@ -32,10 +23,8 @@ export type ObjectsStore = Record export type ObjectStore = { objects: ObjectsStore ensureObject: (objectName: ObjectName) => void - setObject: (objectState: SingleObjectState) => void - getObject: ( - objectName: ObjectName, - ) => SingleObjectState | typeof initialObjectState + setObject: (objectState: Json) => void + getObject: (objectName: ObjectName) => Json | typeof initialObjectState removeObject: (name: ObjectName) => void searchObjects(query: SearchQuery, props?: string[]): StoredObject[] clear: () => void diff --git a/packages/vault/src/objects/objectsStore.ts b/packages/vault/src/objects/objectsStore.ts index c9e162c3..f69f4da4 100644 --- a/packages/vault/src/objects/objectsStore.ts +++ b/packages/vault/src/objects/objectsStore.ts @@ -11,16 +11,16 @@ export const objectSlice: StateCreator = (set, get) => ({ set( produce((draft) => { draft.objects[objectName] = draft.objects[objectName] || { - ...initialObjectState, + ...(initialObjectState as object), objectName, } }), ) }, - setObject: (objectState) => { + setObject: (objectState: any) => { const { objectName } = objectState set((current) => - produce(current, (draft) => { + produce(current, (draft: any) => { draft.objects[objectName] = { ...draft.objects[objectName], ...objectState, @@ -43,7 +43,7 @@ export const objectSlice: StateCreator = (set, get) => ({ // TODO: improve const { objects } = get() const objectsStatesArray = Object.values(objects) - const objectsArray = objectsStatesArray.map((obj) => obj.object) + const objectsArray = objectsStatesArray.map((obj: any) => obj?.object) const filteredObjects = objectsArray.filter((object) => { if (!object) { return false diff --git a/packages/vault/src/vault/utils/index.ts b/packages/vault/src/vault/utils/index.ts index 6b6618e5..5a54fc37 100644 --- a/packages/vault/src/vault/utils/index.ts +++ b/packages/vault/src/vault/utils/index.ts @@ -5,7 +5,6 @@ export * from "./get-wallet-transactions" export * from "./restart-wallet" export * from "./restore-wallet" export * from "./sign" -export * from "./submit-tx" export * from "./switch-network" export * from "./sync-account-info" export * from "./sync-transactions" diff --git a/packages/vault/src/vault/utils/submit-tx.ts b/packages/vault/src/vault/utils/submit-tx.ts deleted file mode 100644 index 5252c7e8..00000000 --- a/packages/vault/src/vault/utils/submit-tx.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { SubmitTxArgs } from "@palladxyz/pallad-core" -import { createChainProvider } from "@palladxyz/providers" - -import { AddressError } from "../../lib/Errors" - -export async function submitTxHelper(get: any, submitTxArgs: SubmitTxArgs) { - const { getCurrentNetworkInfo, getCurrentWallet, _syncTransactions } = get() - const providerConfig = getCurrentNetworkInfo() - const provider = createChainProvider(providerConfig) - const publicKey = getCurrentWallet().credential.credential?.address - if (!publicKey) - throw new AddressError("Wallet address is undefined in submitTx method") - const txResult = await provider.submitTransaction(submitTxArgs) - await _syncTransactions(providerConfig, publicKey) - return txResult -} diff --git a/packages/vault/src/vault/vaultState.ts b/packages/vault/src/vault/vaultState.ts index c4fc2915..2ae0b760 100644 --- a/packages/vault/src/vault/vaultState.ts +++ b/packages/vault/src/vault/vaultState.ts @@ -12,10 +12,10 @@ import type { PalladNetworkNames, SubmitTxArgs, Tx, - constructTxArgs, } from "@palladxyz/pallad-core" import type { ProviderConfig } from "@palladxyz/providers" +import type { TransactionBody } from "@mina-js/utils" import type { CredentialName, SingleCredentialState, @@ -80,8 +80,8 @@ export type GlobalVaultActions = { args: ChainOperationArgs, getPassphrase: GetPassphrase, ) => Promise - constructTx: (args: constructTxArgs) => unknown - submitTx: (submitTxArgs: SubmitTxArgs) => Promise + constructTx: (args: { transaction: any }) => TransactionBody + submitTx: (submitTxArgs: SubmitTxArgs) => Promise createWallet: (strength?: number) => CreateWalletReturn restoreWallet: ( args: ChainDerivationArgs, diff --git a/packages/vault/src/vault/vaultStore.ts b/packages/vault/src/vault/vaultStore.ts index 43ebfaad..a459b00f 100644 --- a/packages/vault/src/vault/vaultStore.ts +++ b/packages/vault/src/vault/vaultStore.ts @@ -1,13 +1,11 @@ import { generateMnemonicWords } from "@palladxyz/key-management" -import { - Network, - PalladNetworkNames, - constructTransaction, -} from "@palladxyz/pallad-core" +import { Network, PalladNetworkNames } from "@palladxyz/pallad-core" import { produce } from "immer" import { create } from "zustand" import { createJSONStorage, persist } from "zustand/middleware" +import { createClient } from "@mina-js/klesia-sdk" +import { TransactionBodySchema } from "@mina-js/utils" import { type AccountStore, accountSlice } from "../account" import { type CredentialStore, credentialSlice } from "../credentials" import { type KeyAgentStore, KeyAgents, keyAgentSlice } from "../keyAgent" @@ -24,7 +22,6 @@ import { restartWalletHelper, restoreWalletHelper, signHelper, - submitTxHelper, switchNetworkHelper, syncAccountHelper, syncTransactionHelper, @@ -128,11 +125,24 @@ export const useVault = create< return await signHelper(get, signable, args, getPassphrase) }, constructTx: (args) => { - // TODO: agnostic construct transaction Util that takes any transaction type - return constructTransaction(args.transaction, args.transactionType) - }, - submitTx: async (submitTxArgs) => { - return await submitTxHelper(get, submitTxArgs) + return TransactionBodySchema.parse(args.transaction) + }, + submitTx: async ({ signedTransaction, type }) => { + const { currentNetworkName } = get() + const networkName = + currentNetworkName === "Mainnet" ? "mainnet" : "devnet" + const klesia = createClient({ network: networkName }) + const { result } = await klesia.request<"mina_sendTransaction">({ + method: "mina_sendTransaction", + params: [ + { + input: signedTransaction.data, + signature: signedTransaction.signature, + }, + type as "payment" | "delegation", + ], + }) + return result }, createWallet: (strength = 128) => { const mnemonic = generateMnemonicWords(strength) diff --git a/packages/vault/test/objects/objectsStore.test.ts b/packages/vault/test/objects/objectsStore.test.ts index 0b7db46f..44329ed7 100644 --- a/packages/vault/test/objects/objectsStore.test.ts +++ b/packages/vault/test/objects/objectsStore.test.ts @@ -3,10 +3,10 @@ import { Network } from "@palladxyz/pallad-core" import { act, renderHook } from "@testing-library/react" import { afterEach, beforeEach, describe, expect, it } from "vitest" +import type { Json } from "@mina-js/utils" import { DEFAULT_OBJECTS, type ObjectName, - type SingleObjectState, type StoredObject, useVault, } from "../../src" @@ -14,8 +14,8 @@ import { describe("ObjectStore", () => { let object: GroupedCredentials let objectName: ObjectName - let objectState: SingleObjectState - let objectStateTwo: SingleObjectState + let objectState: Json + let objectStateTwo: Json let objectTwo: object beforeEach(() => { @@ -81,7 +81,7 @@ describe("ObjectStore", () => { }) it("should add one object and remove one from store", () => { - let storedObject: SingleObjectState | undefined + let storedObject const { result } = renderHook(() => useVault()) act(() => { result.current.setObject(objectState) @@ -93,7 +93,7 @@ describe("ObjectStore", () => { storedObject = result.current.getObject(objectName) }) // check that object is removed - expect(storedObject?.object).toBeUndefined() + expect(storedObject.object).toEqual({}) }) it("should add two objects and search for Mina addresses and return them as an array not as a credential object", () => { let storedObjects: StoredObject[] | undefined diff --git a/packages/web-provider/package.json b/packages/web-provider/package.json index 22482d57..696683bf 100644 --- a/packages/web-provider/package.json +++ b/packages/web-provider/package.json @@ -18,6 +18,8 @@ "cleanup": "rimraf node_modules dist .turbo" }, "dependencies": { + "@mina-js/klesia-sdk": "https://pkg.pr.new/palladians/mina-js/@mina-js/klesia-sdk@7fe60e8", + "@mina-js/providers": "https://pkg.pr.new/palladians/mina-js/@mina-js/providers@7fe60e8", "@noble/hashes": "1.4.0", "@palladxyz/key-management": "workspace:*", "@palladxyz/mina-core": "workspace:*", @@ -28,6 +30,7 @@ "mina-signer": "3.0.7", "mitt": "3.0.1", "superjson": "2.2.1", + "ts-pattern": "5.5.0", "webext-bridge": "6.0.1", "webextension-polyfill": "0.12.0", "zod": "3.23.8" diff --git a/packages/web-provider/src/mina-network/index.ts b/packages/web-provider/src/mina-network/index.ts index f9ce97ae..d24f9766 100644 --- a/packages/web-provider/src/mina-network/index.ts +++ b/packages/web-provider/src/mina-network/index.ts @@ -1,4 +1,3 @@ export * from "./constants" export * from "./mina-provider" -export * from "./types" export * as Validation from "./validation" diff --git a/packages/web-provider/src/mina-network/mina-provider.ts b/packages/web-provider/src/mina-network/mina-provider.ts index abe92dad..1798aa91 100644 --- a/packages/web-provider/src/mina-network/mina-provider.ts +++ b/packages/web-provider/src/mina-network/mina-provider.ts @@ -1,69 +1,49 @@ +import type { + MinaProviderClient, + ProviderRequestParams, +} from "@mina-js/providers" +import type { + Nullifier, + SignedFields, + SignedMessage, + SignedTransaction, +} from "@mina-js/utils" import { utf8ToBytes } from "@noble/hashes/utils" import type { ChainOperationArgs, ChainSignablePayload, - MinaSignablePayload, } from "@palladxyz/key-management" -import { - type BorrowedTypes, - type Mina, - TransactionType, -} from "@palladxyz/mina-core" -import EventEmitter from "eventemitter3" -import type { Validation } from "." +import type { SearchQuery } from "@palladxyz/vault" +import mitt from "mitt" +import { P, match } from "ts-pattern" import { showUserPrompt } from "../utils/prompts" -import { type VaultService, vaultService } from "../vault-service" -import type { ConnectOps, RequestArguments } from "../web-provider-types" -import { serializeField, serializeGroup, serializeTransaction } from "./utils" - -export type RpcMethod = - | "mina_sendTransaction" - | "mina_accounts" - | "mina_requestAccounts" - | "mina_getBalance" - | "mina_sign" - | "mina_signTransaction" - | "mina_createNullifier" - | "mina_getState" - | "mina_setState" +import { createVaultService } from "../vault-service" +import type { ConnectOps } from "../web-provider-types" +import { serializeField, serializeGroup } from "./utils" export function getMinaChainId(chains: string[]) { return Number(chains[0]?.split(":")[1]) } -export class MinaProvider extends EventEmitter { - private static instance: MinaProvider - public accounts: string[] = [] - public chainId: string | undefined = undefined - private _vault: VaultService - - private userPrompt: typeof showUserPrompt - - constructor() { - super() - this._vault = vaultService - this.userPrompt = showUserPrompt - } - - static async init() { - const provider = new MinaProvider() - await provider.initialize() - return provider - } - - public static async getInstance() { - if (!MinaProvider.instance) { - MinaProvider.instance = await MinaProvider.init() - } - return MinaProvider.instance - } +const createProviderRpcError = (code: number, message: string) => { + const error = new Error(`${code} - ${message}`) + error.name = "ProviderRpcError" + return error +} - private async initialize() { - this.chainId = await this._vault.getChainId() - } +const verifyInitialized = async () => { + const { PalladApp } = await chrome.storage.local.get("PalladApp") + if (!PalladApp) return false + return !PalladApp.includes("UNINITIALIZED") +} - async unlockWallet() { - const passphrase = await this.userPrompt({ +export const createMinaProvider = async (): Promise< + MinaProviderClient & { emit: (type: any, event: any) => void } +> => { + const _emitter = mitt() + const _vault = createVaultService() + const unlockWallet = async () => { + const passphrase = await showUserPrompt({ inputType: "password", metadata: { title: "Unlock your wallet", @@ -71,34 +51,33 @@ export class MinaProvider extends EventEmitter { rejectButtonLabel: "Cancel", }, }) - if (passphrase === null) - throw this.createProviderRpcError(4100, "Unauthorized") - await this._vault.unlockWallet(passphrase as string) + if (passphrase === null) throw createProviderRpcError(4100, "Unauthorized") + await _vault.unlockWallet(passphrase) } - - async verifyInitialized() { - const { PalladApp } = await chrome.storage.local.get("PalladApp") - if (!PalladApp) return false - return !PalladApp.includes("UNINITIALIZED") + const connectOrigin = async (opts: ConnectOps) => { + try { + if (await _vault.getEnabled({ origin: opts.origin })) + throw createProviderRpcError(4100, "Already enabled.") + if (!opts.chains) { + const defaultChainId = await _vault.getChainId() + if (!defaultChainId) { + throw createProviderRpcError(4100, "Chain ID is undefined.") + } + } + await _vault.setEnabled({ origin: opts.origin }) + } catch (error) { + console.error("Error during connection:", error) + } } - - async checkAndUnlock() { - const locked = await this._vault.isLocked() + const checkAndUnlockWallet = async () => { + const locked = await _vault.isLocked() if (locked === true) { - await this.unlockWallet() + await unlockWallet() } } - - private createProviderRpcError(code: number, message: string) { - const error = new Error(`${code} - ${message}`) - error.name = "ProviderRpcError" - return error - } - // TODO: add ConnectOps as an optional parameter - public async enable({ origin }: { origin: string }) { - // check if wallet is locked first - await this.checkAndUnlock() - const userConfirmed = await this.userPrompt({ + const enableOrigin = async ({ origin }: { origin: string }) => { + await checkAndUnlockWallet() + const userConfirmed = await showUserPrompt({ inputType: "confirmation", metadata: { title: "Connection request.", @@ -107,45 +86,12 @@ export class MinaProvider extends EventEmitter { emitConnected: true, }) if (!userConfirmed) { - // should this emit an error event? - throw this.createProviderRpcError(4001, "User Rejected Request") - } - await this.connect({ origin }) - // TODO: perform 'mina_requestAccounts' method - return await this._vault.getAccounts() - } - public async connect(opts: ConnectOps) { - try { - // Step 1: Check if already connected. - if (await this._vault.getEnabled({ origin: opts.origin })) - throw this.createProviderRpcError(4100, "Already enabled.") - // Step 2: Attempt to connect to a chain. - if (!opts.chains) { - // Try to connect to the default chain -- this is actually the current chain the wallet is connected to not the default chain - const defaultChainId = await this._vault.getChainId() - if (!defaultChainId) { - throw this.createProviderRpcError(4100, "Chain ID is undefined.") - } - this.chainId = defaultChainId - } else if (opts.chains && opts.chains.length > 0) { - this.chainId = String(opts.chains[0]) - } else { - throw this.createProviderRpcError(4901, "Chain Disconnected") - } - await this._vault.setEnabled({ origin: opts.origin }) - } catch (error) { - // Handle any errors that occurred during connection. - console.error("Error during connection:", error) - // Additional error handling as needed + throw createProviderRpcError(4001, "User Rejected Request") } + await connectOrigin({ origin }) + return _vault.getAccounts() } - public requestAccounts() { - return this.accounts - } - public async isConnected({ origin }: { origin: string }) { - return await this._vault.getEnabled({ origin }) - } - private async sign({ + const signPayload = async ({ signable, operationArgs, passphrase, @@ -153,234 +99,241 @@ export class MinaProvider extends EventEmitter { signable: ChainSignablePayload operationArgs: ChainOperationArgs passphrase: string - }) { + }) => { try { - return await this._vault.sign(signable, operationArgs, () => + return (await _vault.sign(signable, operationArgs, () => utf8ToBytes(passphrase), - ) + )) as T } catch (error) { - throw this.createProviderRpcError(4100, "Unauthorized") + throw createProviderRpcError(4100, "Unauthorized") } } - public async request(args: RequestArguments): Promise { - // Step 1: Check if request instantiator is in blocked list. - const { params } = args - const requestOrigin = params.origin - if (!requestOrigin) { - throw this.createProviderRpcError(4100, "Unauthorized") - } - if (await this._vault.isBlocked({ origin: requestOrigin })) { - throw this.createProviderRpcError(4100, "Unauthorized") - } - const initialized = await this.verifyInitialized() - if (!initialized) { - throw this.createProviderRpcError(4100, "Unauthorized") - } - - const locked = await this._vault.isLocked() - const enabled = await this._vault.getEnabled({ origin: requestOrigin }) - if ((locked || !enabled) && args.method === "mina_accounts") { - return [] as T - } - - if (locked) await this.unlockWallet() - if (!enabled) await this.enable({ origin: requestOrigin }) - - switch (args.method) { - case "mina_accounts": - case "mina_requestAccounts": - return (await this._vault.getAccounts()) as unknown as T - case "mina_addChain": { - throw this.createProviderRpcError(4200, "Unsupported Method") + return { + on: _emitter.on, + removeListener: _emitter.off, + emit: _emitter.emit, + request: async (args) => { + const typedArgs: ProviderRequestParams = args + const { context } = typedArgs + const { origin } = context as Record + if (!origin) { + throw createProviderRpcError(4100, "Unauthorized") + } + if (await _vault.isBlocked({ origin })) { + throw createProviderRpcError(4100, "Unauthorized") } - case "mina_switchChain": { - throw this.createProviderRpcError(4200, "Unsupported Method") + const initialized = await verifyInitialized() + if (!initialized) { + throw createProviderRpcError(4100, "Unauthorized") } - case "mina_requestNetwork": { - { - const userConfirmed = await this.userPrompt({ + const locked = await _vault.isLocked() + const enabled = await _vault.getEnabled({ origin }) + if ((locked || !enabled) && typedArgs.method === "mina_accounts") { + return [] + } + if (locked) await unlockWallet() + if (!enabled) await enableOrigin({ origin }) + return match(typedArgs) + .with( + { method: P.union("mina_accounts", "mina_requestAccounts") }, + _vault.getAccounts, + ) + .with({ method: "mina_getBalance" }, _vault.getBalance) + .with({ method: "mina_chainId" }, _vault.getChainId) + .with({ method: "mina_addChain" }, () => + createProviderRpcError(4200, "Unsupported Method"), + ) + .with({ method: "mina_switchChain" }, () => + createProviderRpcError(4200, "Unsupported Method"), + ) + .with({ method: "mina_chainInformation" }, async () => { + const userConfirmed = await showUserPrompt({ inputType: "confirmation", metadata: { title: "Request to current Mina network information.", - payload: JSON.stringify(params), }, }) if (!userConfirmed) { - throw this.createProviderRpcError(4001, "User Rejected Request") + throw createProviderRpcError(4001, "User Rejected Request") } - const requestNetworkResponse = await this._vault.requestNetwork() - return requestNetworkResponse as unknown as T - } - } - case "mina_sign": - case "mina_createNullifier": - case "mina_signFields": - case "mina_signTransaction": { - const passphrase = await this.userPrompt({ - inputType: "password", - metadata: { - title: "Signature request", - payload: JSON.stringify(params), - }, + const requestNetworkResponse = await _vault.requestNetwork() + return requestNetworkResponse }) - if (passphrase === null) - throw this.createProviderRpcError(4100, "Unauthorized.") - const operationArgs: ChainOperationArgs = { - operation: args.method, - network: "Mina", - } - - if (args.method === "mina_signFields") { - const requestData = params as Validation.SignFieldsData - const signable = { - fields: requestData.fields.map((item: any) => { - // Convert to BigInt only if the item is a number - if (typeof item === "number") { - return BigInt(item) - } - // If it's not a number, return the item as is - return item - }), - } as MinaSignablePayload - const signedResponse = (await this.sign({ - signable, - operationArgs, - passphrase, - })) as Mina.SignedFields - // serialise the response if mina_signFields - const serializedResponseData = signedResponse.data.map((item) => { - // Convert to BigInt only if the item is a number - if (typeof item === "bigint") { - return String(item) + .with( + { method: P.union("mina_sign", "mina_signTransaction") }, + async (signatureRequest) => { + const passphrase = await showUserPrompt({ + inputType: "password", + metadata: { + title: "Signature request", + payload: JSON.stringify(signatureRequest.params), + }, + }) + if (passphrase === null) + throw createProviderRpcError(4100, "Unauthorized.") + const operationArgs: ChainOperationArgs = { + operation: args.method, + network: "Mina", } - // If it's not a number, return the item as is - return item + return match(signatureRequest) + .with({ method: "mina_signTransaction" }, ({ params }) => { + const payload = params?.[0] + if (!payload) { + throw createProviderRpcError(4100, "Unauthorized.") + } + if ("transaction" in payload) { + return signPayload({ + signable: payload, + operationArgs, + passphrase, + }) + } + return signPayload({ + signable: payload, + operationArgs, + passphrase, + }) + }) + .with({ method: "mina_sign" }, ({ params }) => { + const [message] = params as string[] + return signPayload({ + signable: { message: message as string }, + operationArgs, + passphrase, + }) + }) + .exhaustive() + }, + ) + .with( + { method: P.union("mina_createNullifier", "mina_signFields") }, + async (signatureRequest) => { + const passphrase = await showUserPrompt({ + inputType: "password", + metadata: { + title: "Signature request", + payload: JSON.stringify(signatureRequest.params), + }, + }) + if (passphrase === null) + throw createProviderRpcError(4100, "Unauthorized.") + const operationArgs: ChainOperationArgs = { + operation: args.method, + network: "Mina", + } + return match(signatureRequest) + .with({ method: "mina_signFields" }, async ({ params }) => { + const [fields] = params + if (!fields || !fields.length) { + throw createProviderRpcError(4100, "Unauthorized.") + } + const signedResponse = await signPayload({ + signable: { + fields: fields.map((item: any) => BigInt(item)), + }, + operationArgs, + passphrase, + }) + const serializedResponseData = signedResponse.data.map( + (item) => { + if (typeof item === "bigint") { + return String(item) + } + return item + }, + ) + const seriliasedResponse = { + ...signedResponse, + data: serializedResponseData, + } + return seriliasedResponse + }) + .with({ method: "mina_createNullifier" }, async ({ params }) => { + const [message] = params + if (!message || !message.length) { + throw createProviderRpcError(4100, "Unauthorized.") + } + const signedResponse = await signPayload({ + signable: { + message: message.map((item: any) => BigInt(item)), + }, + operationArgs, + passphrase, + }) + // serialise the response if mina_createNullifier + const serializedResponseData = { + publicKey: serializeGroup(signedResponse.publicKey), + public: { + nullifier: serializeGroup(signedResponse.public.nullifier), + s: serializeField(signedResponse.public.s), + }, + private: { + c: serializeField(signedResponse.private.c), + g_r: serializeGroup(signedResponse.private.g_r), + h_m_pk_r: serializeGroup(signedResponse.private.h_m_pk_r), + }, + } + return serializedResponseData + }) + .exhaustive() + }, + ) + .with({ method: "mina_getState" }, async ({ params }) => { + const [query, props] = params + const credentials = await _vault.getState( + query as SearchQuery, + props as string[], + ) + const confirmation = await showUserPrompt({ + inputType: "confirmation", + metadata: { + title: "Credential read request", + payload: JSON.stringify({ ...params, credentials }), + }, }) - const seriliasedResponse = { - ...signedResponse, - data: serializedResponseData, + if (!confirmation) { + throw createProviderRpcError(4001, "User Rejected Request") } - return seriliasedResponse as unknown as T - } - if (args.method === "mina_createNullifier") { - const requestData = params as Validation.CreateNullifierData - const signable = { - message: requestData.message.map((item) => { - // Convert to BigInt only if the item is a number - if (typeof item === "number") { - return BigInt(item) - } - // If it's not a number, return the item as is - return item - }), - } as MinaSignablePayload - const signedResponse = (await this.sign({ - signable, - operationArgs, - passphrase, - })) as BorrowedTypes.Nullifier - // serialise the response if mina_createNullifier - const serializedResponseData = { - publicKey: serializeGroup(signedResponse.publicKey), - public: { - nullifier: serializeGroup(signedResponse.public.nullifier), - s: serializeField(signedResponse.public.s), - }, - private: { - c: serializeField(signedResponse.private.c), - g_r: serializeGroup(signedResponse.private.g_r), - h_m_pk_r: serializeGroup(signedResponse.private.h_m_pk_r), + return credentials + }) + .with({ method: "mina_setState" }, async ({ params }) => { + const payload = params?.[0] + if (!payload) throw createProviderRpcError(4000, "Invalid Request") + const confirmation = await showUserPrompt({ + inputType: "confirmation", + metadata: { + title: "Credential write request", + payload: JSON.stringify(payload), }, + }) + if (!confirmation) { + throw createProviderRpcError(4001, "User Rejected Request") } - - return serializedResponseData as unknown as T - } - if (args.method === "mina_signTransaction") { - const requestData = params as Validation.SignTransactionData - const signable = serializeTransaction({ - ...requestData.transaction, - memo: requestData.transaction.memo ?? "", - validUntil: requestData.transaction.validUntil ?? 4294967295, - amount: requestData.transaction.amount ?? 0, - type: TransactionType.PAYMENT, - }) as MinaSignablePayload - return (await this.sign({ - signable, - operationArgs, - passphrase, - })) as unknown as T - } - const requestData = params as Validation.SignMessageData - return (await this.sign({ - signable: requestData, - operationArgs, - passphrase, - })) as unknown as T - } - case "mina_getBalance": - return (await this._vault.getBalance()) as unknown as T - - case "mina_getState": { - const { query, props } = params as Validation.GetStateData - const credentials = await this._vault.getState(query as any, props) - const confirmation = await this.userPrompt({ - inputType: "confirmation", - metadata: { - title: "Credential read request", - payload: JSON.stringify({ ...params, credentials }), - }, + await _vault.setState(payload) + return { success: true } }) - if (!confirmation) { - throw this.createProviderRpcError(4001, "User Rejected Request") - } - return credentials as unknown as T - } - - case "mina_setState": { - const confirmation = await this.userPrompt({ - inputType: "confirmation", - metadata: { - title: "Credential write request", - payload: JSON.stringify(params), - }, - }) - if (!confirmation) { - throw this.createProviderRpcError(4001, "User Rejected Request") - } - const requestData = params as Validation.SetStateData - await this._vault.setState(requestData as any) - return { success: true } as unknown as T - } - - case "mina_chainId": { - return (await this._vault.getChainId()) as unknown as T - } - - case "mina_sendTransaction": { - const requestData = params as Validation.SendTransactionData - const passphrase = await this.userPrompt({ - inputType: "password", - metadata: { - title: "Send transaction request", - payload: JSON.stringify(params), - }, + .with({ method: "mina_sendTransaction" }, async ({ params }) => { + const [payload] = params + if (!payload) throw createProviderRpcError(4000, "Invalid Request") + const passphrase = await showUserPrompt({ + inputType: "password", + metadata: { + title: "Send transaction request", + payload: JSON.stringify(payload), + }, + }) + if (passphrase === null) + throw createProviderRpcError(4100, "Unauthorized.") + try { + return _vault.submitTransaction(payload) + } catch (error: any) { + throw createProviderRpcError( + 4100, + "Unauthorized. Coudldn't broadscast transaction. Make sure nonce is correct.", + ) + } }) - if (passphrase === null) - throw this.createProviderRpcError(4100, "Unauthorized.") - try { - return (await this._vault.submitTransaction( - requestData, - )) as unknown as T - } catch (error: any) { - throw this.createProviderRpcError( - 4100, - "Unauthorized. Coudldn't broadscast transaction. Make sure nonce is correct.", - ) - } - } - - default: - throw this.createProviderRpcError(4200, "Unsupported Method") - } + .exhaustive() as any + }, } } diff --git a/packages/web-provider/src/mina-network/types.ts b/packages/web-provider/src/mina-network/types.ts deleted file mode 100644 index 697dc4cc..00000000 --- a/packages/web-provider/src/mina-network/types.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { - ProviderAccounts, - ProviderChainId, - ProviderConnectInfo, - ProviderMessage, - ProviderRpcError, - RequestArguments, -} from "../web-provider-types" -import type { MinaProvider } from "./mina-provider" - -export interface MinaRpcProviderMap { - [chainId: string]: IMinaProviderBase -} - -// originally the EIP1193Provider interface -export interface IMinaProviderBase { - // connection event - on( - event: "connect", - listener: (info: ProviderConnectInfo) => void, - ): MinaProvider - // disconnection event - on( - event: "disconnect", - listener: (error: ProviderRpcError) => void, - ): MinaProvider - // arbitrary messages - on( - event: "message", - listener: (message: ProviderMessage) => void, - ): MinaProvider - // chain changed event - on( - event: "chainChanged", - listener: (chainId: ProviderChainId) => void, - ): MinaProvider - // accounts changed event - on( - event: "accountsChanged", - listener: (accounts: ProviderAccounts) => void, - ): MinaProvider - // make an Ethereum RPC method call. - request(args: RequestArguments): Promise -} diff --git a/packages/web-provider/src/mina-network/validation.ts b/packages/web-provider/src/mina-network/validation.ts index 28aa3204..957ae9a6 100644 --- a/packages/web-provider/src/mina-network/validation.ts +++ b/packages/web-provider/src/mina-network/validation.ts @@ -7,31 +7,31 @@ const jsonSchema: z.ZodType = z.lazy(() => z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)]), ) -export const originSchema = z.string().url() +export const OriginSchema = z.string().url() -export type Origin = z.infer +export type Origin = z.infer -export const requestSchema = z +export const RequestSchema = z .object({ - origin: originSchema, + origin: OriginSchema, }) .strict() -export type RequestData = z.infer +export type RequestData = z.infer -export const signFieldsRequestSchema = requestSchema.extend({ +export const signFieldsRequestSchema = RequestSchema.extend({ fields: z.array(z.coerce.number()), }) export type SignFieldsData = z.infer -export const signMessageRequestSchema = requestSchema.extend({ +export const signMessageRequestSchema = RequestSchema.extend({ message: z.string(), }) export type SignMessageData = z.infer -export const createNullifierRequestSchema = requestSchema.extend({ +export const createNullifierRequestSchema = RequestSchema.extend({ message: z.array(z.coerce.number()), }) @@ -64,27 +64,27 @@ export const signedTransactionSchema = z.object({ signature: signatureSchema, }) -export const signTransactionRequestSchema = requestSchema.extend({ +export const signTransactionRequestSchema = RequestSchema.extend({ transaction: transactionSchema.strict(), }) export type SignTransactionData = z.infer -export const sendTransactionRequestSchema = requestSchema.extend({ +export const sendTransactionRequestSchema = RequestSchema.extend({ signedTransaction: signedTransactionSchema.strict(), transactionType: z.enum(["payment", "delegation", "zkapp"]), }) export type SendTransactionData = z.infer -export const setStateRequestSchema = requestSchema.extend({ +export const setStateRequestSchema = RequestSchema.extend({ objectName: z.string(), object: jsonSchema, }) export type SetStateData = z.infer -export const getStateRequestSchema = requestSchema.extend({ +export const getStateRequestSchema = RequestSchema.extend({ query: jsonSchema, props: z.array(z.string()), }) diff --git a/packages/web-provider/src/utils/guards.ts b/packages/web-provider/src/utils/guards.ts index 63366cfa..5073853b 100644 --- a/packages/web-provider/src/utils/guards.ts +++ b/packages/web-provider/src/utils/guards.ts @@ -1,12 +1,10 @@ -import type { SingleObjectState } from "@palladxyz/vault" +import type { Json } from "@mina-js/utils" -import type { RequestArguments } from "../web-provider-types" - -export function hasQueryAndProps(obj: any): obj is RequestArguments { +export function hasQueryAndProps(obj: any) { return obj && typeof obj === "object" && "query" in obj && "props" in obj } -export function hasObjectProps(obj: any): obj is SingleObjectState { +export function hasObjectProps(obj: any): obj is Json { // Check for the existence of both 'objectName' and 'object' properties return ( obj && typeof obj === "object" && "objectName" in obj && "object" in obj diff --git a/packages/web-provider/src/utils/prompts.ts b/packages/web-provider/src/utils/prompts.ts index 8e8237d8..107f30ea 100644 --- a/packages/web-provider/src/utils/prompts.ts +++ b/packages/web-provider/src/utils/prompts.ts @@ -6,7 +6,7 @@ type Metadata = { payload?: string } -export const showUserPrompt = async ({ +export const showUserPrompt = async ({ inputType, metadata, emitConnected = false, @@ -14,8 +14,8 @@ export const showUserPrompt = async ({ inputType: InputType metadata: Metadata emitConnected?: boolean -}) => { - return new Promise((resolve, reject) => { +}): Promise => { + return new Promise((resolve, reject) => { chrome.windows .create({ url: "prompt.html", @@ -32,7 +32,7 @@ export const showUserPrompt = async ({ return reject(new Error("4001 - User Rejected Request")) } if (inputType === "confirmation") { - if (response.userConfirmed) return resolve(true) + if (response.userConfirmed) return resolve(true as any) return reject(new Error("4001 - User Rejected Request")) } if (response.userInput.length > 0) diff --git a/packages/web-provider/src/vault-service/types.ts b/packages/web-provider/src/vault-service/types.ts index 53ef86a7..d9b5b725 100644 --- a/packages/web-provider/src/vault-service/types.ts +++ b/packages/web-provider/src/vault-service/types.ts @@ -3,9 +3,10 @@ import type { ChainSignablePayload, GetPassphrase, } from "@palladxyz/key-management" -import type { SingleObjectState } from "@palladxyz/vault" -import type { ZkAppUrl } from "./vault-service" +import type { Json, SignedTransaction } from "@mina-js/utils" +import type { ProviderConfig } from "@palladxyz/providers" +import type { SearchQuery, StoredObject } from "@palladxyz/vault" export interface IVaultService { getAccounts(): Promise @@ -17,10 +18,22 @@ export interface IVaultService { getBalance(): Promise getChainId(): Promise getChainIds(): Promise - setState(state: SingleObjectState): Promise - getEnabled({ origin }: { origin: ZkAppUrl }): Promise - setEnabled({ origin }: { origin: ZkAppUrl }): Promise + setState(state: Json): Promise + getEnabled({ origin }: { origin: string }): Promise + setEnabled({ origin }: { origin: string }): Promise switchNetwork(network: string): Promise isLocked(): Promise - // Add other method signatures as needed + submitTransaction( + signedTransaction: SignedTransaction, + ): Promise<{ hash: string }> + getState(params: SearchQuery, props?: string[]): Promise + isBlocked({ origin }: { origin: string }): Promise + addChain( + providerConfig: ProviderConfig, + ): Promise<{ networkName: string; chainId: string }> + switchChain( + chainId: string, + ): Promise<{ chainId: string; networkName: string }> + requestNetwork(): Promise<{ chainId: string; networkName: string }> + unlockWallet(spendingPassword: string): Promise } diff --git a/packages/web-provider/src/vault-service/vault-service.ts b/packages/web-provider/src/vault-service/vault-service.ts index e35106e0..800afb50 100644 --- a/packages/web-provider/src/vault-service/vault-service.ts +++ b/packages/web-provider/src/vault-service/vault-service.ts @@ -1,21 +1,13 @@ -import type { - ChainSignablePayload, - GetPassphrase, -} from "@palladxyz/key-management" +import { createClient } from "@mina-js/klesia-sdk" import type { ChainOperationArgs } from "@palladxyz/key-management" import type { ProviderConfig } from "@palladxyz/providers" import { securePersistence, sessionPersistence } from "@palladxyz/vault" import { usePendingTransactionStore } from "@palladxyz/vault" -import { - type SearchQuery, - type SingleObjectState, - useVault, -} from "@palladxyz/vault" +import { useVault } from "@palladxyz/vault" import dayjs from "dayjs" import Client from "mina-signer" -import type { Mina } from "@palladxyz/mina-core" -import type { Validation } from ".." +import type { SignedTransaction } from "@mina-js/utils" import { chainIdToNetwork } from "../utils" import type { IVaultService } from "./types" @@ -24,23 +16,15 @@ export enum AuthorizationState { BLOCKED = "BLOCKED", } -export type ZkAppUrl = string - -export class VaultService implements IVaultService { - private static instance: VaultService - - private constructor() {} - - public static getInstance() { - if (!VaultService.instance) { - VaultService.instance = new VaultService() - } - return VaultService.instance - } - - async getAccounts() { - await this.rehydrate() - // TODO: handle errors +export const createVaultService = (): IVaultService => { + const rehydrate = useVault.persist.rehydrate + const { currentNetworkName } = useVault.getState() + const network = currentNetworkName === "Mainnet" ? "mainnet" : "devnet" + const networkType = currentNetworkName === "Mainnet" ? "mainnet" : "testnet" + const client = createClient({ network }) + const signer = new Client({ network: networkType }) + const getAccounts = async () => { + await rehydrate() const store = useVault.getState() const credentials = store.credentials const addresses = Object.values(credentials).map( @@ -48,227 +32,187 @@ export class VaultService implements IVaultService { ) return addresses.filter((address) => address !== undefined) as string[] } - - async sign( - signable: ChainSignablePayload, - args: ChainOperationArgs, - getPassphrase: GetPassphrase, - ) { - await this.rehydrate() - const store = useVault.getState() - const { currentNetworkName } = useVault.getState() - const networkType = currentNetworkName === "Mainnet" ? "mainnet" : "testnet" - const networkAwareArgs: ChainOperationArgs = { - ...args, - networkType, - } - return store.sign(signable, networkAwareArgs, getPassphrase) - } - - async submitTransaction(payload: Validation.SendTransactionData) { - await this.rehydrate() - const { addPendingTransaction } = usePendingTransactionStore.getState() - const { currentNetworkName, submitTx, _syncWallet } = useVault.getState() - const network = currentNetworkName === "Mainnet" ? "mainnet" : "testnet" - const accounts = await this.getAccounts() - const publicKey = accounts?.[0] - if (!publicKey) throw new Error("Wallet is not initialized.") - if (publicKey !== payload.signedTransaction.data.from) - throw new Error("Wallet is not initialized.") - const client = new Client({ network }) - const validTransaction = client.verifyTransaction( - payload.signedTransaction as Mina.SignedTransaction, - ) - if (!validTransaction) throw new Error("Invalid transaction.") - const submittable = { - signedTransaction: payload.signedTransaction, - type: payload.transactionType, - transactionDetails: payload.signedTransaction.data, - } - const submittedTx: any = await submitTx(submittable as any) - const hash = - submittedTx?.sendPayment?.payment?.hash ?? - submittedTx?.sendDelegation?.delegation?.hash - addPendingTransaction({ - hash, - expireAt: dayjs().add(8, "hours").toISOString(), - }) - await _syncWallet() - return { hash } - } - - async getState(params: SearchQuery, props?: string[]) { - await this.rehydrate() - const store = useVault.getState() - /* - // the searchObjects method operates with - // storedObjects = result.current.searchObjects(searchQuery, props) - // the search query can contain expected properties of the object - // and the optional props is the required output field - // for example: - - const searchQuery = { - type: 'KYCCredential', - chain: Network.Mina - } - // return props - const props = ['proof'] - // this will return the KYC credential's `proof` field and nothing else - */ - // TODO: we can also implement getObjects instead of searchObjs if necessary - if (props === undefined) { - return store.searchObjects(params) - } - return store.searchObjects(params, props) - } - - async setState(state: SingleObjectState) { - await this.rehydrate() - const store = useVault.getState() - // add the given object to the store - store.setObject(state) - } - - async getEnabled({ origin }: { origin: ZkAppUrl }) { - const { permissions } = await chrome.storage.local.get({ - permissions: true, - }) - return permissions[origin] === AuthorizationState.ALLOWED - } - - async isBlocked({ origin }: { origin: ZkAppUrl }) { - const { permissions } = await chrome.storage.local.get({ - permissions: true, - }) - return permissions[origin] === AuthorizationState.BLOCKED - } - - async setEnabled({ origin }: { origin: ZkAppUrl }) { - const { permissions } = await chrome.storage.local.get({ - permissions: true, - }) - return chrome.storage.local.set({ - permissions: { - ...permissions, - [origin]: AuthorizationState.ALLOWED, - }, - }) + const isLocked = async () => { + await rehydrate() + const authenticated = + ((await securePersistence.getItem("foo")) as unknown) === "bar" + return !authenticated } - - async getBalance() { - await this.rehydrate() + const syncWallet = async () => { + await rehydrate() const store = useVault.getState() - return Number( - // TODO: this doesn't have to be 'MINA' - (store.getCurrentWallet().accountInfo.MINA?.balance.total ?? 0) / 1e9, - ) as number + await store._syncWallet() } - - async addChain(providerConfig: ProviderConfig) { - await this.rehydrate() - const store = useVault.getState() - store.setNetworkInfo(providerConfig.networkName, providerConfig) - return { - networkName: providerConfig.networkName, - chainId: providerConfig.chainId, + const getTxType = (signedTransaction: SignedTransaction) => { + if ("feePayer" in signedTransaction.data) { + return "zkapp" } - } - - async switchChain(chainId: string) { - await this.rehydrate() - const store = useVault.getState() - // check if chainId is in the store - const allChains = store.getChainIds() - if (allChains.includes(chainId)) { - // get the networkName if the chainId exists - const allNetworks = store.allNetworkInfo() - const networkConfig = allNetworks.find( - (network) => network?.chainId === chainId, - ) - if (!networkConfig || !networkConfig.networkName) { - throw new Error( - `Network Configuration is not defined for the chainId: ${chainId}`, + if (signedTransaction.data.amount) { + return "payment" + } + return "delegation" + } + return { + getAccounts, + isLocked, + sign: async (signable, args, getPassphrase) => { + await rehydrate() + const store = useVault.getState() + const { currentNetworkName } = useVault.getState() + const networkType = + currentNetworkName === "Mainnet" ? "mainnet" : "testnet" + const networkAwareArgs: ChainOperationArgs = { + ...args, + networkType, + } + return store.sign(signable, networkAwareArgs, getPassphrase) + }, + submitTransaction: async (payload) => { + await rehydrate() + const { addPendingTransaction } = usePendingTransactionStore.getState() + const { _syncWallet } = useVault.getState() + const accounts = await getAccounts() + const publicKey = accounts?.[0] + if (!publicKey) throw new Error("Wallet is not initialized.") + const validTransaction = signer.verifyTransaction(payload as never) + if (!validTransaction) throw new Error("Invalid transaction.") + const type = getTxType(payload) + const { result } = await client.request<"mina_sendTransaction">({ + method: "mina_sendTransaction", + params: [{ input: payload.data, signature: payload.signature }, type], + }) + addPendingTransaction({ + hash: result, + expireAt: dayjs().add(8, "hours").toISOString(), + }) + await _syncWallet() + return { hash: result } + }, + getState: async (params, props?) => { + await rehydrate() + const store = useVault.getState() + if (props === undefined) { + return store.searchObjects(params) + } + return store.searchObjects(params, props) + }, + setState: async (state) => { + await rehydrate() + const store = useVault.getState() + store.setObject(state) + }, + getEnabled: async ({ origin }) => { + const { permissions } = await chrome.storage.local.get({ + permissions: true, + }) + return permissions[origin] === AuthorizationState.ALLOWED + }, + isBlocked: async ({ origin }) => { + const { permissions } = await chrome.storage.local.get({ + permissions: true, + }) + return permissions[origin] === AuthorizationState.BLOCKED + }, + setEnabled: async ({ origin }) => { + const { permissions } = await chrome.storage.local.get({ + permissions: true, + }) + return chrome.storage.local.set({ + permissions: { + ...permissions, + [origin]: AuthorizationState.ALLOWED, + }, + }) + }, + getBalance: async () => { + await rehydrate() + const store = useVault.getState() + return Number( + (store.getCurrentWallet().accountInfo.MINA?.balance.total ?? 0) / 1e9, + ) as number + }, + addChain: async (providerConfig: ProviderConfig) => { + await rehydrate() + const store = useVault.getState() + store.setNetworkInfo(providerConfig.networkName, providerConfig) + return { + networkName: providerConfig.networkName, + chainId: providerConfig.chainId, + } + }, + switchChain: async (chainId) => { + await rehydrate() + const store = useVault.getState() + // check if chainId is in the store + const allChains = store.getChainIds() + if (allChains.includes(chainId)) { + // get the networkName if the chainId exists + const allNetworks = store.allNetworkInfo() + const networkConfig = allNetworks.find( + (network) => network?.chainId === chainId, ) + if (!networkConfig || !networkConfig.networkName) { + throw new Error( + `Network Configuration is not defined for the chainId: ${chainId}`, + ) + } + await store.switchNetwork(networkConfig?.networkName) + return { chainId, networkName: networkConfig?.networkName } } - await store.switchNetwork(networkConfig?.networkName) - return { chainId: chainId, networkName: networkConfig?.networkName } - } - throw new Error( - "chainId does not exist in the store, please use `addChain` first.", - ) - } - - async requestNetwork() { - await this.rehydrate() - const store = useVault.getState() - // check if chainId is in the store - const chainId = store.getChainId() - if (chainId !== "...") { - // get the networkName if the chainId exists - const allNetworks = store.allNetworkInfo() - const networkConfig = allNetworks.find( - (network) => network?.chainId === chainId, + throw new Error( + "chainId does not exist in the store, please use `addChain` first.", ) - if (!networkConfig || !networkConfig.networkName) { - throw new Error( - `Network Configuration is not defined for the chainId: ${chainId}`, + }, + requestNetwork: async () => { + await rehydrate() + const store = useVault.getState() + // check if chainId is in the store + const chainId = store.getChainId() + if (chainId !== "...") { + // get the networkName if the chainId exists + const allNetworks = store.allNetworkInfo() + const networkConfig = allNetworks.find( + (network) => network?.chainId === chainId, ) + if (!networkConfig || !networkConfig.networkName) { + throw new Error( + `Network Configuration is not defined for the chainId: ${chainId}`, + ) + } + return { chainId: chainId, networkName: networkConfig?.networkName } } - return { chainId: chainId, networkName: networkConfig?.networkName } - } - throw new Error("Something went wrong!") - } - - async getChainId() { - await this.rehydrate() - const store = useVault.getState() - return store.getChainId() - } - - async getChainIds() { - await this.rehydrate() - const store = useVault.getState() - return store.getChainIds() - } - // TODO: deprecate this method - async switchNetwork(network: string) { - await this.rehydrate() - const store = useVault.getState() - // map chainId to network - const networkName = chainIdToNetwork(network) - if (!networkName) { - throw new Error(`Invalid chain id: ${network}`) - } - return store.switchNetwork(networkName) - } - - async isLocked() { - await this.rehydrate() - const authenticated = - ((await securePersistence.getItem("foo")) as unknown) === "bar" - return !authenticated - } - - async unlockWallet(spendingPassword: string) { - await sessionPersistence.setItem("spendingPassword", spendingPassword) - await this.rehydrate() - const locked = await this.isLocked() - if (locked === true) { - console.error("incorrect password.") - } else { - await this.syncWallet() - } - } - - async rehydrate() { - return useVault.persist.rehydrate() - } - - async syncWallet() { - await this.rehydrate() - const store = useVault.getState() - await store._syncWallet() + throw new Error("Something went wrong!") + }, + getChainId: async () => { + await rehydrate() + const store = useVault.getState() + return store.getChainId() + }, + getChainIds: async () => { + await rehydrate() + const store = useVault.getState() + return store.getChainIds() + }, + // TODO: deprecate this method + switchNetwork: async (network) => { + await rehydrate() + const store = useVault.getState() + // map chainId to network + const networkName = chainIdToNetwork(network) + if (!networkName) { + throw new Error(`Invalid chain id: ${network}`) + } + return store.switchNetwork(networkName) + }, + unlockWallet: async (spendingPassword: string) => { + await sessionPersistence.setItem("spendingPassword", spendingPassword) + await rehydrate() + const locked = await isLocked() + if (locked === true) { + console.error("incorrect password.") + } else { + await syncWallet() + } + }, } } - -export const vaultService = VaultService.getInstance() diff --git a/packages/web-provider/src/web-provider-types/data-model.ts b/packages/web-provider/src/web-provider-types/data-model.ts index e50461ad..35b043f6 100644 --- a/packages/web-provider/src/web-provider-types/data-model.ts +++ b/packages/web-provider/src/web-provider-types/data-model.ts @@ -1,4 +1,3 @@ -import type { Validation } from "../mina-network" import type { showUserPrompt } from "../utils" export type NamespacesParams = { @@ -72,19 +71,6 @@ export interface ProviderConnectInfo { readonly chainId: string } -export interface RequestArguments { - method: string - params: - | Validation.RequestData - | Validation.SignFieldsData - | Validation.SignMessageData - | Validation.CreateNullifierData - | Validation.SignTransactionData - | Validation.SendTransactionData - | Validation.SetStateData - | Validation.GetStateData -} - export interface Params { data?: { origin?: string diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 424a0c3a..20fd3130 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@biomejs/biome': specifier: 1.8.3 version: 1.8.3 + '@mina-js/utils': + specifier: https://pkg.pr.new/palladians/mina-js/@mina-js/utils@7fe60e8 + version: https://pkg.pr.new/palladians/mina-js/@mina-js/utils@7fe60e8(typescript@5.5.4) '@testing-library/react': specifier: 16.0.0 version: 16.0.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -65,6 +68,9 @@ importers: apps/extension: dependencies: + '@mina-js/providers': + specifier: https://pkg.pr.new/palladians/mina-js/@mina-js/providers@7fe60e8 + version: https://pkg.pr.new/palladians/mina-js/@mina-js/providers@7fe60e8(typescript@5.5.4) '@palladxyz/common': specifier: workspace:* version: link:../../packages/common @@ -337,7 +343,7 @@ importers: version: 2.4.0 tailwindcss-animate: specifier: 1.0.7 - version: 1.0.7 + version: 1.0.7(tailwindcss@3.4.7(ts-node@10.9.2(@swc/core@1.7.3(@swc/helpers@0.5.12))(@types/node@22.0.0)(typescript@5.5.4))) webext-bridge: specifier: 6.0.1 version: 6.0.1 @@ -350,9 +356,6 @@ importers: yaml: specifier: 2.5.0 version: 2.5.0 - zod: - specifier: 3.23.8 - version: 3.23.8 zustand: specifier: 4.5.4 version: 4.5.4(@types/react@18.3.3)(immer@10.1.1)(react@18.3.1) @@ -426,6 +429,9 @@ importers: vite-plugin-top-level-await: specifier: 1.4.2 version: 1.4.2(@swc/helpers@0.5.12)(rollup@4.19.1)(vite@5.3.5(@types/node@22.0.0)) + zod: + specifier: 3.23.8 + version: 3.23.8 packages/key-management: dependencies: @@ -648,6 +654,9 @@ importers: packages/vault: dependencies: + '@mina-js/klesia-sdk': + specifier: https://pkg.pr.new/palladians/mina-js/@mina-js/klesia-sdk@7fe60e8 + version: https://pkg.pr.new/palladians/mina-js/@mina-js/klesia-sdk@7fe60e8(typescript@5.5.4) '@noble/hashes': specifier: 1.4.0 version: 1.4.0 @@ -721,6 +730,12 @@ importers: packages/web-provider: dependencies: + '@mina-js/klesia-sdk': + specifier: https://pkg.pr.new/palladians/mina-js/@mina-js/klesia-sdk@7fe60e8 + version: https://pkg.pr.new/palladians/mina-js/@mina-js/klesia-sdk@7fe60e8(typescript@5.5.4) + '@mina-js/providers': + specifier: https://pkg.pr.new/palladians/mina-js/@mina-js/providers@7fe60e8 + version: https://pkg.pr.new/palladians/mina-js/@mina-js/providers@7fe60e8(typescript@5.5.4) '@noble/hashes': specifier: 1.4.0 version: 1.4.0 @@ -751,6 +766,9 @@ importers: superjson: specifier: 2.2.1 version: 2.2.1 + ts-pattern: + specifier: 5.5.0 + version: 5.5.0 webext-bridge: specifier: 6.0.1 version: 6.0.1 @@ -1990,6 +2008,24 @@ packages: '@types/react': '>=16' react: '>=16' + '@mina-js/klesia-sdk@https://pkg.pr.new/palladians/mina-js/@mina-js/klesia-sdk@7fe60e8': + resolution: {tarball: https://pkg.pr.new/palladians/mina-js/@mina-js/klesia-sdk@7fe60e8} + version: 0.0.1 + peerDependencies: + typescript: ^5.0.0 + + '@mina-js/providers@https://pkg.pr.new/palladians/mina-js/@mina-js/providers@7fe60e8': + resolution: {tarball: https://pkg.pr.new/palladians/mina-js/@mina-js/providers@7fe60e8} + version: 0.0.1 + peerDependencies: + typescript: ^5.0.0 + + '@mina-js/utils@https://pkg.pr.new/palladians/mina-js/@mina-js/utils@7fe60e8': + resolution: {tarball: https://pkg.pr.new/palladians/mina-js/@mina-js/utils@7fe60e8} + version: 0.0.1 + peerDependencies: + typescript: ^5.0.0 + '@molt/command@0.9.0': resolution: {integrity: sha512-1JI8dAlpqlZoXyKWVQggX7geFNPxBpocHIXQCsnxDjKy+3WX4SGyZVJXuLlqRRrX7FmQCuuMAfx642ovXmPA9g==} @@ -4691,6 +4727,10 @@ packages: hmac-drbg@1.0.1: resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + hono@4.6.8: + resolution: {integrity: sha512-f+2Ec9JAzabT61pglDiLJcF/DjiSefZkjCn9bzm1cYLGkD5ExJ3Jnv93ax9h0bn7UPLHF81KktoyjdQfWI2n1Q==} + engines: {node: '>=16.9.0'} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -7069,6 +7109,9 @@ packages: '@swc/wasm': optional: true + ts-pattern@5.5.0: + resolution: {integrity: sha512-jqbIpTsa/KKTJYWgPNsFNbLVpwCgzXfFJ1ukNn4I8hMwyQzHMJnk/BqWzggB0xpkILuKzaO/aMYhS0SkaJyKXg==} + ts-toolbelt@9.6.0: resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} @@ -9112,6 +9155,24 @@ snapshots: '@types/react': 18.3.3 react: 18.3.1 + '@mina-js/klesia-sdk@https://pkg.pr.new/palladians/mina-js/@mina-js/klesia-sdk@7fe60e8(typescript@5.5.4)': + dependencies: + hono: 4.6.8 + ts-pattern: 5.5.0 + typescript: 5.5.4 + + '@mina-js/providers@https://pkg.pr.new/palladians/mina-js/@mina-js/providers@7fe60e8(typescript@5.5.4)': + dependencies: + '@mina-js/utils': https://pkg.pr.new/palladians/mina-js/@mina-js/utils@7fe60e8(typescript@5.5.4) + typescript: 5.5.4 + zod: 3.23.8 + + '@mina-js/utils@https://pkg.pr.new/palladians/mina-js/@mina-js/utils@7fe60e8(typescript@5.5.4)': + dependencies: + mina-signer: 3.0.7 + typescript: 5.5.4 + zod: 3.23.8 + '@molt/command@0.9.0': dependencies: '@molt/types': 0.2.0 @@ -12093,6 +12154,8 @@ snapshots: minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 + hono@4.6.8: {} + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -14710,8 +14773,6 @@ snapshots: tailwind-merge@2.4.0: {} - tailwindcss-animate@1.0.7: {} - tailwindcss-animate@1.0.7(tailwindcss@3.4.7(ts-node@10.9.2(@swc/core@1.7.3(@swc/helpers@0.5.12))(@types/node@22.0.0)(typescript@5.5.4))): dependencies: tailwindcss: 3.4.7(ts-node@10.9.2(@swc/core@1.7.3(@swc/helpers@0.5.12))(@types/node@22.0.0)(typescript@5.5.4)) @@ -14859,6 +14920,8 @@ snapshots: optionalDependencies: '@swc/core': 1.7.3(@swc/helpers@0.5.12) + ts-pattern@5.5.0: {} + ts-toolbelt@9.6.0: {} tsconfck@3.1.1(typescript@5.5.4):