Connect wallet to a dapp using WalletConnect (#38)

* Connect with dapp using WalletConnect

* Pair dapp with wallet

* Sign message taken from dapp and return the signature

* Add todos

* Move wallet connect functions to seperate screen

* Change ui

* Change ui for wc modals

* Add styles

* Remove border radius at the bottom

* Make review changes

* Add dependancy to useEffect

* Move pairing modal methods

---------

Co-authored-by: Adw8 <adwait@deepstacksoft.com>
This commit is contained in:
shreerang6921 2024-03-05 19:20:31 +05:30 committed by GitHub
parent 21b749d9a4
commit 150f10b91f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 1995 additions and 23 deletions

73
App.tsx
View File

@ -1,5 +1,6 @@
import React from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { SignClientTypes } from '@walletconnect/types';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
@ -8,12 +9,62 @@ import HomeScreen from './components/HomeScreen';
import SignRequest from './components/SignRequest';
import InvalidPath from './components/InvalidPath';
import QRScanner from './components/QRScanner';
import PairingModal from './components/PairingModal';
import SignModal from './components/SignModal';
import WalletConnect from './components/WalletConnect';
import { StackParamsList } from './types';
import useInitialization, {
web3wallet,
} from './utils/wallet-connect/WalletConnectUtils';
import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Lib';
const Stack = createNativeStackNavigator<StackParamsList>();
const App = (): React.JSX.Element => {
const [modalVisible, setModalVisible] = useState(false);
//TODO: Remove any
const [currentProposal, setCurrentProposal] = useState<
SignClientTypes.EventArguments['session_proposal'] | undefined
>();
const [requestSession, setRequestSession] = useState<any>();
const [requestEventData, setRequestEventData] = useState<any>();
const [signModalVisible, setSignModalVisible] = useState(false);
useInitialization();
const onSessionProposal = useCallback(
(proposal: SignClientTypes.EventArguments['session_proposal']) => {
setModalVisible(true);
setCurrentProposal(proposal);
},
[],
);
const onSessionRequest = useCallback(
async (requestEvent: SignClientTypes.EventArguments['session_request']) => {
const { topic, params } = requestEvent;
const { request } = params;
const requestSessionData =
web3wallet.engine.signClient.session.get(topic);
switch (request.method) {
case EIP155_SIGNING_METHODS.ETH_SIGN:
case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
setRequestSession(requestSessionData);
setRequestEventData(requestEvent);
setSignModalVisible(true);
return;
}
},
[],
);
useEffect(() => {
web3wallet?.on('session_proposal', onSessionProposal);
web3wallet?.on('session_request', onSessionRequest);
//TODO: Investigate dependancies
});
const linking = {
prefixes: ['https://www.laconic-wallet.com'],
config: {
@ -59,6 +110,13 @@ const App = (): React.JSX.Element => {
headerBackVisible: false,
}}
/>
<Stack.Screen
name="WalletConnect"
component={WalletConnect}
options={{
title: 'Connect Wallet',
}}
/>
<Stack.Screen
name="QRScanner"
component={QRScanner}
@ -67,6 +125,19 @@ const App = (): React.JSX.Element => {
}}
/>
</Stack.Navigator>
<PairingModal
visible={modalVisible}
setModalVisible={setModalVisible}
currentProposal={currentProposal}
setCurrentProposal={setCurrentProposal}
/>
<SignModal
visible={signModalVisible}
setModalVisible={setSignModalVisible}
requestEvent={requestEventData}
requestSession={requestSession}
/>
</NavigationContainer>
);
};

View File

@ -117,7 +117,7 @@ const Accounts = ({
<TouchableOpacity
onPress={() => {
navigation.navigate('QRScanner');
navigation.navigate('WalletConnect');
}}>
<Text
variant="titleSmall"

View File

@ -13,7 +13,9 @@ const InvalidPath = () => {
useNavigation<NativeStackNavigationProp<StackParamsList>>();
return (
<View style={styles.badRequestContainer}>
<Text style={styles.messageText}>The signature request was invalid.</Text>
<Text style={styles.invalidMessageText}>
The signature request was invalid.
</Text>
<Button
mode="contained"
onPress={() => {

118
components/PairingModal.tsx Normal file
View File

@ -0,0 +1,118 @@
import React from 'react';
import { Image, View, Modal } from 'react-native';
import { Button, Text } from 'react-native-paper';
import { PairingModalProps } from '../types';
import styles from '../styles/stylesheet';
import {
currentETHAddress,
web3wallet,
} from '../utils/wallet-connect/WalletConnectUtils';
import { SessionTypes } from '@walletconnect/types';
import { getSdkError } from '@walletconnect/utils';
const PairingModal = ({
visible,
currentProposal,
setCurrentProposal,
setModalVisible,
}: PairingModalProps) => {
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 handleAccept = async () => {
if (currentProposal) {
const { id, params } = currentProposal;
const { requiredNamespaces, relays } = params;
const namespaces: SessionTypes.Namespaces = {};
Object.keys(requiredNamespaces).forEach(key => {
const accounts: string[] = [];
requiredNamespaces[key].chains!.map((chain: any) => {
[currentETHAddress].map(acc => accounts.push(`${chain}:${acc}`));
});
namespaces[key] = {
accounts,
methods: requiredNamespaces[key].methods,
events: requiredNamespaces[key].events,
};
});
await web3wallet.approveSession({
id,
relayProtocol: relays[0].protocol,
namespaces,
});
setModalVisible(false);
setCurrentProposal(undefined);
}
};
const handleReject = async () => {
if (currentProposal) {
const { id } = currentProposal;
await web3wallet.rejectSession({
id,
reason: getSdkError('USER_REJECTED_METHODS'),
});
setModalVisible(false);
setCurrentProposal(undefined);
}
};
return (
<Modal visible={visible} animationType="slide" transparent>
<View style={styles.container}>
<View style={styles.modalContentContainer}>
{icon && (
<Image
style={styles.dappLogo}
source={icon ? { uri: icon } : undefined}
/>
)}
<Text variant="bodyMedium">{url}</Text>
<View style={styles.marginVertical8} />
<Text variant="titleMedium">Connect to this site?</Text>
<Text>Chains: {chains}</Text>
<View style={styles.marginVertical8}>
<Text variant="titleMedium">Methods Requested:</Text>
{methods?.map(method => (
<Text style={styles.centerText} key={method}>
{method}
</Text>
))}
</View>
<View style={styles.marginVertical8}>
<Text variant="titleMedium">Events Requested:</Text>
{events?.map(event => (
<Text style={styles.centerText} key={event}>
{event}
</Text>
))}
</View>
<View style={styles.flexRow}>
<Button mode="outlined" onPress={() => handleReject()}>
Cancel
</Button>
<View style={styles.space} />
<Button mode="contained" onPress={() => handleAccept()}>
Accept
</Button>
</View>
</View>
</View>
</Modal>
);
};
export default PairingModal;

92
components/SignModal.tsx Normal file
View File

@ -0,0 +1,92 @@
import React from 'react';
import { Button, Text } from 'react-native-paper';
import { Image, Modal, View } from 'react-native';
import { getSignParamsMessage } from '../utils/wallet-connect/Helpers';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
import {
approveEIP155Request,
rejectEIP155Request,
} from '../utils/wallet-connect/EIP155Requests';
import styles from '../styles/stylesheet';
import { SignModalProps } from '../types';
const SignModal = ({
visible,
setModalVisible,
requestEvent,
requestSession,
}: SignModalProps) => {
if (!requestEvent || !requestSession) {
return null;
}
const chainID = requestEvent?.params?.chainId?.toUpperCase();
const message = getSignParamsMessage(requestEvent?.params?.request?.params);
const requestName = requestSession?.peer?.metadata?.name;
const requestIcon = requestSession?.peer?.metadata?.icons[0];
const requestURL = requestSession?.peer?.metadata?.url;
const { topic } = requestEvent;
const onApprove = async () => {
if (requestEvent) {
const response = await approveEIP155Request(requestEvent);
await web3wallet.respondSessionRequest({
topic,
response,
});
setModalVisible(false);
}
};
const onReject = async () => {
if (requestEvent) {
const response = rejectEIP155Request(requestEvent);
await web3wallet.respondSessionRequest({
topic,
response,
});
setModalVisible(false);
}
};
return (
<Modal visible={visible} animationType="slide" transparent>
<View style={styles.container}>
<View style={styles.modalContentContainer}>
<Text variant="titleLarge">Sign this message?</Text>
<Image
style={styles.dappLogo}
source={{
uri: requestIcon,
}}
/>
<Text>{requestName}</Text>
<Text variant="bodyMedium">{requestURL}</Text>
<View style={styles.messageBody}>
<Text variant="bodyLarge">{message}</Text>
</View>
<Text>Chains: {chainID}</Text>
<View style={styles.flexRow}>
<Button mode="outlined" onPress={() => onReject()}>
Cancel
</Button>
<View style={styles.space} />
<Button mode="contained" onPress={() => onApprove()}>
Accept
</Button>
</View>
</View>
</View>
</Modal>
);
};
export default SignModal;

View File

@ -0,0 +1,42 @@
import React, { useState } from 'react';
import { View } from 'react-native';
import { Button, TextInput } from 'react-native-paper';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useNavigation } from '@react-navigation/native';
import { web3WalletPair } from '../utils/wallet-connect/WalletConnectUtils';
import styles from '../styles/stylesheet';
import { StackParamsList } from '../types';
const WalletConnect = () => {
const [currentWCURI, setCurrentWCURI] = useState<string>('');
const navigation =
useNavigation<NativeStackNavigationProp<StackParamsList>>();
const pair = async () => {
const pairing = await web3WalletPair({ uri: currentWCURI });
navigation.navigate('Laconic');
return pairing;
};
return (
<View style={styles.appContainer}>
<TextInput
mode="outlined"
onChangeText={setCurrentWCURI}
value={currentWCURI}
placeholder="Enter WalletConnect URI"
/>
<View style={styles.signButton}>
<Button mode="contained" onPress={pair}>
Pair Session
</Button>
</View>
</View>
);
};
export default WalletConnect;

View File

@ -14,9 +14,15 @@
"@cosmjs/amino": "^0.32.2",
"@cosmjs/crypto": "^0.32.2",
"@ethersproject/shims": "^5.7.0",
"@json-rpc-tools/utils": "^1.7.6",
"@react-native-async-storage/async-storage": "^1.22.3",
"@react-native-community/netinfo": "^11.3.1",
"@react-navigation/native": "^6.1.10",
"@react-navigation/native-stack": "^6.9.18",
"ethers": "5",
"@walletconnect/react-native-compat": "^2.11.2",
"@walletconnect/web3wallet": "^1.10.2",
"ethers": "5.7.2",
"fast-text-encoding": "^1.0.6",
"metro-react-native-babel-preset": "^0.77.0",
"patch-package": "^8.0.0",
"postinstall-postinstall": "^2.1.0",
@ -43,6 +49,7 @@
"@react-native/typescript-config": "0.73.1",
"@types/react": "^18.2.6",
"@types/react-test-renderer": "^18.0.0",
"@walletconnect/jsonrpc-types": "^1.0.3",
"babel-jest": "^29.6.3",
"babel-plugin-module-resolver": "^5.0.0",
"eslint": "^8.19.0",

View File

@ -131,12 +131,66 @@ const styles = StyleSheet.create({
justifyContent: 'center',
padding: 20,
},
messageText: {
invalidMessageText: {
color: 'black',
fontSize: 16,
textAlign: 'center',
marginBottom: 20,
},
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
modalContentContainer: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 34,
borderBottomStartRadius: 0,
borderBottomEndRadius: 0,
borderWidth: 1,
width: '100%',
height: '50%',
position: 'absolute',
backgroundColor: 'white',
bottom: 0,
},
dappLogo: {
width: 50,
height: 50,
borderRadius: 8,
marginVertical: 16,
},
space: {
width: 50,
},
flexRow: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: 20,
paddingHorizontal: 16,
},
marginVertical8: {
marginVertical: 8,
textAlign: 'center',
},
subHeading: {
textAlign: 'center',
fontWeight: '600',
},
centerText: {
textAlign: 'center',
},
messageBody: {
borderWidth: 1,
borderRadius: 10,
paddingVertical: 10,
paddingHorizontal: 10,
marginVertical: 20,
},
});
export default styles;

View File

@ -1,3 +1,5 @@
import { SignClientTypes } from '@walletconnect/types';
export type StackParamsList = {
Laconic: undefined;
SignMessage: { selectedNetwork: string; accountInfo: Account } | undefined;
@ -6,6 +8,7 @@ export type StackParamsList = {
| undefined;
InvalidPath: undefined;
QRScanner: undefined;
WalletConnect: undefined;
};
export type Account = {
@ -83,3 +86,21 @@ export type PathState = {
secondNumber: string;
thirdNumber: string;
};
export interface PairingModalProps {
visible: boolean;
setModalVisible: (arg1: boolean) => void;
currentProposal:
| SignClientTypes.EventArguments['session_proposal']
| undefined;
setCurrentProposal: (
arg1: SignClientTypes.EventArguments['session_proposal'] | undefined,
) => void;
}
export interface SignModalProps {
visible: boolean;
setModalVisible: (arg1: boolean) => void;
requestSession: any;
requestEvent: SignClientTypes.EventArguments['session_request'] | undefined;
}

View File

@ -0,0 +1,147 @@
// Taken from https://medium.com/walletconnect/how-to-build-a-wallet-in-react-native-with-the-web3wallet-sdk-b6f57bf02f9a
import { providers, Wallet } from 'ethers';
/**
* Types
*/
interface IInitArgs {
mnemonic?: string;
}
/**
* Library
*/
export default class EIP155Lib {
wallet: Wallet;
constructor(wallet: Wallet) {
this.wallet = wallet;
}
static init({ mnemonic }: IInitArgs) {
const wallet = mnemonic
? Wallet.fromMnemonic(mnemonic)
: Wallet.createRandom();
return new EIP155Lib(wallet);
}
getMnemonic() {
return this.wallet.mnemonic.phrase;
}
getAddress() {
return this.wallet.address;
}
signMessage(message: string) {
return this.wallet.signMessage(message);
}
_signTypedData(domain: any, types: any, data: any) {
return this.wallet._signTypedData(domain, types, data);
}
connect(provider: providers.JsonRpcProvider) {
return this.wallet.connect(provider);
}
signTransaction(transaction: providers.TransactionRequest) {
return this.wallet.signTransaction(transaction);
}
}
/**
* @desc Reference list of eip155 chains
* @url https://chainlist.org
*/
/**
* Types
*/
export type TEIP155Chain = keyof typeof EIP155_CHAINS;
/**
* Chains
*/
export const EIP155_MAINNET_CHAINS = {
'eip155:1': {
chainId: 1,
name: 'Ethereum',
logo: '/chain-logos/eip155-1.png',
rgb: '99, 125, 234',
rpc: 'https://cloudflare-eth.com/',
},
'eip155:43114': {
chainId: 43114,
name: 'Avalanche C-Chain',
logo: '/chain-logos/eip155-43113.png',
rgb: '232, 65, 66',
rpc: 'https://api.avax.network/ext/bc/C/rpc',
},
'eip155:137': {
chainId: 137,
name: 'Polygon',
logo: '/chain-logos/eip155-137.png',
rgb: '130, 71, 229',
rpc: 'https://polygon-rpc.com/',
},
'eip155:10': {
chainId: 10,
name: 'Optimism',
logo: '/chain-logos/eip155-10.png',
rgb: '235, 0, 25',
rpc: 'https://mainnet.optimism.io',
},
};
export const EIP155_TEST_CHAINS = {
'eip155:5': {
chainId: 5,
name: 'Ethereum Goerli',
logo: '/chain-logos/eip155-1.png',
rgb: '99, 125, 234',
rpc: 'https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161',
},
'eip155:43113': {
chainId: 43113,
name: 'Avalanche Fuji',
logo: '/chain-logos/eip155-43113.png',
rgb: '232, 65, 66',
rpc: 'https://api.avax-test.network/ext/bc/C/rpc',
},
'eip155:80001': {
chainId: 80001,
name: 'Polygon Mumbai',
logo: '/chain-logos/eip155-137.png',
rgb: '130, 71, 229',
rpc: 'https://matic-mumbai.chainstacklabs.com',
},
'eip155:420': {
chainId: 420,
name: 'Optimism Goerli',
logo: '/chain-logos/eip155-10.png',
rgb: '235, 0, 25',
rpc: 'https://goerli.optimism.io',
},
};
export const EIP155_CHAINS = {
...EIP155_MAINNET_CHAINS,
...EIP155_TEST_CHAINS,
};
/**
* Methods
*/
export const EIP155_SIGNING_METHODS = {
PERSONAL_SIGN: 'personal_sign',
ETH_SIGN: 'eth_sign',
ETH_SIGN_TRANSACTION: 'eth_signTransaction',
ETH_SIGN_TYPED_DATA: 'eth_signTypedData',
ETH_SIGN_TYPED_DATA_V3: 'eth_signTypedData_v3',
ETH_SIGN_TYPED_DATA_V4: 'eth_signTypedData_v4',
ETH_SEND_RAW_TRANSACTION: 'eth_sendRawTransaction',
ETH_SEND_TRANSACTION: 'eth_sendTransaction',
};

View File

@ -0,0 +1,72 @@
// 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 { SignClientTypes } from '@walletconnect/types';
import { getSdkError } from '@walletconnect/utils';
import { providers } from 'ethers';
import { currentETHAddress } from './WalletConnectUtils';
export async function approveEIP155Request(
requestEvent: SignClientTypes.EventArguments['session_request'],
) {
const { params, id } = requestEvent;
const { chainId, request } = params;
const wallet =
eip155Wallets[getWalletAddressFromParams([currentETHAddress], params)];
switch (request.method) {
case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
case EIP155_SIGNING_METHODS.ETH_SIGN:
const message = getSignParamsMessage(request.params);
const signedMessage = await wallet.signMessage(message);
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:
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);
}

View File

@ -0,0 +1,64 @@
// 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

@ -0,0 +1,106 @@
// Taken from https://medium.com/walletconnect/how-to-build-a-wallet-in-react-native-with-the-web3wallet-sdk-b6f57bf02f9a
import { EIP155_CHAINS, TEIP155Chain } from './EIP155Lib';
import { utils } from 'ethers';
/**
* Truncates string (in the middle) via given lenght value
*/
export function truncate(value: string, length: number) {
if (value?.length <= length) {
return value;
}
const separator = '...';
const stringLength = length - separator.length;
const frontLength = Math.ceil(stringLength / 2);
const backLength = Math.floor(stringLength / 2);
return (
value.substring(0, frontLength) +
separator +
value.substring(value.length - backLength)
);
}
/**
* Converts hex to utf8 string if it is valid bytes
*/
export function convertHexToUtf8(value: string) {
if (utils.isHexString(value)) {
return utils.toUtf8String(value);
}
return value;
}
/**
* Gets message from various signing request methods by filtering out
* a value that is not an address (thus is a message).
* If it is a hex string, it gets converted to utf8 string
*/
export function getSignParamsMessage(params: string[]) {
const message = params.filter(p => !utils.isAddress(p))[0];
return convertHexToUtf8(message);
}
/**
* Gets data from various signTypedData request methods by filtering out
* a value that is not an address (thus is data).
* If data is a string convert it to object
*/
export function getSignTypedDataParamsData(params: string[]) {
const data = params.filter(p => !utils.isAddress(p))[0];
if (typeof data === 'string') {
return JSON.parse(data);
}
return data;
}
/**
* Get our address from params checking if params string contains one
* of our wallet addresses
*/
export function getWalletAddressFromParams(addresses: string[], params: any) {
const paramsString = JSON.stringify(params);
let address = '';
addresses.forEach(addr => {
if (paramsString.includes(addr)) {
address = addr;
}
});
return address;
}
/**
* Check if chain is part of EIP155 standard
*/
export function isEIP155Chain(chain: string) {
return chain.includes('eip155');
}
/**
* Check if chain is part of COSMOS standard
*/
export function isCosmosChain(chain: string) {
return chain.includes('cosmos');
}
/**
* Check if chain is part of SOLANA standard
*/
export function isSolanaChain(chain: string) {
return chain.includes('solana');
}
/**
* Formats chainId to its name
*/
export function formatChainName(chainId: string) {
return EIP155_CHAINS[chainId as TEIP155Chain]?.name ?? chainId;
}

View File

@ -0,0 +1,59 @@
import '@walletconnect/react-native-compat';
import '@ethersproject/shims';
import { Core } from '@walletconnect/core';
import { ICore } from '@walletconnect/types';
import { Web3Wallet, IWeb3Wallet } from '@walletconnect/web3wallet';
export let web3wallet: IWeb3Wallet;
export let core: ICore;
export let currentETHAddress: string;
import { useState, useCallback, useEffect } from 'react';
import { createOrRestoreEIP155Wallet } from './EIP155Wallet';
async function createWeb3Wallet() {
const { eip155Addresses } = await createOrRestoreEIP155Wallet();
currentETHAddress = eip155Addresses[0];
// TODO: Move to dotenv
const ENV_PROJECT_ID = 'c97365bf9f06d12a7488de36240b0ff4';
const core = new Core({
projectId: ENV_PROJECT_ID,
});
web3wallet = await Web3Wallet.init({
core,
metadata: {
name: 'Web3Wallet React Native Tutorial',
description: 'ReactNative Web3Wallet',
url: 'https://walletconnect.com/',
icons: ['https://avatars.githubusercontent.com/u/37784886'],
},
});
}
export default function useInitialization() {
const [initialized, setInitialized] = useState(false);
const onInitialize = useCallback(async () => {
try {
await createWeb3Wallet();
setInitialized(true);
} catch (err: unknown) {
console.log('Error for initializing', err);
}
}, []);
useEffect(() => {
if (!initialized) {
onInitialize();
}
}, [initialized, onInitialize]);
return initialized;
}
export async function web3WalletPair(params: { uri: string }) {
return await web3wallet.core.pairing.pair({ uri: params.uri });
}

1153
yarn.lock

File diff suppressed because it is too large Load Diff