Use existing ethereum accounts while connecting wallet with dapp (#39)

* Use existing accounts while pairing with dapp

* Listen for events emitted from dapp on every render

* Handle review changes
This commit is contained in:
shreerang6921 2024-03-06 11:08:02 +05:30 committed by GitHub
parent 150f10b91f
commit 276cb3695a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 80 additions and 137 deletions

49
App.tsx
View File

@ -12,16 +12,18 @@ import QRScanner from './components/QRScanner';
import PairingModal from './components/PairingModal'; import PairingModal from './components/PairingModal';
import SignModal from './components/SignModal'; import SignModal from './components/SignModal';
import WalletConnect from './components/WalletConnect'; import WalletConnect from './components/WalletConnect';
import { StackParamsList } from './types'; import { StackParamsList } from './types';
import useInitialization, { import useInitialization, {
web3wallet, web3wallet,
} from './utils/wallet-connect/WalletConnectUtils'; } from './utils/wallet-connect/WalletConnectUtils';
import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Lib'; import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Lib';
import { retrieveAccounts } from './utils/accounts';
const Stack = createNativeStackNavigator<StackParamsList>(); const Stack = createNativeStackNavigator<StackParamsList>();
const App = (): React.JSX.Element => { const App = (): React.JSX.Element => {
useInitialization();
const [modalVisible, setModalVisible] = useState(false); const [modalVisible, setModalVisible] = useState(false);
//TODO: Remove any //TODO: Remove any
const [currentProposal, setCurrentProposal] = useState< const [currentProposal, setCurrentProposal] = useState<
@ -30,8 +32,7 @@ const App = (): React.JSX.Element => {
const [requestSession, setRequestSession] = useState<any>(); const [requestSession, setRequestSession] = useState<any>();
const [requestEventData, setRequestEventData] = useState<any>(); const [requestEventData, setRequestEventData] = useState<any>();
const [signModalVisible, setSignModalVisible] = useState(false); const [signModalVisible, setSignModalVisible] = useState(false);
const [currentEthAddresses, setCurrentEthAddresses] = useState<string[]>([]);
useInitialization();
const onSessionProposal = useCallback( const onSessionProposal = useCallback(
(proposal: SignClientTypes.EventArguments['session_proposal']) => { (proposal: SignClientTypes.EventArguments['session_proposal']) => {
@ -65,6 +66,20 @@ const App = (): React.JSX.Element => {
//TODO: Investigate dependancies //TODO: Investigate dependancies
}); });
useEffect(() => {
const fetchEthAccounts = async () => {
const { ethLoadedAccounts } = await retrieveAccounts();
if (ethLoadedAccounts) {
const ethAddreses = ethLoadedAccounts.map(account => account.address);
setCurrentEthAddresses(ethAddreses);
}
};
fetchEthAccounts();
// TODO: Use context to maintain accounts state
}, [modalVisible]);
const linking = { const linking = {
prefixes: ['https://www.laconic-wallet.com'], prefixes: ['https://www.laconic-wallet.com'],
config: { config: {
@ -125,19 +140,23 @@ const App = (): React.JSX.Element => {
}} }}
/> />
</Stack.Navigator> </Stack.Navigator>
<PairingModal
visible={modalVisible}
setModalVisible={setModalVisible}
currentProposal={currentProposal}
setCurrentProposal={setCurrentProposal}
/>
<SignModal <>
visible={signModalVisible} <PairingModal
setModalVisible={setSignModalVisible} visible={modalVisible}
requestEvent={requestEventData} setModalVisible={setModalVisible}
requestSession={requestSession} currentProposal={currentProposal}
/> setCurrentProposal={setCurrentProposal}
currentEthAddresses={currentEthAddresses}
/>
<SignModal
visible={signModalVisible}
setModalVisible={setSignModalVisible}
requestEvent={requestEventData}
requestSession={requestSession}
currentEthAddresses={currentEthAddresses}
/>
</>
</NavigationContainer> </NavigationContainer>
); );
}; };

View File

@ -4,16 +4,14 @@ import { Button, Text } from 'react-native-paper';
import { PairingModalProps } from '../types'; import { PairingModalProps } from '../types';
import styles from '../styles/stylesheet'; import styles from '../styles/stylesheet';
import { import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
currentETHAddress,
web3wallet,
} from '../utils/wallet-connect/WalletConnectUtils';
import { SessionTypes } from '@walletconnect/types'; import { SessionTypes } from '@walletconnect/types';
import { getSdkError } from '@walletconnect/utils'; import { getSdkError } from '@walletconnect/utils';
const PairingModal = ({ const PairingModal = ({
visible, visible,
currentEthAddresses,
currentProposal, currentProposal,
setCurrentProposal, setCurrentProposal,
setModalVisible, setModalVisible,
@ -32,7 +30,7 @@ const PairingModal = ({
Object.keys(requiredNamespaces).forEach(key => { Object.keys(requiredNamespaces).forEach(key => {
const accounts: string[] = []; const accounts: string[] = [];
requiredNamespaces[key].chains!.map((chain: any) => { requiredNamespaces[key].chains!.map((chain: any) => {
[currentETHAddress].map(acc => accounts.push(`${chain}:${acc}`)); currentEthAddresses.map(acc => accounts.push(`${chain}:${acc}`));
}); });
namespaces[key] = { namespaces[key] = {

View File

@ -17,6 +17,7 @@ const SignModal = ({
setModalVisible, setModalVisible,
requestEvent, requestEvent,
requestSession, requestSession,
currentEthAddresses,
}: SignModalProps) => { }: SignModalProps) => {
if (!requestEvent || !requestSession) { if (!requestEvent || !requestSession) {
return null; return null;
@ -33,7 +34,10 @@ const SignModal = ({
const onApprove = async () => { const onApprove = async () => {
if (requestEvent) { if (requestEvent) {
const response = await approveEIP155Request(requestEvent); const response = await approveEIP155Request(
requestEvent,
currentEthAddresses,
);
await web3wallet.respondSessionRequest({ await web3wallet.respondSessionRequest({
topic, topic,
response, response,

View File

@ -90,6 +90,7 @@ export type PathState = {
export interface PairingModalProps { export interface PairingModalProps {
visible: boolean; visible: boolean;
setModalVisible: (arg1: boolean) => void; setModalVisible: (arg1: boolean) => void;
currentEthAddresses: string[];
currentProposal: currentProposal:
| SignClientTypes.EventArguments['session_proposal'] | SignClientTypes.EventArguments['session_proposal']
| undefined; | undefined;
@ -103,4 +104,5 @@ export interface SignModalProps {
setModalVisible: (arg1: boolean) => void; setModalVisible: (arg1: boolean) => void;
requestSession: any; requestSession: any;
requestEvent: SignClientTypes.EventArguments['session_request'] | undefined; requestEvent: SignClientTypes.EventArguments['session_request'] | undefined;
currentEthAddresses: string[];
} }

View File

@ -1,63 +1,30 @@
// Taken from https://medium.com/walletconnect/how-to-build-a-wallet-in-react-native-with-the-web3wallet-sdk-b6f57bf02f9a // Taken from https://medium.com/walletconnect/how-to-build-a-wallet-in-react-native-with-the-web3wallet-sdk-b6f57bf02f9a
import {
EIP155_CHAINS,
EIP155_SIGNING_METHODS,
TEIP155Chain,
} from './EIP155Lib';
import { eip155Wallets } from './EIP155Wallet';
import {
getSignParamsMessage,
getSignTypedDataParamsData,
getWalletAddressFromParams,
} from './Helpers';
import { formatJsonRpcError, formatJsonRpcResult } from '@json-rpc-tools/utils'; import { formatJsonRpcError, formatJsonRpcResult } from '@json-rpc-tools/utils';
import { SignClientTypes } from '@walletconnect/types'; import { SignClientTypes } from '@walletconnect/types';
import { getSdkError } from '@walletconnect/utils'; import { getSdkError } from '@walletconnect/utils';
import { providers } from 'ethers';
import { currentETHAddress } from './WalletConnectUtils'; import { EIP155_SIGNING_METHODS } from './EIP155Lib';
import { getSignParamsMessage, getAccountNumberFromParams } from './Helpers';
import { signEthMessage } from '../sign-message';
export async function approveEIP155Request( export async function approveEIP155Request(
requestEvent: SignClientTypes.EventArguments['session_request'], requestEvent: SignClientTypes.EventArguments['session_request'],
currentEthAddresses: string[],
) { ) {
const { params, id } = requestEvent; const { params, id } = requestEvent;
const { chainId, request } = params; const { request } = params;
const wallet = const counterId = await getAccountNumberFromParams(
eip155Wallets[getWalletAddressFromParams([currentETHAddress], params)]; currentEthAddresses,
params,
);
switch (request.method) { switch (request.method) {
case EIP155_SIGNING_METHODS.PERSONAL_SIGN: case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
case EIP155_SIGNING_METHODS.ETH_SIGN:
const message = getSignParamsMessage(request.params); const message = getSignParamsMessage(request.params);
const signedMessage = await wallet.signMessage(message); const signedMessage = await signEthMessage(message, counterId);
return formatJsonRpcResult(id, signedMessage); return formatJsonRpcResult(id, signedMessage);
case EIP155_SIGNING_METHODS.ETH_SIGN_TYPED_DATA:
case EIP155_SIGNING_METHODS.ETH_SIGN_TYPED_DATA_V3:
case EIP155_SIGNING_METHODS.ETH_SIGN_TYPED_DATA_V4:
const {
domain,
types,
message: data,
} = getSignTypedDataParamsData(request.params);
// https://github.com/ethers-io/ethers.js/issues/687#issuecomment-714069471
delete types.EIP712Domain;
const signedData = await wallet._signTypedData(domain, types, data);
return formatJsonRpcResult(id, signedData);
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
const provider = new providers.JsonRpcProvider(
EIP155_CHAINS[chainId as TEIP155Chain].rpc,
);
const sendTransaction = request.params[0];
const connectedWallet = wallet.connect(provider);
const { hash } = await connectedWallet.sendTransaction(sendTransaction);
return formatJsonRpcResult(id, hash);
case EIP155_SIGNING_METHODS.ETH_SIGN_TRANSACTION:
const signTransaction = request.params[0];
const signature = await wallet.signTransaction(signTransaction);
return formatJsonRpcResult(id, signature);
default: default:
throw new Error(getSdkError('INVALID_METHOD').message); throw new Error(getSdkError('INVALID_METHOD').message);
} }

View File

@ -1,64 +0,0 @@
// https://medium.com/walletconnect/how-to-build-a-wallet-in-react-native-with-the-web3wallet-sdk-b6f57bf02f9a
// TODO: check and remove if not used
import AsyncStorage from '@react-native-async-storage/async-storage';
import EIP155Lib from './EIP155Lib';
export let wallet1: EIP155Lib;
export let wallet2: EIP155Lib;
export let eip155Wallets: Record<string, EIP155Lib>;
export let eip155Addresses: string[];
export let address1: string;
let address2: string;
/**
* Utilities
*/
export const setLocalStorage = async (mnemonic: any) => {
try {
const value = await AsyncStorage.setItem('EIP155_MNEMONIC_1', mnemonic);
if (value !== null) {
return value;
}
} catch (e) {
console.log('setLocalStorage Error:', e);
}
};
export const getLocalStorage = async () => {
try {
const value = await AsyncStorage.getItem('EIP155_MNEMONIC_1');
if (value !== null) {
return value;
}
} catch (e) {
console.log('getLocalStorage Error:', e);
}
};
// Function to create or restore a wallet
export async function createOrRestoreEIP155Wallet() {
let mnemonic1 = await getLocalStorage();
if (mnemonic1) {
wallet1 = EIP155Lib.init({ mnemonic: mnemonic1 });
} else {
wallet1 = EIP155Lib.init({});
}
// @notice / Warning!!! : This is a test wallet, do not use it for real transactions
setLocalStorage(wallet1?.getMnemonic());
address1 = wallet1.getAddress();
eip155Wallets = {
[address1]: wallet1,
[address2]: wallet2,
};
eip155Addresses = Object.keys(eip155Wallets);
return {
eip155Wallets,
eip155Addresses,
};
}

View File

@ -1,5 +1,6 @@
// Taken from https://medium.com/walletconnect/how-to-build-a-wallet-in-react-native-with-the-web3wallet-sdk-b6f57bf02f9a // Taken from https://medium.com/walletconnect/how-to-build-a-wallet-in-react-native-with-the-web3wallet-sdk-b6f57bf02f9a
import { retrieveAccounts } from '../accounts';
import { EIP155_CHAINS, TEIP155Chain } from './EIP155Lib'; import { EIP155_CHAINS, TEIP155Chain } from './EIP155Lib';
import { utils } from 'ethers'; import { utils } from 'ethers';
@ -64,7 +65,10 @@ export function getSignTypedDataParamsData(params: string[]) {
* Get our address from params checking if params string contains one * Get our address from params checking if params string contains one
* of our wallet addresses * of our wallet addresses
*/ */
export function getWalletAddressFromParams(addresses: string[], params: any) { export async function getAccountNumberFromParams(
addresses: string[],
params: any,
) {
const paramsString = JSON.stringify(params); const paramsString = JSON.stringify(params);
let address = ''; let address = '';
@ -74,7 +78,17 @@ export function getWalletAddressFromParams(addresses: string[], params: any) {
} }
}); });
return address; const { ethLoadedAccounts } = await retrieveAccounts();
const currentAccount = ethLoadedAccounts!.find(
account => account.address === address,
);
if (!currentAccount) {
throw new Error('Account with given adress not found');
}
return currentAccount.counterId;
} }
/** /**

View File

@ -7,14 +7,17 @@ import { Web3Wallet, IWeb3Wallet } from '@walletconnect/web3wallet';
export let web3wallet: IWeb3Wallet; export let web3wallet: IWeb3Wallet;
export let core: ICore; export let core: ICore;
export let currentETHAddress: string; export let currentETHAddresses: string[];
export let currentCosmosAddresses: string[];
import { useState, useCallback, useEffect } from 'react'; import { useState, useCallback, useEffect } from 'react';
import { createOrRestoreEIP155Wallet } from './EIP155Wallet'; import { retrieveAccounts } from '../accounts';
async function createWeb3Wallet() { export async function createWeb3Wallet() {
const { eip155Addresses } = await createOrRestoreEIP155Wallet(); const { ethLoadedAccounts } = await retrieveAccounts();
currentETHAddress = eip155Addresses[0]; currentETHAddresses = ethLoadedAccounts
? ethLoadedAccounts.map(ethAccount => ethAccount.address)
: [];
// TODO: Move to dotenv // TODO: Move to dotenv
const ENV_PROJECT_ID = 'c97365bf9f06d12a7488de36240b0ff4'; const ENV_PROJECT_ID = 'c97365bf9f06d12a7488de36240b0ff4';