Compare commits
19 Commits
7f44fc1cf0
...
ec3617ad42
Author | SHA1 | Date | |
---|---|---|---|
ec3617ad42 | |||
393a42fceb | |||
4ff1c10699 | |||
17810801dd | |||
|
da3e4534d0 | ||
a1747b8ba7 | |||
|
c818bc0b9d | ||
|
b2a043068a | ||
|
f67569a25e | ||
|
38d68a86d5 | ||
7b72631206 | |||
45e045d956 | |||
181f9e8b7d | |||
cca89775aa | |||
|
afd09dc4c1 | ||
e0632d1a50 | |||
ba05a82406 | |||
2bb92205ba | |||
c26bddec1a |
18
prettier.config.js
Normal file
18
prettier.config.js
Normal file
@ -0,0 +1,18 @@
|
||||
const config = {
|
||||
arrowParens: "always",
|
||||
printWidth: 80,
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
tabWidth: 2,
|
||||
trailingComma: "es5",
|
||||
importOrderSeparation: true,
|
||||
importOrderSortSpecifiers: true,
|
||||
plugins: ["@trivago/prettier-plugin-sort-imports"],
|
||||
importOrder: [
|
||||
"<THIRD_PARTY_MODULES>",
|
||||
"^(pages|components|utils|icons|test|graphql)/(.*)$",
|
||||
"^[./]",
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
@ -1,5 +1,5 @@
|
||||
import { View } from "react-native";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
|
||||
import { CreateWalletProps } from "../types";
|
||||
|
66
src/components/ImportWalletDialog.tsx
Normal file
66
src/components/ImportWalletDialog.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { Dialog, DialogActions, DialogContent, DialogTitle, TextField, Grid, Button, Typography } from "@mui/material";
|
||||
|
||||
const ImportWalletDialog = ({
|
||||
visible,
|
||||
hideDialog,
|
||||
importWalletHandler
|
||||
}: {
|
||||
visible: boolean;
|
||||
hideDialog: () => void;
|
||||
importWalletHandler: (recoveryPhrase: string) => Promise<void>;
|
||||
}) => {
|
||||
const [words, setWords] = useState(Array(12).fill(''));
|
||||
|
||||
const handleWordChange = (index: number, value: string) => {
|
||||
const newWords = [...words];
|
||||
newWords[index] = value;
|
||||
setWords(newWords);
|
||||
};
|
||||
|
||||
const handlePaste = (event: React.ClipboardEvent<HTMLDivElement>) => {
|
||||
const pastedText = event.clipboardData.getData('Text');
|
||||
const splitWords = pastedText.trim().split(/\s+/);
|
||||
|
||||
if (splitWords.length === 12) {
|
||||
setWords(splitWords);
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setWords(Array(12).fill(''));
|
||||
},[visible]);
|
||||
|
||||
return (
|
||||
<Dialog open={visible} onClose={hideDialog}>
|
||||
<DialogTitle>Import your wallet from your mnemonic</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography>
|
||||
(You can paste your entire mnemonic into the first textbox)
|
||||
</Typography>
|
||||
<Grid container spacing={2}>
|
||||
{words.map((word, index) => (
|
||||
<Grid item xs={6} sm={4} key={index}>
|
||||
<TextField
|
||||
value={word}
|
||||
onChange={(e) => handleWordChange(index, e.target.value)}
|
||||
onPaste={index === 0 ? handlePaste : undefined}
|
||||
placeholder={`Word ${index + 1}`}
|
||||
fullWidth
|
||||
margin="normal"
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => importWalletHandler(words.join(' '))}>Import Wallet</Button>
|
||||
<Button onClick={hideDialog}>Cancel</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImportWalletDialog;
|
51
src/components/MnemonicDialog.tsx
Normal file
51
src/components/MnemonicDialog.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Button,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
|
||||
import styles from "../styles/stylesheet";
|
||||
import GridView from "./Grid";
|
||||
import { CustomDialogProps } from "../types";
|
||||
|
||||
const MnemonicDialog = ({
|
||||
visible,
|
||||
hideDialog,
|
||||
contentText,
|
||||
}: CustomDialogProps) => {
|
||||
const words = contentText.split(" ");
|
||||
|
||||
const copyMnemonic = () => {
|
||||
navigator.clipboard.writeText(contentText);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={visible} onClose={hideDialog}>
|
||||
<DialogTitle style={{ ...styles.mnemonicTitle }}>Mnemonic</DialogTitle>
|
||||
<DialogContent style={{ ...styles.mnemonicContainer }}>
|
||||
<Typography component="div">
|
||||
Your mnemonic provides full access to your wallet and funds. <br />
|
||||
Make sure to note it down.
|
||||
</Typography>
|
||||
<Typography component="div" style={{ ...styles.mnomonicDialogWarning }}>
|
||||
Do not share your mnemonic with anyone
|
||||
</Typography>
|
||||
<GridView words={words} />
|
||||
</DialogContent>
|
||||
<DialogActions style={{ ...styles.mnemonicButtonRow }}>
|
||||
<Button style={{ ...styles.mnemonicButton }} onClick={copyMnemonic}>
|
||||
Copy
|
||||
</Button>
|
||||
<Button style={{ ...styles.mnemonicButton }} onClick={hideDialog}>
|
||||
Done
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export { MnemonicDialog };
|
@ -1,13 +1,14 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Image, ScrollView, View } from 'react-native';
|
||||
import { Button, Text, TextInput } from 'react-native-paper';
|
||||
import { MsgCreateValidator } from 'cosmjs-types/cosmos/staking/v1beta1/tx';
|
||||
|
||||
import {
|
||||
NativeStackNavigationProp,
|
||||
NativeStackScreenProps,
|
||||
} from '@react-navigation/native-stack';
|
||||
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 { GasPrice, calculateFee } from '@cosmjs/stargate';
|
||||
import { formatJsonRpcError } from '@json-rpc-tools/utils';
|
||||
@ -40,7 +41,6 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
||||
const requestName = requestSession.peer.metadata.name;
|
||||
const requestIcon = requestSession.peer.metadata.icons[0];
|
||||
const requestURL = requestSession.peer.metadata.url;
|
||||
const transactionMessage = route.params.transactionMessage;
|
||||
const signer = route.params.signer;
|
||||
const requestEvent = route.params.requestEvent;
|
||||
const chainId = requestEvent.params.chainId;
|
||||
@ -67,6 +67,20 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
||||
|
||||
const { web3wallet } = useWalletConnect();
|
||||
|
||||
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(() => {
|
||||
if (namespace !== COSMOS) {
|
||||
return;
|
||||
@ -138,7 +152,7 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
||||
return;
|
||||
}
|
||||
const gasEstimation = await cosmosStargateClient!.simulate(
|
||||
transactionMessage.value.participant!,
|
||||
signer,
|
||||
[transactionMessage],
|
||||
MEMO,
|
||||
);
|
||||
@ -160,7 +174,7 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
||||
}
|
||||
};
|
||||
getCosmosGas();
|
||||
}, [cosmosStargateClient, transactionMessage, requestEventId, topic, web3wallet]);
|
||||
}, [cosmosStargateClient, transactionMessage, requestEventId, topic, web3wallet, signer]);
|
||||
|
||||
useEffect(() => {
|
||||
const gasPrice = GasPrice.fromString(
|
||||
@ -239,6 +253,13 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
||||
navigation.navigate('Home');
|
||||
};
|
||||
|
||||
const replacer = (key: string, value: any): any => {
|
||||
if (value instanceof Uint8Array) {
|
||||
return Buffer.from(value).toString('hex');
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ScrollView contentContainerStyle={styles.approveTransaction}>
|
||||
@ -264,7 +285,7 @@ const ApproveTransaction = ({ route }: ApproveTransactionProps) => {
|
||||
</Text>
|
||||
<View style={styles.messageBody}>
|
||||
<Text variant="bodyLarge">
|
||||
{JSON.stringify(transactionMessage, null, 2)}
|
||||
{JSON.stringify(transactionMessage, replacer, 2)}
|
||||
</Text>
|
||||
</View>
|
||||
<>
|
||||
|
@ -1,14 +1,13 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { View, ActivityIndicator, Image } from "react-native";
|
||||
import { Text } from "react-native-paper";
|
||||
import { Button, Divider } from "@mui/material";
|
||||
import { Button, Divider, Portal, Snackbar } from "@mui/material";
|
||||
|
||||
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import { getSdkError } from "@walletconnect/utils";
|
||||
|
||||
import { createWallet, resetWallet, retrieveAccounts } from "../utils/accounts";
|
||||
import { DialogComponent } from "../components/Dialog";
|
||||
import { NetworkDropdown } from "../components/NetworkDropdown";
|
||||
import Accounts from "../components/Accounts";
|
||||
import CreateWallet from "../components/CreateWallet";
|
||||
@ -18,6 +17,8 @@ import { useAccounts } from "../context/AccountsContext";
|
||||
import { useWalletConnect } from "../context/WalletConnectContext";
|
||||
import { NetworksDataState, StackParamsList } from "../types";
|
||||
import { useNetworks } from "../context/NetworksContext";
|
||||
import ImportWalletDialog from "../components/ImportWalletDialog";
|
||||
import { MnemonicDialog } from "../components/MnemonicDialog";
|
||||
import { Container } from "../components/Container";
|
||||
|
||||
const WCLogo = () => {
|
||||
@ -57,12 +58,15 @@ const HomeScreen = () => {
|
||||
|
||||
const [isWalletCreated, setIsWalletCreated] = useState<boolean>(false);
|
||||
const [isWalletCreating, setIsWalletCreating] = useState<boolean>(false);
|
||||
const [walletDialog, setWalletDialog] = useState<boolean>(false);
|
||||
const [importWalletDialog, setImportWalletDialog] = useState<boolean>(false);
|
||||
const [mnemonicDialog, setMnemonicDialog] = useState<boolean>(false);
|
||||
const [resetWalletDialog, setResetWalletDialog] = useState<boolean>(false);
|
||||
const [toastVisible, setToastVisible] = useState(false);
|
||||
const [invalidMnemonicError, setInvalidMnemonicError] = useState("");
|
||||
const [isAccountsFetched, setIsAccountsFetched] = useState<boolean>(true);
|
||||
const [phrase, setPhrase] = useState("");
|
||||
|
||||
const hideWalletDialog = () => setWalletDialog(false);
|
||||
const hideMnemonicDialog = () => setMnemonicDialog(false);
|
||||
const hideResetDialog = () => setResetWalletDialog(false);
|
||||
|
||||
const fetchAccounts = useCallback(async () => {
|
||||
@ -85,12 +89,27 @@ const HomeScreen = () => {
|
||||
const mnemonic = await createWallet(networksData);
|
||||
if (mnemonic) {
|
||||
fetchAccounts();
|
||||
setWalletDialog(true);
|
||||
setMnemonicDialog(true);
|
||||
setPhrase(mnemonic);
|
||||
setSelectedNetwork(networksData[0]);
|
||||
}
|
||||
};
|
||||
|
||||
const importWalletHandler = async (recoveryPhrase: string) => {
|
||||
try {
|
||||
const mnemonic = await createWallet(networksData, recoveryPhrase);
|
||||
if (mnemonic) {
|
||||
fetchAccounts();
|
||||
setPhrase(mnemonic);
|
||||
setSelectedNetwork(networksData[0]);
|
||||
setImportWalletDialog(false);
|
||||
}
|
||||
} catch (error: any) {
|
||||
setInvalidMnemonicError((error.message as string).toUpperCase());
|
||||
setToastVisible(true);
|
||||
}
|
||||
};
|
||||
|
||||
const confirmResetWallet = useCallback(async () => {
|
||||
setIsWalletCreated(false);
|
||||
setIsWalletCreating(false);
|
||||
@ -154,14 +173,29 @@ const HomeScreen = () => {
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<CreateWallet
|
||||
isWalletCreating={isWalletCreating}
|
||||
createWalletHandler={createWalletHandler}
|
||||
/>
|
||||
<>
|
||||
<CreateWallet
|
||||
isWalletCreating={isWalletCreating}
|
||||
createWalletHandler={createWalletHandler}
|
||||
/>
|
||||
<View style={styles.createWalletContainer}>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => setImportWalletDialog(true)}
|
||||
>
|
||||
Import Wallet
|
||||
</Button>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
<DialogComponent
|
||||
visible={walletDialog}
|
||||
hideDialog={hideWalletDialog}
|
||||
<ImportWalletDialog
|
||||
visible={importWalletDialog}
|
||||
hideDialog={() => setImportWalletDialog(false)}
|
||||
importWalletHandler={importWalletHandler}
|
||||
/>
|
||||
<MnemonicDialog
|
||||
visible={mnemonicDialog}
|
||||
hideDialog={hideMnemonicDialog}
|
||||
contentText={phrase}
|
||||
/>
|
||||
<ConfirmDialog
|
||||
@ -171,6 +205,16 @@ const HomeScreen = () => {
|
||||
onConfirm={confirmResetWallet}
|
||||
/>
|
||||
</Container>
|
||||
<Portal>
|
||||
<Snackbar
|
||||
open={toastVisible}
|
||||
autoHideDuration={3000}
|
||||
message={invalidMnemonicError}
|
||||
onClose={() => setToastVisible(false)}
|
||||
anchorOrigin={{ horizontal: "center", vertical: "bottom" }}
|
||||
ContentProps={{ style: { backgroundColor: "red", color: "white" } }}
|
||||
/>
|
||||
</Portal>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
@ -4,7 +4,7 @@ const styles = StyleSheet.create({
|
||||
createWalletContainer: {
|
||||
marginTop: 20,
|
||||
alignSelf: 'center',
|
||||
marginBottom: 40
|
||||
marginBottom: 30,
|
||||
},
|
||||
signLink: {
|
||||
alignItems: 'flex-end',
|
||||
|
@ -27,12 +27,23 @@ import { COSMOS, EIP155 } from './constants';
|
||||
|
||||
const createWallet = async (
|
||||
networksData: NetworksDataState[],
|
||||
recoveryPhrase?: string,
|
||||
): Promise<string> => {
|
||||
const mnemonic = utils.entropyToMnemonic(utils.randomBytes(16));
|
||||
await setInternetCredentials('mnemonicServer', 'mnemonic', mnemonic);
|
||||
const mnemonic = recoveryPhrase ? recoveryPhrase : utils.entropyToMnemonic(utils.randomBytes(16));
|
||||
|
||||
const hdNode = HDNode.fromMnemonic(mnemonic);
|
||||
await setInternetCredentials('mnemonicServer', 'mnemonic', mnemonic);
|
||||
|
||||
await createWalletFromMnemonic(networksData, hdNode, mnemonic);
|
||||
|
||||
return mnemonic;
|
||||
};
|
||||
|
||||
const createWalletFromMnemonic = async (
|
||||
networksData: NetworksDataState[],
|
||||
hdNode: HDNode,
|
||||
mnemonic: string
|
||||
): Promise<void> => {
|
||||
for (const network of networksData) {
|
||||
const hdPath = `m/44'/${network.coinType}'/0'/0/0`;
|
||||
const node = hdNode.derivePath(hdPath);
|
||||
@ -73,8 +84,6 @@ const createWallet = async (
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
return mnemonic;
|
||||
};
|
||||
|
||||
const addAccount = async (
|
||||
|
@ -46,3 +46,5 @@ export const EMPTY_FIELD_ERROR = 'Field cannot be empty';
|
||||
export const INVALID_URL_ERROR = 'Invalid URL';
|
||||
|
||||
export const IS_NUMBER_REGEX = /^\d+$/;
|
||||
|
||||
export const IS_IMPORT_WALLET_ENABLED = false;
|
||||
|
@ -7,7 +7,7 @@ import { getSdkError } from '@walletconnect/utils';
|
||||
import {
|
||||
SigningStargateClient,
|
||||
StdFee,
|
||||
MsgSendEncodeObject,
|
||||
MsgSendEncodeObject
|
||||
} from '@cosmjs/stargate';
|
||||
import { EncodeObject } from '@cosmjs/proto-signing';
|
||||
import { LaconicClient } from '@cerc-io/registry-sdk';
|
||||
|
Loading…
Reference in New Issue
Block a user