forked from cerc-io/laconic-wallet
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:
parent
150f10b91f
commit
276cb3695a
49
App.tsx
49
App.tsx
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -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] = {
|
||||||
|
@ -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,
|
||||||
|
2
types.ts
2
types.ts
@ -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[];
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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,
|
|
||||||
};
|
|
||||||
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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';
|
||||||
|
Loading…
Reference in New Issue
Block a user