Compare commits

...

9 Commits

Author SHA1 Message Date
bb5223afda Add support for staking module tx MsgCreateValidator (#14)
Part of [laconicd testnet validator enrollment](https://www.notion.so/laconicd-testnet-validator-enrollment-6fc1d3cafcc64fef8c5ed3affa27c675)

Co-authored-by: Prathamesh Musale <prathamesh.musale0@gmail.com>
Co-authored-by: Adw8 <adwaitgharpure@gmail.com>
Reviewed-on: cerc-io/laconic-wallet#14
2024-08-09 09:41:46 +00:00
e975f4c9f7 Add copy button for mnemonic after creating new wallet (#13)
Part of [laconicd testnet validator enrollment](https://www.notion.so/laconicd-testnet-validator-enrollment-6fc1d3cafcc64fef8c5ed3affa27c675)

Co-authored-by: Shreerang Kale <shreerangkale@gmail.com>
Co-authored-by: IshaVenikar <ishavenikar7@gmail.com>
Reviewed-on: cerc-io/laconic-wallet#13
2024-08-08 07:35:50 +00:00
4827fa8c7c Fix accounts order in namespaces object and prevent re-render from useEffect (#12)
Part of [laconicd testnet validator enrollment](https://www.notion.so/laconicd-testnet-validator-enrollment-6fc1d3cafcc64fef8c5ed3affa27c675)
- Send namespaces object with correct accounts sequence while pairing with dApps

Co-authored-by: Shreerang Kale <shreerangkale@gmail.com>
Reviewed-on: cerc-io/laconic-wallet#12
2024-08-01 10:38:21 +00:00
36c3adb1b1 Upgrade registry-sdk version to 0.2.5 (#11)
Part of [laconicd testnet validator enrollment](https://www.notion.so/laconicd-testnet-validator-enrollment-6fc1d3cafcc64fef8c5ed3affa27c675)

Co-authored-by: Shreerang Kale <shreerangkale@gmail.com>
Reviewed-on: cerc-io/laconic-wallet#11
2024-07-30 13:32:32 +00:00
2ca91cad06 Replace photon with alnt for laconicd network (#10)
Part of [laconicd testnet validator enrollment](https://www.notion.so/laconicd-testnet-validator-enrollment-6fc1d3cafcc64fef8c5ed3affa27c675)

Co-authored-by: IshaVenikar <ishavenikar7@gmail.com>
Reviewed-on: cerc-io/laconic-wallet#10
2024-07-30 13:20:17 +00:00
eab4fd425a Add laconicd as a default network in wallet (#9)
Part of [laconicd testnet validator enrollment
](https://www.notion.so/laconicd-testnet-validator-enrollment-6fc1d3cafcc64fef8c5ed3affa27c675)

Co-authored-by: IshaVenikar <ishavenikar7@gmail.com>
Reviewed-on: cerc-io/laconic-wallet#9
2024-07-29 12:55:50 +00:00
361b79b696 Refactor wallet connect instance to use state variables (#8)
Part of [laconicd testnet validator enrollment](https://www.notion.so/laconicd-testnet-validator-enrollment-6fc1d3cafcc64fef8c5ed3affa27c675)
- Refactor `web3wallet` variable into a state variable

Co-authored-by: Shreerang Kale <shreerangkale@gmail.com>
Co-authored-by: Adw8 <adwaitgharpure@gmail.com>
Reviewed-on: cerc-io/laconic-wallet#8
2024-07-29 12:13:15 +00:00
0bd9dec8a9 Upgrade registry-sdk package version to 0.2.3 (#7)
Part of [laconicd testnet validator enrollment](https://www.notion.so/laconicd-testnet-validator-enrollment-6fc1d3cafcc64fef8c5ed3affa27c675)

- Upgrade registry-sdk to include onboarding module changes

Co-authored-by: Adw8 <adwaitgharpure@gmail.com>
Reviewed-on: cerc-io/laconic-wallet#7
Co-authored-by: Nabarun <nabarun@deepstacksoft.com>
Co-committed-by: Nabarun <nabarun@deepstacksoft.com>
2024-07-29 05:40:34 +00:00
c027f5b934 Hotfix to remove non-null assertion for WalletConnect instance (#6)
Part of [laconicd testnet validator enrollment](https://www.notion.so/laconicd-testnet-validator-enrollment-6fc1d3cafcc64fef8c5ed3affa27c675)

TODO:
- Refactor WalletConnect instance in laconic wallet to use state variables

Co-authored-by: Shreerang Kale <shreerangkale@gmail.com>
Reviewed-on: cerc-io/laconic-wallet#6
2024-07-26 12:17:28 +00:00
22 changed files with 222 additions and 115 deletions

View File

@ -2,3 +2,4 @@ WALLET_CONNECT_PROJECT_ID=
DEFAULT_GAS_PRICE=0.025 DEFAULT_GAS_PRICE=0.025
# Reference: https://github.com/cosmos/cosmos-sdk/issues/16020 # Reference: https://github.com/cosmos/cosmos-sdk/issues/16020
DEFAULT_GAS_ADJUSTMENT=2 DEFAULT_GAS_ADJUSTMENT=2
LACONICD_RPC_URL=https://laconicd.laconic.com

View File

@ -9,7 +9,6 @@
- Function for creating web3 wallet - Function for creating web3 wallet
```js ```js
export let web3wallet: IWeb3Wallet;
export let core: ICore; export let core: ICore;
export async function createWeb3Wallet() { export async function createWeb3Wallet() {
@ -26,23 +25,27 @@
icons: ['https://avatars.githubusercontent.com/u/92608123'], icons: ['https://avatars.githubusercontent.com/u/92608123'],
}, },
}); });
return web3wallet;
} }
``` ```
- Hook used for intializing web3 wallet - Hook used for intializing web3 wallet
```js ```js
export default function useInitialization() { export default function useInitialization(setWeb3wallet) {
const [initialized, setInitialized] = useState(false); const [initialized, setInitialized] = useState(false);
const onInitialize = useCallback(async () => { const onInitialize = useCallback(async () => {
try { try {
await createWeb3Wallet(); const web3walletInstance = await createWeb3Wallet();
setWeb3wallet(web3walletInstance);
setInitialized(true); setInitialized(true);
} catch (err: unknown) { } catch (err: unknown) {
console.log('Error for initializing', err); console.error('Error for initializing', err);
} }
}, []); }, [setWeb3wallet]);
useEffect(() => { useEffect(() => {
if (!initialized) { if (!initialized) {

View File

@ -12,7 +12,7 @@
"prepare": "husky" "prepare": "husky"
}, },
"dependencies": { "dependencies": {
"@cerc-io/registry-sdk": "^0.2.2", "@cerc-io/registry-sdk": "^0.2.5",
"@cosmjs/amino": "^0.32.3", "@cosmjs/amino": "^0.32.3",
"@cosmjs/crypto": "^0.32.3", "@cosmjs/crypto": "^0.32.3",
"@cosmjs/proto-signing": "^0.32.3", "@cosmjs/proto-signing": "^0.32.3",
@ -21,6 +21,7 @@
"@hookform/resolvers": "^3.3.4", "@hookform/resolvers": "^3.3.4",
"@json-rpc-tools/utils": "^1.7.6", "@json-rpc-tools/utils": "^1.7.6",
"@react-native-async-storage/async-storage": "^1.22.3", "@react-native-async-storage/async-storage": "^1.22.3",
"@react-native-clipboard/clipboard": "^1.14.1",
"@react-native-community/netinfo": "^11.3.1", "@react-native-community/netinfo": "^11.3.1",
"@react-navigation/elements": "^1.3.30", "@react-navigation/elements": "^1.3.30",
"@react-navigation/native": "^6.1.10", "@react-navigation/native": "^6.1.10",

View File

@ -4,6 +4,7 @@ declare module 'react-native-config' {
WALLET_CONNECT_PROJECT_ID: string; WALLET_CONNECT_PROJECT_ID: string;
DEFAULT_GAS_PRICE: string; DEFAULT_GAS_PRICE: string;
DEFAULT_GAS_ADJUSTMENT: string; DEFAULT_GAS_ADJUSTMENT: string;
LACONICD_RPC_URL: string;
} }
export const Config: NativeConfig; export const Config: NativeConfig;

31
scripts/build-apk.sh Executable file
View File

@ -0,0 +1,31 @@
#!/bin/bash
# Default value for IS_RELEASE
IS_RELEASE=${IS_RELEASE:-false}
# Install dependencies
echo "Installing dependencies..."
yarn
# Create the necessary directory for assets
mkdir -p android/app/src/main/assets/
# Bundle the React Native application
yarn react-native bundle \
--platform android \
--dev false \
--entry-file index.js \
--bundle-output android/app/src/main/assets/index.android.bundle \
--assets-dest android/app/src/main/res
# Navigate to the android directory
cd android
# Run the Gradle build based on the IS_RELEASE flag
if [ "$IS_RELEASE" = "true" ]; then
echo "Building release version..."
./gradlew assembleRelease
else
echo "Building debug version..."
./gradlew assembleDebug
fi

View File

@ -24,7 +24,6 @@ import AddSession from './screens/AddSession';
import WalletConnect from './screens/WalletConnect'; import WalletConnect from './screens/WalletConnect';
import ApproveTransaction from './screens/ApproveTransaction'; import ApproveTransaction from './screens/ApproveTransaction';
import { StackParamsList } from './types'; import { StackParamsList } from './types';
import { web3wallet } from './utils/wallet-connect/WalletConnectUtils';
import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Data'; import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Data';
import { getSignParamsMessage } from './utils/wallet-connect/helpers'; import { getSignParamsMessage } from './utils/wallet-connect/helpers';
import ApproveTransfer from './screens/ApproveTransfer'; import ApproveTransfer from './screens/ApproveTransfer';
@ -41,7 +40,7 @@ const App = (): React.JSX.Element => {
const navigation = const navigation =
useNavigation<NativeStackNavigationProp<StackParamsList>>(); useNavigation<NativeStackNavigationProp<StackParamsList>>();
const { setActiveSessions } = useWalletConnect(); const { web3wallet, setActiveSessions } = useWalletConnect();
const { accounts, setCurrentIndex } = useAccounts(); const { accounts, setCurrentIndex } = useAccounts();
const { networksData, selectedNetwork, setSelectedNetwork } = useNetworks(); const { networksData, selectedNetwork, setSelectedNetwork } = useNetworks();
const [modalVisible, setModalVisible] = useState(false); const [modalVisible, setModalVisible] = useState(false);
@ -63,7 +62,7 @@ const App = (): React.JSX.Element => {
setModalVisible(true); setModalVisible(true);
setCurrentProposal(proposal); setCurrentProposal(proposal);
}, },
[accounts], [accounts, web3wallet],
); );
const onSessionRequest = useCallback( const onSessionRequest = useCallback(
@ -197,13 +196,14 @@ const App = (): React.JSX.Element => {
setSelectedNetwork, setSelectedNetwork,
setCurrentIndex, setCurrentIndex,
selectedNetwork, selectedNetwork,
web3wallet,
], ],
); );
const onSessionDelete = useCallback(() => { const onSessionDelete = useCallback(() => {
const sessions = web3wallet!.getActiveSessions(); const sessions = web3wallet!.getActiveSessions();
setActiveSessions(sessions); setActiveSessions(sessions);
}, [setActiveSessions]); }, [setActiveSessions, web3wallet]);
useEffect(() => { useEffect(() => {
web3wallet?.on('session_proposal', onSessionProposal); web3wallet?.on('session_proposal', onSessionProposal);

View File

@ -12,11 +12,11 @@ import styles from '../styles/stylesheet';
import HDPathDialog from './HDPathDialog'; import HDPathDialog from './HDPathDialog';
import AccountDetails from './AccountDetails'; import AccountDetails from './AccountDetails';
import { useAccounts } from '../context/AccountsContext'; import { useAccounts } from '../context/AccountsContext';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
import { useNetworks } from '../context/NetworksContext'; import { useNetworks } from '../context/NetworksContext';
import ConfirmDialog from './ConfirmDialog'; import ConfirmDialog from './ConfirmDialog';
import { getNamespaces } from '../utils/wallet-connect/helpers'; import { getNamespaces } from '../utils/wallet-connect/helpers';
import ShowPKDialog from './ShowPKDialog'; import ShowPKDialog from './ShowPKDialog';
import { useWalletConnect } from '../context/WalletConnectContext';
const Accounts = () => { const Accounts = () => {
const navigation = const navigation =
@ -26,6 +26,8 @@ const Accounts = () => {
useAccounts(); useAccounts();
const { networksData, selectedNetwork, setNetworksData, setSelectedNetwork } = const { networksData, selectedNetwork, setNetworksData, setSelectedNetwork } =
useNetworks(); useNetworks();
const { web3wallet } = useWalletConnect();
const [expanded, setExpanded] = useState(false); const [expanded, setExpanded] = useState(false);
const [isAccountCreating, setIsAccountCreating] = useState(false); const [isAccountCreating, setIsAccountCreating] = useState(false);
const [hdDialog, setHdDialog] = useState(false); const [hdDialog, setHdDialog] = useState(false);
@ -58,7 +60,6 @@ const Accounts = () => {
networksData, networksData,
selectedNetwork!, selectedNetwork!,
accounts, accounts,
currentIndex,
); );
if (!updatedNamespaces) { if (!updatedNamespaces) {
@ -74,7 +75,7 @@ const Accounts = () => {
// Call the updateSessions function when the 'accounts' dependency changes // Call the updateSessions function when the 'accounts' dependency changes
updateSessions(); updateSessions();
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [accounts]); }, [accounts, web3wallet]);
const addAccountHandler = async () => { const addAccountHandler = async () => {
setIsAccountCreating(true); setIsAccountCreating(true);

View File

@ -1,6 +1,8 @@
import React from 'react'; import React, { useState } from 'react';
import { View } from 'react-native'; import { View } from 'react-native';
import { Button, Dialog, Portal, Text } from 'react-native-paper'; import { Button, Dialog, Portal, Snackbar, Text } from 'react-native-paper';
import Clipboard from '@react-native-clipboard/clipboard';
import styles from '../styles/stylesheet'; import styles from '../styles/stylesheet';
import GridView from './Grid'; import GridView from './Grid';
@ -11,17 +13,25 @@ const DialogComponent = ({
hideDialog, hideDialog,
contentText, contentText,
}: CustomDialogProps) => { }: CustomDialogProps) => {
const [toastVisible, setToastVisible] = useState(false);
const words = contentText.split(' '); const words = contentText.split(' ');
const handleCopy = () => {
Clipboard.setString(contentText);
setToastVisible(true);
};
return ( return (
<>
<Portal> <Portal>
<Dialog visible={visible} onDismiss={hideDialog}> <Dialog visible={visible} onDismiss={hideDialog}>
<Dialog.Content> <Dialog.Content>
<Text variant="titleLarge">Mnemonic</Text> <Text variant="titleLarge">Mnemonic</Text>
<View style={styles.dialogTitle}> <View style={styles.dialogTitle}>
<Text variant="titleMedium"> <Text variant="titleMedium">
Your mnemonic provides full access to your wallet and funds. Make Your mnemonic provides full access to your wallet and funds.
sure to note it down.{' '} Make sure to note it down.{' '}
</Text> </Text>
<Text variant="titleMedium" style={styles.dialogWarning}> <Text variant="titleMedium" style={styles.dialogWarning}>
Do not share your mnemonic with anyone Do not share your mnemonic with anyone
@ -30,10 +40,18 @@ const DialogComponent = ({
</View> </View>
</Dialog.Content> </Dialog.Content>
<Dialog.Actions> <Dialog.Actions>
<Button onPress={handleCopy}>Copy</Button>
<Button onPress={hideDialog}>Done</Button> <Button onPress={hideDialog}>Done</Button>
</Dialog.Actions> </Dialog.Actions>
</Dialog> </Dialog>
</Portal> </Portal>
<Snackbar
visible={toastVisible}
onDismiss={() => setToastVisible(false)}
duration={1000}>
Mnemonic copied to clipboard
</Snackbar>
</>
); );
}; };

View File

@ -8,7 +8,6 @@ import { buildApprovedNamespaces, getSdkError } from '@walletconnect/utils';
import { PairingModalProps } from '../types'; import { PairingModalProps } from '../types';
import styles from '../styles/stylesheet'; import styles from '../styles/stylesheet';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
import { useAccounts } from '../context/AccountsContext'; import { useAccounts } from '../context/AccountsContext';
import { useWalletConnect } from '../context/WalletConnectContext'; import { useWalletConnect } from '../context/WalletConnectContext';
import { useNetworks } from '../context/NetworksContext'; import { useNetworks } from '../context/NetworksContext';
@ -21,7 +20,7 @@ const PairingModal = ({
setModalVisible, setModalVisible,
setToastVisible, setToastVisible,
}: PairingModalProps) => { }: PairingModalProps) => {
const { accounts, currentIndex } = useAccounts(); const { accounts } = useAccounts();
const { selectedNetwork, networksData } = useNetworks(); const { selectedNetwork, networksData } = useNetworks();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [chainError, setChainError] = useState(''); const [chainError, setChainError] = useState('');
@ -87,7 +86,7 @@ const PairingModal = ({
}); });
}, [currentProposal]); }, [currentProposal]);
const { setActiveSessions } = useWalletConnect(); const { setActiveSessions, web3wallet } = useWalletConnect();
useEffect(() => { useEffect(() => {
const getSupportedNamespaces = async () => { const getSupportedNamespaces = async () => {
@ -104,7 +103,6 @@ const PairingModal = ({
networksData, networksData,
selectedNetwork!, selectedNetwork!,
accounts, accounts,
currentIndex,
); );
setSupportedNamespaces(nameSpaces); setSupportedNamespaces(nameSpaces);
} catch (err) { } catch (err) {
@ -130,7 +128,7 @@ const PairingModal = ({
networksData, networksData,
selectedNetwork, selectedNetwork,
accounts, accounts,
currentIndex, web3wallet,
setCurrentProposal, setCurrentProposal,
setModalVisible, setModalVisible,
]); ]);

View File

@ -1,14 +1,16 @@
import React, { createContext, useContext, useEffect, useState } from 'react'; import React, { createContext, useContext, useEffect, useState } from 'react';
import { SessionTypes } from '@walletconnect/types'; import { SessionTypes } from '@walletconnect/types';
import { IWeb3Wallet } from '@walletconnect/web3wallet';
import { WalletConnectContextProps } from '../types'; import { WalletConnectContextProps } from '../types';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
import useInitialization from '../hooks/useInitialization'; import useInitialization from '../hooks/useInitialization';
const WalletConnectContext = createContext<WalletConnectContextProps>({ const WalletConnectContext = createContext<WalletConnectContextProps>({
activeSessions: {}, activeSessions: {},
setActiveSessions: () => {}, setActiveSessions: () => {},
web3wallet: {} as IWeb3Wallet,
setWeb3wallet: () => {},
}); });
const useWalletConnect = () => { const useWalletConnect = () => {
@ -17,12 +19,14 @@ const useWalletConnect = () => {
}; };
const WalletConnectProvider = ({ children }: { children: React.ReactNode }) => { const WalletConnectProvider = ({ children }: { children: React.ReactNode }) => {
useInitialization(); const [web3wallet, setWeb3wallet] = useState<IWeb3Wallet | undefined>();
useInitialization(setWeb3wallet);
useEffect(() => { useEffect(() => {
const sessions = (web3wallet && web3wallet.getActiveSessions()) || {}; const sessions = (web3wallet && web3wallet.getActiveSessions()) || {};
setActiveSessions(sessions); setActiveSessions(sessions);
}, []); }, [web3wallet]);
const [activeSessions, setActiveSessions] = useState< const [activeSessions, setActiveSessions] = useState<
Record<string, SessionTypes.Struct> Record<string, SessionTypes.Struct>
@ -33,6 +37,8 @@ const WalletConnectProvider = ({ children }: { children: React.ReactNode }) => {
value={{ value={{
activeSessions, activeSessions,
setActiveSessions, setActiveSessions,
web3wallet,
setWeb3wallet,
}}> }}>
{children} {children}
</WalletConnectContext.Provider> </WalletConnectContext.Provider>

View File

@ -1,17 +1,24 @@
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { IWeb3Wallet } from '@walletconnect/web3wallet';
import { createWeb3Wallet } from '../utils/wallet-connect/WalletConnectUtils'; import { createWeb3Wallet } from '../utils/wallet-connect/WalletConnectUtils';
export default function useInitialization() { export default function useInitialization(
setWeb3wallet: React.Dispatch<React.SetStateAction<IWeb3Wallet | undefined>>,
) {
const [initialized, setInitialized] = useState(false); const [initialized, setInitialized] = useState(false);
const onInitialize = useCallback(async () => { const onInitialize = useCallback(async () => {
try { try {
await createWeb3Wallet(); const web3walletInstance = await createWeb3Wallet();
setWeb3wallet(web3walletInstance);
setInitialized(true); setInitialized(true);
} catch (err: unknown) { } catch (err: unknown) {
console.error('Error for initializing', err); console.error('Error for initializing', err);
} }
}, []); }, [setWeb3wallet]);
useEffect(() => { useEffect(() => {
if (!initialized) { if (!initialized) {

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import { AppState, TouchableOpacity, View } from 'react-native'; import { AppState, TouchableOpacity, View } from 'react-native';
import { Button, Text, TextInput } from 'react-native-paper'; import { Button, Text, TextInput } from 'react-native-paper';
import { import {
@ -15,6 +15,7 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { web3WalletPair } from '../utils/wallet-connect/WalletConnectUtils'; import { web3WalletPair } from '../utils/wallet-connect/WalletConnectUtils';
import styles from '../styles/stylesheet'; import styles from '../styles/stylesheet';
import { StackParamsList } from '../types'; import { StackParamsList } from '../types';
import { useWalletConnect } from '../context/WalletConnectContext';
const AddSession = () => { const AddSession = () => {
const navigation = const navigation =
@ -23,6 +24,8 @@ const AddSession = () => {
const { hasPermission, requestPermission } = useCameraPermission(); const { hasPermission, requestPermission } = useCameraPermission();
const device = useCameraDevice('back'); const device = useCameraDevice('back');
const { web3wallet } = useWalletConnect();
const [currentWCURI, setCurrentWCURI] = useState<string>(''); const [currentWCURI, setCurrentWCURI] = useState<string>('');
const [isActive, setIsActive] = useState(AppState.currentState === 'active'); const [isActive, setIsActive] = useState(AppState.currentState === 'active');
const [isScanning, setScanning] = useState(true); const [isScanning, setScanning] = useState(true);
@ -45,11 +48,15 @@ const AddSession = () => {
await Linking.openSettings(); await Linking.openSettings();
}; };
const pair = async () => { const pair = useCallback(async () => {
const pairing = await web3WalletPair({ uri: currentWCURI }); if (!web3wallet) {
return;
}
const pairing = await web3WalletPair(web3wallet, { uri: currentWCURI });
navigation.navigate('WalletConnect'); navigation.navigate('WalletConnect');
return pairing; return pairing;
}; }, [web3wallet, currentWCURI, navigation]);
useEffect(() => { useEffect(() => {
const handleAppStateChange = (newState: string) => { const handleAppStateChange = (newState: string) => {

View File

@ -1,15 +1,16 @@
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Image, ScrollView, View } from 'react-native'; import { Image, ScrollView, View } from 'react-native';
import { Button, Text, TextInput } from 'react-native-paper'; import { Button, Text, TextInput } from 'react-native-paper';
import { SvgUri } from 'react-native-svg'; import { SvgUri } from 'react-native-svg';
import Config from 'react-native-config'; import Config from 'react-native-config';
import { MsgCreateValidator } from 'cosmjs-types/cosmos/staking/v1beta1/tx';
import { import {
NativeStackNavigationProp, NativeStackNavigationProp,
NativeStackScreenProps, NativeStackScreenProps,
} from '@react-navigation/native-stack'; } from '@react-navigation/native-stack';
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing'; import { DirectSecp256k1Wallet, EncodeObject } from '@cosmjs/proto-signing';
import { LaconicClient } from '@cerc-io/registry-sdk'; import { LaconicClient } from '@cerc-io/registry-sdk';
import { GasPrice, calculateFee } from '@cosmjs/stargate'; import { GasPrice, calculateFee } from '@cosmjs/stargate';
import { formatJsonRpcError } from '@json-rpc-tools/utils'; import { formatJsonRpcError } from '@json-rpc-tools/utils';
@ -25,10 +26,10 @@ import {
approveWalletConnectRequest, approveWalletConnectRequest,
rejectWalletConnectRequest, rejectWalletConnectRequest,
} from '../utils/wallet-connect/wallet-connect-requests'; } from '../utils/wallet-connect/wallet-connect-requests';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
import { MEMO } from './ApproveTransfer'; import { MEMO } from './ApproveTransfer';
import TxErrorDialog from '../components/TxErrorDialog'; import TxErrorDialog from '../components/TxErrorDialog';
import AccountDetails from '../components/AccountDetails'; import AccountDetails from '../components/AccountDetails';
import { useWalletConnect } from '../context/WalletConnectContext';
type ApproveTransactionProps = NativeStackScreenProps< type ApproveTransactionProps = NativeStackScreenProps<
StackParamsList, StackParamsList,
@ -42,13 +43,14 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
const requestName = requestSession.peer.metadata.name; const requestName = requestSession.peer.metadata.name;
const requestIcon = requestSession.peer.metadata.icons[0]; const requestIcon = requestSession.peer.metadata.icons[0];
const requestURL = requestSession.peer.metadata.url; const requestURL = requestSession.peer.metadata.url;
const transactionMessage = route.params.transactionMessage;
const signer = route.params.signer; const signer = route.params.signer;
const requestEvent = route.params.requestEvent; const requestEvent = route.params.requestEvent;
const chainId = requestEvent.params.chainId; const chainId = requestEvent.params.chainId;
const requestEventId = requestEvent.id; const requestEventId = requestEvent.id;
const topic = requestEvent.topic; const topic = requestEvent.topic;
const { web3wallet } = useWalletConnect();
const [account, setAccount] = useState<Account>(); const [account, setAccount] = useState<Account>();
const [cosmosStargateClient, setCosmosStargateClient] = const [cosmosStargateClient, setCosmosStargateClient] =
useState<LaconicClient>(); useState<LaconicClient>();
@ -67,6 +69,20 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
); );
const namespace = requestedNetwork!.namespace; const namespace = requestedNetwork!.namespace;
const transactionMessage = useMemo((): EncodeObject => {
const inputTxMsg = route.params.transactionMessage;
// If it's a MsgCreateValidator, decode the tx msg value using MsgCreateValidator type
if (inputTxMsg.typeUrl.includes('MsgCreateValidator')) {
return {
typeUrl: inputTxMsg.typeUrl,
value: MsgCreateValidator.fromJSON(inputTxMsg.value),
};
}
return inputTxMsg;
}, [route.params.transactionMessage]);
useEffect(() => { useEffect(() => {
if (namespace !== COSMOS) { if (namespace !== COSMOS) {
return; return;
@ -104,7 +120,15 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
}; };
setClient(); setClient();
}, [account, requestedNetwork, chainId, namespace, requestEventId, topic]); }, [
account,
requestedNetwork,
chainId,
namespace,
requestEventId,
topic,
web3wallet,
]);
const retrieveData = useCallback( const retrieveData = useCallback(
async (requestAddress: string) => { async (requestAddress: string) => {
@ -134,7 +158,7 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
return; return;
} }
const gasEstimation = await cosmosStargateClient!.simulate( const gasEstimation = await cosmosStargateClient!.simulate(
transactionMessage.value.participant!, signer,
[transactionMessage], [transactionMessage],
MEMO, MEMO,
); );
@ -152,7 +176,14 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
} }
}; };
getCosmosGas(); getCosmosGas();
}, [cosmosStargateClient, transactionMessage, requestEventId, topic]); }, [
cosmosStargateClient,
transactionMessage,
requestEventId,
topic,
web3wallet,
signer,
]);
useEffect(() => { useEffect(() => {
const gasPrice = GasPrice.fromString( const gasPrice = GasPrice.fromString(
@ -227,6 +258,13 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
navigation.navigate('Laconic'); navigation.navigate('Laconic');
}; };
const replacer = (key: string, value: any): any => {
if (value instanceof Uint8Array) {
return Buffer.from(value).toString('hex');
}
return value;
};
return ( return (
<> <>
<ScrollView contentContainerStyle={styles.approveTransaction}> <ScrollView contentContainerStyle={styles.approveTransaction}>
@ -251,7 +289,7 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
</Text> </Text>
<View style={styles.messageBody}> <View style={styles.messageBody}>
<Text variant="bodyLarge"> <Text variant="bodyLarge">
{JSON.stringify(transactionMessage, null, 2)} {JSON.stringify(transactionMessage, replacer, 2)}
</Text> </Text>
</View> </View>
<> <>

View File

@ -34,7 +34,6 @@ import {
rejectWalletConnectRequest, rejectWalletConnectRequest,
WalletConnectRequests, WalletConnectRequests,
} from '../utils/wallet-connect/wallet-connect-requests'; } from '../utils/wallet-connect/wallet-connect-requests';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
import DataBox from '../components/DataBox'; import DataBox from '../components/DataBox';
import { getPathKey } from '../utils/misc'; import { getPathKey } from '../utils/misc';
import { useNetworks } from '../context/NetworksContext'; import { useNetworks } from '../context/NetworksContext';
@ -42,6 +41,7 @@ import { COSMOS, EIP155, IS_NUMBER_REGEX } from '../utils/constants';
import TxErrorDialog from '../components/TxErrorDialog'; import TxErrorDialog from '../components/TxErrorDialog';
import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data'; import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data';
import { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData'; import { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData';
import { useWalletConnect } from '../context/WalletConnectContext';
export const MEMO = 'Sending signed tx from Laconic Wallet'; export const MEMO = 'Sending signed tx from Laconic Wallet';
// Reference: https://ethereum.org/en/developers/docs/gas/#what-is-gas-limit // Reference: https://ethereum.org/en/developers/docs/gas/#what-is-gas-limit
@ -64,6 +64,8 @@ const ApproveTransfer = ({ route }: SignRequestProps) => {
const chainId = requestEvent.params.chainId; const chainId = requestEvent.params.chainId;
const requestMethod = requestEvent.params.request.method; const requestMethod = requestEvent.params.request.method;
const { web3wallet } = useWalletConnect();
const [account, setAccount] = useState<Account>(); const [account, setAccount] = useState<Account>();
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [balance, setBalance] = useState<string>(''); const [balance, setBalance] = useState<string>('');

View File

@ -16,7 +16,6 @@ import styles from '../styles/stylesheet';
import { useAccounts } from '../context/AccountsContext'; import { useAccounts } from '../context/AccountsContext';
import { useWalletConnect } from '../context/WalletConnectContext'; import { useWalletConnect } from '../context/WalletConnectContext';
import { NetworksDataState, StackParamsList } from '../types'; import { NetworksDataState, StackParamsList } from '../types';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
import { useNetworks } from '../context/NetworksContext'; import { useNetworks } from '../context/NetworksContext';
const WCLogo = () => { const WCLogo = () => {
@ -33,7 +32,7 @@ const HomeScreen = () => {
const { networksData, selectedNetwork, setSelectedNetwork, setNetworksData } = const { networksData, selectedNetwork, setSelectedNetwork, setNetworksData } =
useNetworks(); useNetworks();
const { setActiveSessions } = useWalletConnect(); const { web3wallet, setActiveSessions } = useWalletConnect();
const navigation = const navigation =
useNavigation<NativeStackNavigationProp<StackParamsList>>(); useNavigation<NativeStackNavigationProp<StackParamsList>>();
@ -110,6 +109,7 @@ const HomeScreen = () => {
hideResetDialog(); hideResetDialog();
}, [ }, [
web3wallet,
setAccounts, setAccounts,
setActiveSessions, setActiveSessions,
setCurrentIndex, setCurrentIndex,

View File

@ -20,10 +20,10 @@ import {
rejectWalletConnectRequest, rejectWalletConnectRequest,
WalletConnectRequests, WalletConnectRequests,
} from '../utils/wallet-connect/wallet-connect-requests'; } from '../utils/wallet-connect/wallet-connect-requests';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data'; import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data';
import { useNetworks } from '../context/NetworksContext'; import { useNetworks } from '../context/NetworksContext';
import { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData'; import { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData';
import { useWalletConnect } from '../context/WalletConnectContext';
type SignRequestProps = NativeStackScreenProps<StackParamsList, 'SignRequest'>; type SignRequestProps = NativeStackScreenProps<StackParamsList, 'SignRequest'>;
@ -35,6 +35,8 @@ const SignRequest = ({ route }: SignRequestProps) => {
const requestIcon = requestSession?.peer?.metadata?.icons[0]; const requestIcon = requestSession?.peer?.metadata?.icons[0];
const requestURL = requestSession?.peer?.metadata?.url; const requestURL = requestSession?.peer?.metadata?.url;
const { web3wallet } = useWalletConnect();
const [account, setAccount] = useState<Account>(); const [account, setAccount] = useState<Account>();
const [message, setMessage] = useState<string>(''); const [message, setMessage] = useState<string>('');
const [namespace, setNamespace] = useState<string>(''); const [namespace, setNamespace] = useState<string>('');
@ -86,21 +88,13 @@ const SignRequest = ({ route }: SignRequestProps) => {
return; return;
} }
if (requestAccount !== account) {
setAccount(requestAccount); setAccount(requestAccount);
}
if (requestMessage !== message) {
setMessage(decodeURIComponent(requestMessage)); setMessage(decodeURIComponent(requestMessage));
}
if (requestNamespace !== namespace) {
setNamespace(requestNamespace); setNamespace(requestNamespace);
}
if (requestChainId !== chainId) {
setChainId(requestChainId); setChainId(requestChainId);
}
setIsLoading(false); setIsLoading(false);
}, },
[account, message, navigation, namespace, chainId], [navigation],
); );
const sanitizePath = useCallback( const sanitizePath = useCallback(

View File

@ -6,11 +6,10 @@ import { SvgUri } from 'react-native-svg';
import { getSdkError } from '@walletconnect/utils'; import { getSdkError } from '@walletconnect/utils';
import { useWalletConnect } from '../context/WalletConnectContext'; import { useWalletConnect } from '../context/WalletConnectContext';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
import styles from '../styles/stylesheet'; import styles from '../styles/stylesheet';
export default function WalletConnect() { export default function WalletConnect() {
const { activeSessions, setActiveSessions } = useWalletConnect(); const { web3wallet, activeSessions, setActiveSessions } = useWalletConnect();
const disconnect = async (sessionId: string) => { const disconnect = async (sessionId: string) => {
await web3wallet!.disconnectSession({ await web3wallet!.disconnectSession({
@ -23,9 +22,9 @@ export default function WalletConnect() {
}; };
useEffect(() => { useEffect(() => {
const sessions = web3wallet!.getActiveSessions(); const sessions = web3wallet?.getActiveSessions() || {};
setActiveSessions(sessions); setActiveSessions(sessions);
}, [setActiveSessions]); }, [web3wallet, setActiveSessions]);
return ( return (
<View> <View>

View File

@ -1,7 +1,7 @@
import { PopulatedTransaction } from 'ethers'; import { PopulatedTransaction } from 'ethers';
import { SignClientTypes, SessionTypes } from '@walletconnect/types'; import { SignClientTypes, SessionTypes } from '@walletconnect/types';
import { Web3WalletTypes } from '@walletconnect/web3wallet'; import { IWeb3Wallet, Web3WalletTypes } from '@walletconnect/web3wallet';
import { EncodeObject } from '@cosmjs/proto-signing'; import { EncodeObject } from '@cosmjs/proto-signing';
export type StackParamsList = { export type StackParamsList = {
@ -127,4 +127,6 @@ export interface WalletConnectContextProps {
setActiveSessions: ( setActiveSessions: (
activeSessions: Record<string, SessionTypes.Struct>, activeSessions: Record<string, SessionTypes.Struct>,
) => void; ) => void;
web3wallet: IWeb3Wallet | undefined;
setWeb3wallet: React.Dispatch<React.SetStateAction<IWeb3Wallet | undefined>>;
} }

View File

@ -1,9 +1,22 @@
import Config from 'react-native-config';
import { COSMOS_TESTNET_CHAINS } from './wallet-connect/COSMOSData'; import { COSMOS_TESTNET_CHAINS } from './wallet-connect/COSMOSData';
import { EIP155_CHAINS } from './wallet-connect/EIP155Data'; import { EIP155_CHAINS } from './wallet-connect/EIP155Data';
export const EIP155 = 'eip155'; export const EIP155 = 'eip155';
export const COSMOS = 'cosmos'; export const COSMOS = 'cosmos';
export const DEFAULT_NETWORKS = [ export const DEFAULT_NETWORKS = [
{
chainId: 'laconic_9000-1',
networkName: 'laconicd',
namespace: COSMOS,
rpcUrl: Config.LACONICD_RPC_URL,
blockExplorerUrl: '',
nativeDenom: 'alnt',
addressPrefix: 'laconic',
coinType: '118',
gasPrice: '1',
isDefault: true,
},
{ {
chainId: '1', chainId: '1',
networkName: EIP155_CHAINS['eip155:1'].name, networkName: EIP155_CHAINS['eip155:1'].name,

View File

@ -6,7 +6,6 @@ import { Core } from '@walletconnect/core';
import { ICore } from '@walletconnect/types'; import { ICore } from '@walletconnect/types';
import { Web3Wallet, IWeb3Wallet } from '@walletconnect/web3wallet'; import { Web3Wallet, IWeb3Wallet } from '@walletconnect/web3wallet';
export let web3wallet: IWeb3Wallet | undefined;
export let core: ICore; export let core: ICore;
export async function createWeb3Wallet() { export async function createWeb3Wallet() {
@ -14,7 +13,7 @@ export async function createWeb3Wallet() {
projectId: Config.WALLET_CONNECT_PROJECT_ID, projectId: Config.WALLET_CONNECT_PROJECT_ID,
}); });
web3wallet = await Web3Wallet.init({ const web3wallet = await Web3Wallet.init({
core, core,
metadata: { metadata: {
name: 'Laconic Wallet', name: 'Laconic Wallet',
@ -23,9 +22,14 @@ export async function createWeb3Wallet() {
icons: ['https://avatars.githubusercontent.com/u/92608123'], icons: ['https://avatars.githubusercontent.com/u/92608123'],
}, },
}); });
return web3wallet;
} }
export async function web3WalletPair(params: { uri: string }) { export async function web3WalletPair(
web3wallet: IWeb3Wallet,
params: { uri: string },
) {
if (web3wallet) { if (web3wallet) {
return await web3wallet.core.pairing.pair({ uri: params.uri }); return await web3wallet.core.pairing.pair({ uri: params.uri });
} }

View File

@ -40,7 +40,6 @@ export const getNamespaces = async (
networksData: NetworksDataState[], networksData: NetworksDataState[],
selectedNetwork: NetworksDataState, selectedNetwork: NetworksDataState,
accounts: Account[], accounts: Account[],
currentIndex: number,
) => { ) => {
const namespaceChainId = `${selectedNetwork.namespace}:${selectedNetwork.chainId}`; const namespaceChainId = `${selectedNetwork.namespace}:${selectedNetwork.chainId}`;
@ -101,23 +100,6 @@ export const getNamespaces = async (
const requiredAddressesArray = await Promise.all(requiredAddressesPromise); const requiredAddressesArray = await Promise.all(requiredAddressesPromise);
const requiredAddresses = requiredAddressesArray.flat(); const requiredAddresses = requiredAddressesArray.flat();
let sortedAccounts = requiredAddresses;
// If selected network is included in chains requested from dApp,
// Put selected account as first account
if (walletConnectChains.includes(namespaceChainId)) {
const currentAddresses = requiredAddresses.filter(address =>
address.includes(namespaceChainId),
);
sortedAccounts = [
currentAddresses[currentIndex],
...currentAddresses.filter((address, index) => index !== currentIndex),
...requiredAddresses.filter(
address => !currentAddresses.includes(address),
),
];
}
// construct namespace object // construct namespace object
const newNamespaces = { const newNamespaces = {
eip155: { eip155: {
@ -133,7 +115,7 @@ export const getNamespaces = async (
...(optionalNamespaces.eip155?.events ?? []), ...(optionalNamespaces.eip155?.events ?? []),
...(requiredNamespaces.eip155?.events ?? []), ...(requiredNamespaces.eip155?.events ?? []),
], ],
accounts: sortedAccounts.filter(account => account.includes(EIP155)), accounts: requiredAddresses.filter(account => account.includes(EIP155)),
}, },
cosmos: { cosmos: {
chains: walletConnectChains.filter(chain => chain.includes(COSMOS)), chains: walletConnectChains.filter(chain => chain.includes(COSMOS)),
@ -147,18 +129,12 @@ export const getNamespaces = async (
...(optionalNamespaces.cosmos?.events ?? []), ...(optionalNamespaces.cosmos?.events ?? []),
...(requiredNamespaces.cosmos?.events ?? []), ...(requiredNamespaces.cosmos?.events ?? []),
], ],
accounts: sortedAccounts.filter(account => account.includes(COSMOS)), accounts: requiredAddresses.filter(account => account.includes(COSMOS)),
}, },
}; };
return newNamespaces; return newNamespaces;
} else { } else {
// Set selected account as the first account in supported namespaces
const sortedAccounts = [
accounts[currentIndex],
...accounts.filter((account, index) => index !== currentIndex),
];
switch (selectedNetwork.namespace) { switch (selectedNetwork.namespace) {
case EIP155: case EIP155:
return { return {
@ -175,7 +151,7 @@ export const getNamespaces = async (
...(optionalNamespaces.eip155?.events ?? []), ...(optionalNamespaces.eip155?.events ?? []),
...(requiredNamespaces.eip155?.events ?? []), ...(requiredNamespaces.eip155?.events ?? []),
], ],
accounts: sortedAccounts.map(ethAccount => { accounts: accounts.map(ethAccount => {
return `${namespaceChainId}:${ethAccount.address}`; return `${namespaceChainId}:${ethAccount.address}`;
}), }),
}, },
@ -200,7 +176,7 @@ export const getNamespaces = async (
...(optionalNamespaces.cosmos?.events ?? []), ...(optionalNamespaces.cosmos?.events ?? []),
...(requiredNamespaces.cosmos?.events ?? []), ...(requiredNamespaces.cosmos?.events ?? []),
], ],
accounts: sortedAccounts.map(cosmosAccount => { accounts: accounts.map(cosmosAccount => {
return `${namespaceChainId}:${cosmosAccount.address}`; return `${namespaceChainId}:${cosmosAccount.address}`;
}), }),
}, },

View File

@ -752,10 +752,10 @@
deepmerge "^3.2.0" deepmerge "^3.2.0"
hoist-non-react-statics "^3.3.0" hoist-non-react-statics "^3.3.0"
"@cerc-io/registry-sdk@^0.2.2": "@cerc-io/registry-sdk@^0.2.5":
version "0.2.2" version "0.2.5"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fregistry-sdk/-/0.2.2/registry-sdk-0.2.2.tgz#2e8a533f069b4bb9f4cd4798e783f52e29167d0d" resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fregistry-sdk/-/0.2.5/registry-sdk-0.2.5.tgz#9ca19fecb2923520dd6a19946c309ecb2ec780a2"
integrity sha512-ocRMbWtdewOg02ORfK1U+qbTqB46anHP4ApXokGkY4d+mFSApR3sdUEi2geHcs8oh+SG8YAp7LUJ9AAJneNY8g== integrity sha512-/KXAYf9gStaX/rRBMCEeDCexEIpTOFHeHzMK9B3xfCT+SyYZE9WC9GpX299LzBYJKKPsb0/JvnDfip9S1igJtA==
dependencies: dependencies:
"@cosmjs/amino" "^0.28.1" "@cosmjs/amino" "^0.28.1"
"@cosmjs/crypto" "^0.28.1" "@cosmjs/crypto" "^0.28.1"
@ -2071,6 +2071,11 @@
dependencies: dependencies:
merge-options "^3.0.4" merge-options "^3.0.4"
"@react-native-clipboard/clipboard@^1.14.1":
version "1.14.1"
resolved "https://registry.yarnpkg.com/@react-native-clipboard/clipboard/-/clipboard-1.14.1.tgz#835f82fc86881a0808a8405f2576617bb5383554"
integrity sha512-SM3el0A28SwoeJljVNhF217o0nI4E7RfalLmuRQcT1/7tGcxUjgFa3jyrEndYUct8/uxxK5EUNGUu1YEDqzxqw==
"@react-native-community/cli-clean@12.3.2": "@react-native-community/cli-clean@12.3.2":
version "12.3.2" version "12.3.2"
resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-12.3.2.tgz#d4f1730c3d22d816b4d513d330d5f3896a3f5921" resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-12.3.2.tgz#d4f1730c3d22d816b4d513d330d5f3896a3f5921"