diff --git a/App.tsx b/App.tsx index 0a5aa30..5697b09 100644 --- a/App.tsx +++ b/App.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { Button, Snackbar } from 'react-native-paper'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; @@ -21,7 +21,6 @@ import useInitialization, { web3wallet, } from './utils/wallet-connect/WalletConnectUtils'; import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Lib'; -import { useAccounts } from './context/AccountsContext'; import { getSignParamsMessage } from './utils/wallet-connect/Helpers'; import { useRequests } from './context/RequestContext'; @@ -33,7 +32,6 @@ const App = (): React.JSX.Element => { const navigation = useNavigation>(); - const { accounts } = useAccounts(); const { requestSession, setRequestSession } = useRequests(); const [modalVisible, setModalVisible] = useState(false); @@ -42,13 +40,6 @@ const App = (): React.JSX.Element => { SignClientTypes.EventArguments['session_proposal'] | undefined >(); - const currentEthAddresses = useMemo(() => { - if (accounts.ethAccounts.length > 0) { - return accounts.ethAccounts.map(account => account.address); - } - return []; - }, [accounts]); - const onSessionProposal = useCallback( (proposal: SignClientTypes.EventArguments['session_proposal']) => { setModalVisible(true); @@ -61,28 +52,49 @@ const App = (): React.JSX.Element => { async (requestEvent: SignClientTypes.EventArguments['session_request']) => { const { topic, params } = requestEvent; const { request } = params; - const address = request.params[1]; - const message = getSignParamsMessage(request.params); + const requestSessionData = web3wallet.engine.signClient.session.get(topic); + setRequestSession(requestSessionData); + switch (request.method) { - case EIP155_SIGNING_METHODS.ETH_SIGN: case EIP155_SIGNING_METHODS.PERSONAL_SIGN: - setRequestSession(requestSessionData); - if (address && message) { - navigation.navigate('SignRequest', { - network: 'eth', - address, - message, - requestEvent, - requestSession, - }); - } - return; + navigation.navigate('SignRequest', { + network: 'eth', + address: request.params[1], + message: getSignParamsMessage(request.params), + requestEvent, + requestSession, + }); + + break; + // TODO: Debug signDirect + case 'cosmos_signDirect': + navigation.navigate('SignRequest', { + network: 'cosmos', + address: request.params.signerAddress, + message: request.params.signDoc.bodyBytes, + requestEvent, + requestSession, + }); + + break; + case 'cosmos_signAmino': + navigation.navigate('SignRequest', { + network: 'cosmos', + address: request.params.signerAddress, + message: request.params.signDoc.memo, + requestEvent, + requestSession, + }); + + break; + default: + throw new Error('Invalid method'); } }, - [requestSession, setRequestSession], + [requestSession, setRequestSession, navigation], ); useEffect(() => { web3wallet?.on('session_proposal', onSessionProposal); @@ -158,7 +170,6 @@ const App = (): React.JSX.Element => { setModalVisible={setModalVisible} currentProposal={currentProposal} setCurrentProposal={setCurrentProposal} - currentEthAddresses={currentEthAddresses} setToastVisible={setToastVisible} /> { + const { accounts } = useAccounts(); + const url = currentProposal?.params?.proposer?.metadata.url; - const methods = currentProposal?.params?.requiredNamespaces.eip155.methods; - const events = currentProposal?.params?.requiredNamespaces.eip155.events; - const chains = currentProposal?.params?.requiredNamespaces.eip155.chains; - const icon = currentProposal?.params.proposer.metadata.icons[0]; + const icon = currentProposal?.params.proposer?.metadata.icons[0]; + + const [walletConnectData, setWalletConnectData] = useState<{ + walletConnectMethods: string[]; + walletConnectEvents: string[]; + walletConnectChains: string[]; + }>({ + walletConnectMethods: [], + walletConnectEvents: [], + walletConnectChains: [], + }); + + useEffect(() => { + if (!currentProposal) { + return; + } + const { params } = currentProposal; + const { requiredNamespaces } = params; + Object.keys(requiredNamespaces).forEach(key => { + switch (key) { + case 'eip155': + const { + methods: ethMethods, + events: ethEvents, + chains: ethChains, + } = currentProposal?.params?.requiredNamespaces.eip155; + + setWalletConnectData(prevData => { + return { + walletConnectMethods: [ + ...prevData.walletConnectMethods, + ...ethMethods, + ], + walletConnectEvents: [ + ...prevData.walletConnectEvents, + ...ethEvents, + ], + walletConnectChains: ethChains + ? [...prevData.walletConnectChains, ...ethChains] + : [...prevData.walletConnectChains], + }; + }); + break; + case 'cosmos': + const { + methods: cosmosMethods, + events: cosmosEvents, + chains: cosmosChains, + } = currentProposal?.params?.requiredNamespaces.cosmos; + + setWalletConnectData(prevData => { + return { + walletConnectMethods: [ + ...prevData.walletConnectMethods, + ...cosmosMethods, + ], + walletConnectEvents: [ + ...prevData.walletConnectEvents, + ...cosmosEvents, + ], + walletConnectChains: cosmosChains + ? [...prevData.walletConnectChains, ...cosmosChains] + : [...prevData.walletConnectChains], + }; + }); + break; + default: + throw new Error(`${key} not supported`); + } + }); + }, [currentProposal]); const handleAccept = async () => { if (currentProposal) { const { id, params } = currentProposal; const { requiredNamespaces, relays } = params; const namespaces: SessionTypes.Namespaces = {}; + Object.keys(requiredNamespaces).forEach(key => { - const accounts: string[] = []; + let currentAddresses: string[]; + + switch (key) { + case 'eip155': + if (accounts.ethAccounts.length > 0) { + currentAddresses = accounts.ethAccounts.map( + account => account.address, + ); + } + break; + case 'cosmos': + if (accounts.cosmosAccounts.length > 0) { + currentAddresses = accounts.cosmosAccounts.map( + account => account.address, + ); + } + break; + default: + throw new Error(`${key} not supported`); + } + + const namespaceAccounts: string[] = []; requiredNamespaces[key].chains!.map((chain: any) => { - currentEthAddresses.map(acc => accounts.push(`${chain}:${acc}`)); + currentAddresses.map(acc => + namespaceAccounts.push(`${chain}:${acc}`), + ); }); namespaces[key] = { - accounts, + accounts: namespaceAccounts, methods: requiredNamespaces[key].methods, events: requiredNamespaces[key].events, }; @@ -50,6 +143,11 @@ const PairingModal = ({ setModalVisible(false); setToastVisible(true); setCurrentProposal(undefined); + setWalletConnectData({ + walletConnectMethods: [], + walletConnectEvents: [], + walletConnectChains: [], + }); } }; @@ -63,6 +161,11 @@ const PairingModal = ({ setModalVisible(false); setCurrentProposal(undefined); + setWalletConnectData({ + walletConnectMethods: [], + walletConnectEvents: [], + walletConnectChains: [], + }); } }; @@ -80,11 +183,11 @@ const PairingModal = ({ {url} Connect to this site? - Chains: {chains} + Chains: {walletConnectData.walletConnectChains} Methods Requested: - {methods?.map(method => ( + {walletConnectData.walletConnectMethods.map(method => ( {method} @@ -93,7 +196,7 @@ const PairingModal = ({ Events Requested: - {events?.map(event => ( + {walletConnectData.walletConnectEvents.map(event => ( {event} diff --git a/components/SignRequest.tsx b/components/SignRequest.tsx index 92f088a..df13712 100644 --- a/components/SignRequest.tsx +++ b/components/SignRequest.tsx @@ -14,9 +14,9 @@ import styles from '../styles/stylesheet'; import { signMessage } from '../utils/sign-message'; import { retrieveSingleAccount } from '../utils/accounts'; import { - approveEIP155Request, + approveWalletConnectRequest, rejectEIP155Request, -} from '../utils/wallet-connect/EIP155Requests'; +} from '../utils/wallet-connect/WalletConnectRequests'; import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils'; import { useRequests } from '../context/RequestContext'; @@ -100,17 +100,20 @@ const SignRequest = ({ route }: SignRequestProps) => { ); }, [route]); - const handleEIP155Request = async () => { + const handleWalletConnectRequest = async () => { const { requestEvent } = route.params || {}; if (!account) { throw new Error('account not found'); } - const response = await approveEIP155Request( + const response = await approveWalletConnectRequest( requestEvent, - account.counterId, + account, + network, + message, ); + const { topic } = requestEvent; await web3wallet.respondSessionRequest({ topic, response }); }; @@ -131,7 +134,7 @@ const SignRequest = ({ route }: SignRequestProps) => { const signMessageHandler = async () => { if (route.params?.requestEvent) { - await handleEIP155Request(); + await handleWalletConnectRequest(); } else { await handleIntent(); } diff --git a/types.ts b/types.ts index 980c889..51418ad 100644 --- a/types.ts +++ b/types.ts @@ -1,3 +1,4 @@ +import { StdSignDoc } from '@cosmjs/amino'; import { SignClientTypes } from '@walletconnect/types'; export type StackParamsList = { @@ -92,7 +93,6 @@ export type PathState = { export interface PairingModalProps { visible: boolean; setModalVisible: (arg1: boolean) => void; - currentEthAddresses: string[]; currentProposal: | SignClientTypes.EventArguments['session_proposal'] | undefined; diff --git a/utils/wallet-connect/EIP155Requests.ts b/utils/wallet-connect/EIP155Requests.ts deleted file mode 100644 index d83b23e..0000000 --- a/utils/wallet-connect/EIP155Requests.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Taken from https://medium.com/walletconnect/how-to-build-a-wallet-in-react-native-with-the-web3wallet-sdk-b6f57bf02f9a - -import { formatJsonRpcError, formatJsonRpcResult } from '@json-rpc-tools/utils'; -import { SignClientTypes } from '@walletconnect/types'; -import { getSdkError } from '@walletconnect/utils'; - -import { EIP155_SIGNING_METHODS } from './EIP155Lib'; -import { getSignParamsMessage } from './Helpers'; -import { signEthMessage } from '../sign-message'; - -export async function approveEIP155Request( - requestEvent: SignClientTypes.EventArguments['session_request'], - counterId: number, -) { - const { params, id } = requestEvent; - const { request } = params; - switch (request.method) { - case EIP155_SIGNING_METHODS.PERSONAL_SIGN: - const message = getSignParamsMessage(request.params); - const signedMessage = await signEthMessage(message, counterId); - return formatJsonRpcResult(id, signedMessage); - - default: - throw new Error(getSdkError('INVALID_METHOD').message); - } -} - -export function rejectEIP155Request( - request: SignClientTypes.EventArguments['session_request'], -) { - const { id } = request; - - return formatJsonRpcError(id, getSdkError('USER_REJECTED_METHODS').message); -} diff --git a/utils/wallet-connect/WalletConnectRequests.ts b/utils/wallet-connect/WalletConnectRequests.ts new file mode 100644 index 0000000..3efbb69 --- /dev/null +++ b/utils/wallet-connect/WalletConnectRequests.ts @@ -0,0 +1,65 @@ +// Taken from https://medium.com/walletconnect/how-to-build-a-wallet-in-react-native-with-the-web3wallet-sdk-b6f57bf02f9a + +import { formatJsonRpcError, formatJsonRpcResult } from '@json-rpc-tools/utils'; +import { SignClientTypes } from '@walletconnect/types'; +import { getSdkError } from '@walletconnect/utils'; + +import { EIP155_SIGNING_METHODS } from './EIP155Lib'; +import { signEthMessage } from '../sign-message'; +import { Account } from '../../types'; +import { getCosmosAccounts, getMnemonic, getPathKey } from '../utils'; + +export async function approveWalletConnectRequest( + requestEvent: SignClientTypes.EventArguments['session_request'], + account: Account, + network: string, + message: string, +) { + const { params, id } = requestEvent; + const { request } = params; + + const path = (await getPathKey(network, account.counterId)).path; + const mnemonic = await getMnemonic(); + const cosmosAccount = await getCosmosAccounts(mnemonic, path); + const address = cosmosAccount.data.address; + + switch (request.method) { + case EIP155_SIGNING_METHODS.PERSONAL_SIGN: + const signedEthMessage = await signEthMessage(message, account.counterId); + return formatJsonRpcResult(id, signedEthMessage); + // TODO: Debug signDirect + case 'cosmos_signDirect': + const signedCosmosMessage = await cosmosAccount.cosmosWallet.signAmino( + address, + request.params.signDoc, + ); + + return formatJsonRpcResult(id, { + signature: signedCosmosMessage.signature.signature, + }); + + case 'cosmos_signAmino': + const signedAminoMessage = await cosmosAccount.cosmosWallet.signAmino( + address, + request.params.signDoc, + ); + + if (!signedAminoMessage) { + throw new Error('Error signing message'); + } + + return formatJsonRpcResult(id, { + signature: signedAminoMessage.signature.signature, + }); + default: + throw new Error(getSdkError('INVALID_METHOD').message); + } +} + +export function rejectEIP155Request( + request: SignClientTypes.EventArguments['session_request'], +) { + const { id } = request; + + return formatJsonRpcError(id, getSdkError('USER_REJECTED_METHODS').message); +}