Skip to content

Commit

Permalink
chore(private-credential): add contract result handling
Browse files Browse the repository at this point in the history
  • Loading branch information
martonmoro committed Nov 11, 2024
1 parent 636ec20 commit 755c120
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 98 deletions.
23 changes: 13 additions & 10 deletions apps/extension/src/sandbox/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import { Add } from "@palladco/contracts"
import { Credential } from "mina-credentials"
import { serializeError } from "serialize-error"
import { match } from "ts-pattern"
import yaml from "yaml"
import { z } from "zod"

const EventTypeSchema = z.enum(["run"])
const ContractTypeSchema = z.enum(["add", "validate-credential"])

type ValidationResult = {
type: "validate-credential-result"
success: boolean
credential?: string
result?: string
error?: string
}

const recoverOriginalPayload = (sanitizedPayload: string) => {
const parsedYaml = yaml.parse(sanitizedPayload)
return JSON.stringify(parsedYaml)
}

window.addEventListener("message", async (event) => {
console.log(event.data)
const type = EventTypeSchema.parse(event.data.type)
Expand All @@ -34,24 +40,21 @@ window.addEventListener("message", async (event) => {
})
.with("validate-credential", async () => {
try {
const credentialInput = event.data.payload
const credentialDeserialized = Credential.fromJSON(credentialInput)
const sanitizedPayload = event.data.payload
const originalPayload = recoverOriginalPayload(sanitizedPayload)
const credentialDeserialized = Credential.fromJSON(originalPayload)
await Credential.validate(credentialDeserialized)

// Use the existing ValidationResult type structure
const result: ValidationResult = {
type: "validate-credential-result",
success: true,
credential: Credential.toJSON(credentialDeserialized),
result: Credential.toJSON(credentialDeserialized),
}

window.parent.postMessage(result, "*")
} catch (error: any) {
const result: ValidationResult = {
type: "validate-credential-result",
success: false,
error:
error instanceof Error ? error.message : "Validation Error",
error: serializeError(error),
}
window.parent.postMessage(result, "*")
}
Expand Down
21 changes: 19 additions & 2 deletions packages/features/src/web-connector/routes/web-connector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import yaml from "yaml"
import type { UserInputForm } from "../types"
import { WebConnectorView } from "../views/web-connector"

type ContractResult = {
type?: string
result?: string
error?: string
}

const sanitizePayload = async (payload: string) => {
const parsedPayload = JSON.parse(payload) as Record<string, any>
const yamlPayload = yaml.stringify(parsedPayload)
Expand Down Expand Up @@ -59,7 +65,7 @@ export const WebConnectorRoute = () => {
}
}
const onSubmit: SubmitHandler<
UserInputForm & { contractResult?: Json }
UserInputForm & { contractResult?: ContractResult }
> = async ({ userInput, contractResult }) => {
const { id } = await windows.getCurrent()
await runtime.sendMessage({
Expand All @@ -82,7 +88,9 @@ export const WebConnectorRoute = () => {
setLoading(false)
}
}
const confirm = async ({ contractResult }: { contractResult?: Json }) => {
const confirm = async ({
contractResult,
}: { contractResult?: ContractResult }) => {
const { id } = await windows.getCurrent()
await runtime.sendMessage({
userConfirmed: true,
Expand Down Expand Up @@ -111,6 +119,15 @@ export const WebConnectorRoute = () => {
window.close()
}
const eventListener = (event: MessageEvent) => {
if (event.data.type === "validate-credential-result") {
return confirm({
contractResult: {
type: event.data.type,
result: event.data.result,
error: event.data.error,
},
})
}
if (request.inputType === "confirmation")
return confirm({ contractResult: event.data.result })
return onSubmit({
Expand Down
206 changes: 125 additions & 81 deletions packages/web-provider/src/mina-network/mina-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,29 @@ const verifyInitialized = async () => {
return !PalladApp.includes("UNINITIALIZED")
}

const getPromptValue = async <T extends boolean | string>(
promptPromise: Promise<{ value: T; contractResult?: any }>,
): Promise<T> => {
const { value } = await promptPromise
return value
}

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<string>({
inputType: "password",
metadata: {
title: "Unlock your wallet",
submitButtonLabel: "Unlock",
rejectButtonLabel: "Cancel",
},
})
const passphrase = await getPromptValue(
showUserPrompt<string>({
inputType: "password",
metadata: {
title: "Unlock your wallet",
submitButtonLabel: "Unlock",
rejectButtonLabel: "Cancel",
},
}),
)
if (passphrase === null) throw createProviderRpcError(4100, "Unauthorized")
await _vault.unlockWallet(passphrase)
}
Expand All @@ -77,14 +86,16 @@ export const createMinaProvider = async (): Promise<
}
const enableOrigin = async ({ origin }: { origin: string }) => {
await checkAndUnlockWallet()
const userConfirmed = await showUserPrompt<boolean>({
inputType: "confirmation",
metadata: {
title: "Connection request.",
payload: JSON.stringify({ origin }),
},
emitConnected: true,
})
const userConfirmed = await getPromptValue(
showUserPrompt<boolean>({
inputType: "confirmation",
metadata: {
title: "Connection request.",
payload: JSON.stringify({ origin }),
},
emitConnected: true,
}),
)
if (!userConfirmed) {
throw createProviderRpcError(4001, "User Rejected Request")
}
Expand Down Expand Up @@ -152,26 +163,30 @@ export const createMinaProvider = async (): Promise<
if (!networkIds.includes(networkId)) {
throw createProviderRpcError(4100, "Unauthorized.")
}
const userConfirmed = await showUserPrompt<boolean>({
inputType: "confirmation",
metadata: {
title: "Switch to different chain.",
payload: JSON.stringify({ networkId }),
},
})
const userConfirmed = await getPromptValue(
showUserPrompt<boolean>({
inputType: "confirmation",
metadata: {
title: "Switch to different chain.",
payload: JSON.stringify({ networkId }),
},
}),
)
if (!userConfirmed) {
throw createProviderRpcError(4001, "User Rejected Request")
}
await _vault.switchNetwork(networkId)
return networkId
})
.with({ method: "mina_requestNetwork" }, async () => {
const userConfirmed = await showUserPrompt<boolean>({
inputType: "confirmation",
metadata: {
title: "Request to current Mina network information.",
},
})
const userConfirmed = await getPromptValue(
showUserPrompt<boolean>({
inputType: "confirmation",
metadata: {
title: "Request to current Mina network information.",
},
}),
)
if (!userConfirmed) {
throw createProviderRpcError(4001, "User Rejected Request")
}
Expand All @@ -181,15 +196,16 @@ export const createMinaProvider = async (): Promise<
.with(
{ method: P.union("mina_sign", "mina_signTransaction") },
async (signatureRequest) => {
const passphrase = await showUserPrompt<string>({
inputType: "password",
// TODO: Testing only
contract: "add",
metadata: {
title: "Signature request",
payload: JSON.stringify(signatureRequest.params),
},
})
const passphrase = await getPromptValue(
showUserPrompt<string>({
inputType: "password",
contract: "add",
metadata: {
title: "Signature request",
payload: JSON.stringify(signatureRequest.params),
},
}),
)
if (passphrase === null)
throw createProviderRpcError(4100, "Unauthorized.")
const operationArgs: ChainOperationArgs = {
Expand Down Expand Up @@ -229,13 +245,15 @@ export const createMinaProvider = async (): Promise<
.with(
{ method: P.union("mina_createNullifier", "mina_signFields") },
async (signatureRequest) => {
const passphrase = await showUserPrompt<string>({
inputType: "password",
metadata: {
title: "Signature request",
payload: JSON.stringify(signatureRequest.params),
},
})
const passphrase = await getPromptValue(
showUserPrompt<string>({
inputType: "password",
metadata: {
title: "Signature request",
payload: JSON.stringify(signatureRequest.params),
},
}),
)
if (passphrase === null)
throw createProviderRpcError(4100, "Unauthorized.")
const operationArgs: ChainOperationArgs = {
Expand Down Expand Up @@ -305,13 +323,15 @@ export const createMinaProvider = async (): Promise<
query as SearchQuery,
props as string[],
)
const confirmation = await showUserPrompt<boolean>({
inputType: "confirmation",
metadata: {
title: "Credential read request",
payload: JSON.stringify({ ...params, credentials }),
},
})
const confirmation = await getPromptValue(
showUserPrompt<boolean>({
inputType: "confirmation",
metadata: {
title: "Credential read request",
payload: JSON.stringify({ ...params, credentials }),
},
}),
)
if (!confirmation) {
throw createProviderRpcError(4001, "User Rejected Request")
}
Expand All @@ -320,13 +340,15 @@ export const createMinaProvider = async (): Promise<
.with({ method: "mina_setState" }, async ({ params }) => {
const payload = params?.[0]
if (!payload) throw createProviderRpcError(4000, "Invalid Request")
const confirmation = await showUserPrompt<boolean>({
inputType: "confirmation",
metadata: {
title: "Credential write request",
payload: JSON.stringify(payload),
},
})
const confirmation = await getPromptValue(
showUserPrompt<boolean>({
inputType: "confirmation",
metadata: {
title: "Credential write request",
payload: JSON.stringify(payload),
},
}),
)
if (!confirmation) {
throw createProviderRpcError(4001, "User Rejected Request")
}
Expand All @@ -339,38 +361,60 @@ export const createMinaProvider = async (): Promise<

const stringifiedCredential = JSON.stringify(credential)

const confirmation = await showUserPrompt<boolean>({
inputType: "confirmation",
contract: "validate-credential",
metadata: {
title: "Store private credential request",
payload: stringifiedCredential,
},
})

if (!confirmation) {
throw createProviderRpcError(4001, "User Rejected Request")
}
try {
await _vault.storePrivateCredential(stringifiedCredential)
return { success: true }
const { value: userConfirmed, contractResult } =
await showUserPrompt<boolean>({
inputType: "confirmation",
contract: "validate-credential",
metadata: {
title: "Store private credential request",
payload: stringifiedCredential,
},
})

if (!userConfirmed) {
throw createProviderRpcError(4001, "User Rejected Request")
}

if (contractResult?.error) {
throw createProviderRpcError(
4100,
`Credential validation failed: ${contractResult.error}`,
)
}

if (!contractResult?.result) {
throw createProviderRpcError(4100, "Missing validation result")
}

try {
await _vault.storePrivateCredential(contractResult.result)
return { success: contractResult.result }
} catch (error: any) {
throw createProviderRpcError(
4100,
`Failed to store private credential: ${error}`,
)
}
} catch (error: any) {
throw createProviderRpcError(
4100,
`Failed to store private credential: ${error}`,
error.message || "Failed to validate credential",
)
}
})
.with({ method: "mina_sendTransaction" }, async ({ params }) => {
const [payload] = params
if (!payload) throw createProviderRpcError(4000, "Invalid Request")
const passphrase = await showUserPrompt<string>({
inputType: "password",
metadata: {
title: "Send transaction request",
payload: JSON.stringify(payload),
},
})
const passphrase = await getPromptValue(
showUserPrompt<string>({
inputType: "password",
metadata: {
title: "Send transaction request",
payload: JSON.stringify(payload),
},
}),
)
if (passphrase === null)
throw createProviderRpcError(4100, "Unauthorized.")
try {
Expand Down
Loading

0 comments on commit 755c120

Please sign in to comment.