From f99498d2075203a2205609ba4e0526043f46c7f0 Mon Sep 17 00:00:00 2001 From: Vladyslav Dalechyn Date: Wed, 6 Nov 2024 02:48:34 +0200 Subject: [PATCH] fix: mini app communication (#514) * fix: mini-app communications Had a bug where I've constructed posted messages incorrectly, listened to the window.parent instead of the iframe itself, and didn't post messages via window.ReactNativeWebView in case if it's loaded in a react native webview. * chore: changesets * fix: send tx params to convert bigints to strings * nit: listener fix * fix: postjsonrpc * fix: bigint typings * nit: Button.MiniApp to use action prop * nit: lint * nit: add miniApp alias --- .changeset/tender-radios-mate.md | 5 +++ src/components/Button.tsx | 6 ++-- src/frog-base.tsx | 3 ++ src/utils/getSignatureContext.ts | 7 ++-- .../listenForJsonRpcResponseMessage.ts | 11 ++++--- .../jsonRpc/postJsonRpcRequestMessage.ts | 32 +++++++++++++------ .../postSendTransactionRequestMessage.ts | 25 +++++++++++++-- .../postSignTypedDataRequestMessage.ts | 18 ++++++++++- 8 files changed, 86 insertions(+), 21 deletions(-) create mode 100644 .changeset/tender-radios-mate.md diff --git a/.changeset/tender-radios-mate.md b/.changeset/tender-radios-mate.md new file mode 100644 index 00000000..a39c9de9 --- /dev/null +++ b/.changeset/tender-radios-mate.md @@ -0,0 +1,5 @@ +--- +"frog": patch +--- + +Fixed an issue with incorrect JSON-RPC parameters sent for `sendTransaction` and `signTypedDate` for mini-apps. Also fixed and improved communication between the mini-app and the parent. diff --git a/src/components/Button.tsx b/src/components/Button.tsx index b652d094..384f6c85 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -92,7 +92,7 @@ export function ButtonLink({ } export type ButtonMiniAppProps = ButtonProps & { - href: string + action: string prompt?: boolean } @@ -101,7 +101,7 @@ export function ButtonMiniApp({ children, // @ts-ignore - private index = 1, - href, + action, prompt, }: ButtonMiniAppProps) { return [ @@ -113,7 +113,7 @@ export function ButtonMiniApp({ , , ] as unknown as HtmlEscapedString } diff --git a/src/frog-base.tsx b/src/frog-base.tsx index 7e52034f..18302953 100644 --- a/src/frog-base.tsx +++ b/src/frog-base.tsx @@ -1129,6 +1129,9 @@ export class FrogBase< return this } + miniApp: HandlerInterface = + this.composerAction + route< subPath extends string, subSchema extends Schema, diff --git a/src/utils/getSignatureContext.ts b/src/utils/getSignatureContext.ts index 982362a4..5be47563 100644 --- a/src/utils/getSignatureContext.ts +++ b/src/utils/getSignatureContext.ts @@ -92,8 +92,11 @@ export async function getSignatureContext< domain, types, primaryType, - // @TODO: fix typing - message: message!, + message: JSON.parse( + JSON.stringify(message, (_, v) => + typeof v === 'bigint' ? v.toString() : v, + ), + ), }, } diff --git a/src/web/actions/internal/jsonRpc/listenForJsonRpcResponseMessage.ts b/src/web/actions/internal/jsonRpc/listenForJsonRpcResponseMessage.ts index 79f10c24..5a4bf80e 100644 --- a/src/web/actions/internal/jsonRpc/listenForJsonRpcResponseMessage.ts +++ b/src/web/actions/internal/jsonRpc/listenForJsonRpcResponseMessage.ts @@ -16,12 +16,15 @@ export function listenForJsonRpcResponseMessage( JsonRpcResponseSuccess | JsonRpcResponseFailure >, ) => { - if (event.data.id !== requestId) return - + if ( + event.data.id !== requestId || + !('result' in event.data || 'error' in event.data) + ) + return handler(event.data) } - window.parent.addEventListener('message', listener) + window.addEventListener('message', listener) - return () => window.parent.removeEventListener('message', listener) + return () => window.removeEventListener('message', listener) } diff --git a/src/web/actions/internal/jsonRpc/postJsonRpcRequestMessage.ts b/src/web/actions/internal/jsonRpc/postJsonRpcRequestMessage.ts index 83aeefce..a78193ab 100644 --- a/src/web/actions/internal/jsonRpc/postJsonRpcRequestMessage.ts +++ b/src/web/actions/internal/jsonRpc/postJsonRpcRequestMessage.ts @@ -13,14 +13,28 @@ export function postJsonRpcRequestMessage( ) const requestId = requestIdOverride ?? crypto.randomUUID() - window.parent.postMessage( - { - jsonrpc: '2.0', - id: requestId, - method, - params: parameters, - }, - '*', - ) + const message = { + jsonrpc: '2.0', + id: requestId, + method, + params: parameters, + } + + // ref: https://github.com/react-native-webview/react-native-webview/blob/master/docs/Guide.md#the-windowreactnativewebviewpostmessage-method-and-onmessage-prop + if ( + ( + window as { + ReactNativeWebView?: any + } + ).ReactNativeWebView + ) { + ;( + window as { + ReactNativeWebView?: { postMessage: (msg: string) => void } + } + ).ReactNativeWebView?.postMessage(JSON.stringify(message)) + } else { + window.parent.postMessage(message, '*') + } return requestId } diff --git a/src/web/actions/internal/postSendTransactionRequestMessage.ts b/src/web/actions/internal/postSendTransactionRequestMessage.ts index b1402c2e..9a05f7e0 100644 --- a/src/web/actions/internal/postSendTransactionRequestMessage.ts +++ b/src/web/actions/internal/postSendTransactionRequestMessage.ts @@ -1,4 +1,7 @@ -import type { SendTransactionParameters } from '../../../types/transaction.js' +import type { + EthSendTransactionParameters, + SendTransactionParameters, +} from '../../../types/transaction.js' import { type PostJsonRpcRequestMessageReturnType, postJsonRpcRequestMessage, @@ -12,9 +15,27 @@ export function postSendTransactionRequestMessage( parameters: SendTransactionRequestMessageParameters, requestIdOverride?: string, ) { + const { chainId, attribution, abi, data, gas, to, value } = parameters + + const sendTransactionParams: EthSendTransactionParameters = { + abi, + data, + to, + } + + if (gas) sendTransactionParams.gas = gas.toString() + if (value) sendTransactionParams.value = value.toString() + return postJsonRpcRequestMessage( 'fc_requestWalletAction', - parameters, + { + action: { + method: 'eth_sendTransaction', + attribution, + chainId, + params: sendTransactionParams, + }, + }, requestIdOverride, ) } diff --git a/src/web/actions/internal/postSignTypedDataRequestMessage.ts b/src/web/actions/internal/postSignTypedDataRequestMessage.ts index ddb0cc20..d58395bb 100644 --- a/src/web/actions/internal/postSignTypedDataRequestMessage.ts +++ b/src/web/actions/internal/postSignTypedDataRequestMessage.ts @@ -19,9 +19,25 @@ export function postSignTypedDataRequestMessage< parameters: SignTypedDataRequestMessageParameters, requestIdOverride?: string, ) { + const { chainId, domain, message, types, primaryType } = parameters return postJsonRpcRequestMessage( 'fc_requestWalletAction', - parameters, + { + action: { + method: 'eth_signTypedData_v4', + chainId, + params: { + domain, + message: JSON.parse( + JSON.stringify(message, (_, v) => + typeof v === 'bigint' ? v.toString() : v, + ), + ), + types, + primaryType, + }, + }, + }, requestIdOverride, ) }