forked from cerc-io/laconic-wallet
Sign message with cosmos accounts using signAmino method (#49)
* Add functionality to use cosmos accounts while pairing * Sign message using cosmos accounts using signAmino method * Add todo to debug signDirect * Use cosmos wallet amino method directly * Add back displaying wallet connect data while pairing * Reset state for wallet connect data on closing pairing modal
This commit is contained in:
parent
7f1b2e38ef
commit
5b6f4e9f61
63
App.tsx
63
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<NativeStackNavigationProp<StackParamsList>>();
|
||||
|
||||
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}
|
||||
/>
|
||||
<Snackbar
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Image, View, Modal } from 'react-native';
|
||||
import { Button, Text } from 'react-native-paper';
|
||||
|
||||
@ -8,34 +8,127 @@ import { getSdkError } from '@walletconnect/utils';
|
||||
import { PairingModalProps } from '../types';
|
||||
import styles from '../styles/stylesheet';
|
||||
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
||||
import { useAccounts } from '../context/AccountsContext';
|
||||
|
||||
const PairingModal = ({
|
||||
visible,
|
||||
currentProposal,
|
||||
currentEthAddresses,
|
||||
setCurrentProposal,
|
||||
setModalVisible,
|
||||
setToastVisible,
|
||||
}: PairingModalProps) => {
|
||||
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 = ({
|
||||
<Text variant="bodyMedium">{url}</Text>
|
||||
<View style={styles.marginVertical8} />
|
||||
<Text variant="titleMedium">Connect to this site?</Text>
|
||||
<Text>Chains: {chains}</Text>
|
||||
<Text>Chains: {walletConnectData.walletConnectChains}</Text>
|
||||
|
||||
<View style={styles.marginVertical8}>
|
||||
<Text variant="titleMedium">Methods Requested:</Text>
|
||||
{methods?.map(method => (
|
||||
{walletConnectData.walletConnectMethods.map(method => (
|
||||
<Text style={styles.centerText} key={method}>
|
||||
{method}
|
||||
</Text>
|
||||
@ -93,7 +196,7 @@ const PairingModal = ({
|
||||
|
||||
<View style={styles.marginVertical8}>
|
||||
<Text variant="titleMedium">Events Requested:</Text>
|
||||
{events?.map(event => (
|
||||
{walletConnectData.walletConnectEvents.map(event => (
|
||||
<Text style={styles.centerText} key={event}>
|
||||
{event}
|
||||
</Text>
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
2
types.ts
2
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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
65
utils/wallet-connect/WalletConnectRequests.ts
Normal file
65
utils/wallet-connect/WalletConnectRequests.ts
Normal file
@ -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);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user