Revert "Import accounts with private key (#29)"
This reverts commit b362d16862
.
This commit is contained in:
parent
7509130cc5
commit
3c3ba629ec
|
@ -4,83 +4,35 @@ import Button from '@material-ui/core/Button';
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||||
import DialogContent from '@material-ui/core/DialogContent';
|
import DialogContent from '@material-ui/core/DialogContent';
|
||||||
import TextField from '@material-ui/core/TextField';
|
import TextField from '@material-ui/core/TextField';
|
||||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
|
||||||
import FormGroup from '@material-ui/core/FormGroup';
|
|
||||||
import Switch from '@material-ui/core/Switch';
|
|
||||||
import { Account } from '@solana/web3.js';
|
|
||||||
import * as bs58 from 'bs58';
|
|
||||||
import DialogForm from './DialogForm';
|
import DialogForm from './DialogForm';
|
||||||
|
|
||||||
export default function AddAccountDialog({ open, onAdd, onClose }) {
|
export default function AddAccountDialog({ open, onAdd, onClose }) {
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [isImport, setIsImport] = useState(false);
|
|
||||||
const [importedPrivateKey, setPrivateKey] = useState('');
|
|
||||||
|
|
||||||
const importedAccount = isImport ? decodeAccount(importedPrivateKey) : undefined;
|
|
||||||
const isAddEnabled = isImport ? name && importedAccount !== undefined : name;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DialogForm
|
<DialogForm
|
||||||
open={open}
|
open={open}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
onSubmit={() => onAdd({ name, importedAccount })}
|
onSubmit={() => onAdd(name)}
|
||||||
fullWidth
|
fullWidth
|
||||||
>
|
>
|
||||||
<DialogTitle>Add account</DialogTitle>
|
<DialogTitle>Add account</DialogTitle>
|
||||||
<DialogContent style={{ paddingTop: 16 }}>
|
<DialogContent style={{ paddingTop: 16 }}>
|
||||||
<div
|
<TextField
|
||||||
style={{
|
label="Name"
|
||||||
display: 'flex',
|
fullWidth
|
||||||
flexDirection: 'column',
|
variant="outlined"
|
||||||
}}
|
margin="normal"
|
||||||
>
|
value={name}
|
||||||
<TextField
|
onChange={(e) => setName(e.target.value.trim())}
|
||||||
label="Name"
|
/>
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
margin="normal"
|
|
||||||
value={name}
|
|
||||||
onChange={(e) => setName(e.target.value.trim())}
|
|
||||||
/>
|
|
||||||
<FormGroup>
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Switch
|
|
||||||
checked={isImport}
|
|
||||||
onChange={() => setIsImport(!isImport)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Import private key"
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
{isImport && (
|
|
||||||
<TextField
|
|
||||||
label="Paste your private key here"
|
|
||||||
fullWidth
|
|
||||||
type="password"
|
|
||||||
value={importedPrivateKey}
|
|
||||||
variant="outlined"
|
|
||||||
margin="normal"
|
|
||||||
onChange={(e) => setPrivateKey(e.target.value.trim())}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={onClose}>Close</Button>
|
<Button onClick={onClose}>Close</Button>
|
||||||
<Button type="submit" color="primary" disabled={!isAddEnabled}>
|
<Button type="submit" color="primary" disabled={!name}>
|
||||||
Add
|
Add
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</DialogForm>
|
</DialogForm>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeAccount(privateKey) {
|
|
||||||
try {
|
|
||||||
const a = new Account(bs58.decode(privateKey));
|
|
||||||
return a;
|
|
||||||
} catch (_) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ function NetworkSelector() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function WalletSelector() {
|
function WalletSelector() {
|
||||||
const { accounts, setWalletSelector, addAccount } = useWalletSelector();
|
const { accounts, walletIndex, setWalletIndex } = useWalletSelector();
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
const [addAccountOpen, setAddAccountOpen] = useState(false);
|
const [addAccountOpen, setAddAccountOpen] = useState(false);
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
@ -140,14 +140,8 @@ function WalletSelector() {
|
||||||
<AddAccountDialog
|
<AddAccountDialog
|
||||||
open={addAccountOpen}
|
open={addAccountOpen}
|
||||||
onClose={() => setAddAccountOpen(false)}
|
onClose={() => setAddAccountOpen(false)}
|
||||||
onAdd={({ name, importedAccount }) => {
|
onAdd={(name) => {
|
||||||
addAccount({ name, importedAccount });
|
setWalletIndex(accounts.length, name);
|
||||||
setWalletSelector({
|
|
||||||
walletIndex: importedAccount ? undefined : accounts.length,
|
|
||||||
importedPubkey: importedAccount
|
|
||||||
? importedAccount.publicKey.toString()
|
|
||||||
: undefined,
|
|
||||||
});
|
|
||||||
setAddAccountOpen(false);
|
setAddAccountOpen(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -177,21 +171,23 @@ function WalletSelector() {
|
||||||
}}
|
}}
|
||||||
getContentAnchorEl={null}
|
getContentAnchorEl={null}
|
||||||
>
|
>
|
||||||
{accounts.map(({ isSelected, selector, address, name, label }) => (
|
{accounts.map(({ index, address, name }) => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={address.toBase58()}
|
key={address.toBase58()}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
setWalletSelector(selector);
|
setWalletIndex(index);
|
||||||
}}
|
}}
|
||||||
selected={isSelected}
|
selected={index === walletIndex}
|
||||||
component="div"
|
component="div"
|
||||||
>
|
>
|
||||||
<ListItemIcon className={classes.menuItemIcon}>
|
<ListItemIcon className={classes.menuItemIcon}>
|
||||||
{isSelected ? <CheckIcon fontSize="small" /> : null}
|
{index === walletIndex ? <CheckIcon fontSize="small" /> : null}
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
<Typography>{name}</Typography>
|
<Typography>
|
||||||
|
{index === 0 ? 'Main account' : name || `Account ${index}`}
|
||||||
|
</Typography>
|
||||||
<Typography color="textSecondary">
|
<Typography color="textSecondary">
|
||||||
{address.toBase58()}
|
{address.toBase58()}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -208,7 +204,7 @@ function WalletSelector() {
|
||||||
<ListItemIcon className={classes.menuItemIcon}>
|
<ListItemIcon className={classes.menuItemIcon}>
|
||||||
<AddIcon fontSize="small" />
|
<AddIcon fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
Add Account
|
Create Account
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -28,10 +28,7 @@ import Link from '@material-ui/core/Link';
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography';
|
||||||
import { useAsyncData } from '../utils/fetch-loop';
|
import { useAsyncData } from '../utils/fetch-loop';
|
||||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||||
import {
|
import {TOKEN_PROGRAM_ID, WRAPPED_SOL_MINT} from '../utils/tokens/instructions';
|
||||||
TOKEN_PROGRAM_ID,
|
|
||||||
WRAPPED_SOL_MINT,
|
|
||||||
} from '../utils/tokens/instructions';
|
|
||||||
import { parseTokenAccountData } from '../utils/tokens/data';
|
import { parseTokenAccountData } from '../utils/tokens/data';
|
||||||
|
|
||||||
const WUSDC_MINT = new PublicKey(
|
const WUSDC_MINT = new PublicKey(
|
||||||
|
@ -137,10 +134,9 @@ export default function SendDialog({ open, onClose, publicKey, balanceInfo }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function SendSplDialog({ onClose, publicKey, balanceInfo, onSubmitRef }) {
|
function SendSplDialog({ onClose, publicKey, balanceInfo, onSubmitRef }) {
|
||||||
const defaultAddressHelperText =
|
const defaultAddressHelperText = !balanceInfo.mint || balanceInfo.mint.equals(WRAPPED_SOL_MINT) ?
|
||||||
!balanceInfo.mint || balanceInfo.mint.equals(WRAPPED_SOL_MINT)
|
'Enter Solana Address' :
|
||||||
? 'Enter Solana Address'
|
'Enter SPL token or Solana address';
|
||||||
: 'Enter SPL token or Solana address';
|
|
||||||
const wallet = useWallet();
|
const wallet = useWallet();
|
||||||
const [sendTransaction, sending] = useSendTransaction();
|
const [sendTransaction, sending] = useSendTransaction();
|
||||||
const [addressHelperText, setAddressHelperText] = useState(
|
const [addressHelperText, setAddressHelperText] = useState(
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { pbkdf2 } from 'crypto';
|
import { pbkdf2 } from 'crypto';
|
||||||
import { randomBytes, secretbox } from 'tweetnacl';
|
import { randomBytes, secretbox } from 'tweetnacl';
|
||||||
import * as bip32 from 'bip32';
|
|
||||||
import bs58 from 'bs58';
|
import bs58 from 'bs58';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
|
@ -20,20 +19,11 @@ export async function mnemonicToSeed(mnemonic) {
|
||||||
return Buffer.from(seed).toString('hex');
|
return Buffer.from(seed).toString('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
let unlockedMnemonicAndSeed = (() => {
|
let unlockedMnemonicAndSeed = JSON.parse(
|
||||||
const stored = JSON.parse(
|
sessionStorage.getItem('unlocked') ||
|
||||||
sessionStorage.getItem('unlocked') ||
|
localStorage.getItem('unlocked') ||
|
||||||
localStorage.getItem('unlocked') ||
|
'null',
|
||||||
'null',
|
) || { mnemonic: null, seed: null };
|
||||||
);
|
|
||||||
if (stored === null) {
|
|
||||||
return { mnemonic: null, seed: null, importsEncryptionKey: null };
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
importsEncryptionKey: deriveImportsEncryptionKey(stored.seed),
|
|
||||||
...stored,
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
export const walletSeedChanged = new EventEmitter();
|
export const walletSeedChanged = new EventEmitter();
|
||||||
|
|
||||||
export function getUnlockedMnemonicAndSeed() {
|
export function getUnlockedMnemonicAndSeed() {
|
||||||
|
@ -44,8 +34,8 @@ export function hasLockedMnemonicAndSeed() {
|
||||||
return !!localStorage.getItem('locked');
|
return !!localStorage.getItem('locked');
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUnlockedMnemonicAndSeed(mnemonic, seed, importsEncryptionKey) {
|
function setUnlockedMnemonicAndSeed(mnemonic, seed) {
|
||||||
unlockedMnemonicAndSeed = { mnemonic, seed, importsEncryptionKey };
|
unlockedMnemonicAndSeed = { mnemonic, seed };
|
||||||
walletSeedChanged.emit('change', unlockedMnemonicAndSeed);
|
walletSeedChanged.emit('change', unlockedMnemonicAndSeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,8 +67,7 @@ export async function storeMnemonicAndSeed(mnemonic, seed, password) {
|
||||||
localStorage.removeItem('locked');
|
localStorage.removeItem('locked');
|
||||||
sessionStorage.removeItem('unlocked');
|
sessionStorage.removeItem('unlocked');
|
||||||
}
|
}
|
||||||
const privateKey = deriveImportsEncryptionKey(seed);
|
setUnlockedMnemonicAndSeed(mnemonic, seed);
|
||||||
setUnlockedMnemonicAndSeed(mnemonic, seed, privateKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadMnemonicAndSeed(password, stayLoggedIn) {
|
export async function loadMnemonicAndSeed(password, stayLoggedIn) {
|
||||||
|
@ -102,8 +91,7 @@ export async function loadMnemonicAndSeed(password, stayLoggedIn) {
|
||||||
if (stayLoggedIn) {
|
if (stayLoggedIn) {
|
||||||
sessionStorage.setItem('unlocked', decodedPlaintext);
|
sessionStorage.setItem('unlocked', decodedPlaintext);
|
||||||
}
|
}
|
||||||
const privateKey = deriveImportsEncryptionKey(seed);
|
setUnlockedMnemonicAndSeed(mnemonic, seed);
|
||||||
setUnlockedMnemonicAndSeed(mnemonic, seed, privateKey);
|
|
||||||
return { mnemonic, seed };
|
return { mnemonic, seed };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,12 +109,5 @@ async function deriveEncryptionKey(password, salt, iterations, digest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function lockWallet() {
|
export function lockWallet() {
|
||||||
setUnlockedMnemonicAndSeed(null, null, null);
|
setUnlockedMnemonicAndSeed(null, null);
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the 32 byte key used to encrypt imported private keys.
|
|
||||||
function deriveImportsEncryptionKey(seed) {
|
|
||||||
// SLIP16 derivation path.
|
|
||||||
return bip32.fromSeed(Buffer.from(seed, 'hex')).derivePath("m/10016'/0")
|
|
||||||
.privateKey;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
import React, { useContext, useMemo } from 'react';
|
import React, { useContext, useMemo } from 'react';
|
||||||
import * as bip32 from 'bip32';
|
import * as bip32 from 'bip32';
|
||||||
import * as bs58 from 'bs58';
|
import { Account, SystemProgram, Transaction } from '@solana/web3.js';
|
||||||
import {
|
|
||||||
Account,
|
|
||||||
SystemProgram,
|
|
||||||
Transaction,
|
|
||||||
PublicKey,
|
|
||||||
} from '@solana/web3.js';
|
|
||||||
import nacl from 'tweetnacl';
|
import nacl from 'tweetnacl';
|
||||||
import {
|
import {
|
||||||
setInitialAccountInfo,
|
setInitialAccountInfo,
|
||||||
|
@ -30,15 +24,12 @@ import { useTokenName } from './tokens/names';
|
||||||
import { refreshCache, useAsyncData } from './fetch-loop';
|
import { refreshCache, useAsyncData } from './fetch-loop';
|
||||||
import { getUnlockedMnemonicAndSeed, walletSeedChanged } from './wallet-seed';
|
import { getUnlockedMnemonicAndSeed, walletSeedChanged } from './wallet-seed';
|
||||||
|
|
||||||
const DEFAULT_WALLET_SELECTOR = {
|
|
||||||
walletIndex: 0,
|
|
||||||
importedPubkey: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
export class Wallet {
|
export class Wallet {
|
||||||
constructor(connection, account) {
|
constructor(connection, seed, walletIndex = 0) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
this.account = account;
|
this.seed = seed;
|
||||||
|
this.walletIndex = walletIndex;
|
||||||
|
this.account = Wallet.getAccountFromSeed(this.seed, this.walletIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getAccountFromSeed(seed, walletIndex, accountIndex = 0) {
|
static getAccountFromSeed(seed, walletIndex, accountIndex = 0) {
|
||||||
|
@ -122,63 +113,19 @@ const WalletContext = React.createContext(null);
|
||||||
|
|
||||||
export function WalletProvider({ children }) {
|
export function WalletProvider({ children }) {
|
||||||
useListener(walletSeedChanged, 'change');
|
useListener(walletSeedChanged, 'change');
|
||||||
const { mnemonic, seed, importsEncryptionKey } = getUnlockedMnemonicAndSeed();
|
const { mnemonic, seed } = getUnlockedMnemonicAndSeed();
|
||||||
const connection = useConnection();
|
const connection = useConnection();
|
||||||
|
const [walletIndex, setWalletIndex] = useLocalStorageState('walletIndex', 0);
|
||||||
// `privateKeyImports` are accounts imported *in addition* to HD wallets
|
const wallet = useMemo(
|
||||||
const [privateKeyImports, setPrivateKeyImports] = useLocalStorageState(
|
() =>
|
||||||
'walletPrivateKeyImports',
|
seed
|
||||||
{},
|
? new Wallet(connection, Buffer.from(seed, 'hex'), walletIndex)
|
||||||
|
: null,
|
||||||
|
[connection, seed, walletIndex],
|
||||||
);
|
);
|
||||||
// `walletSelector` identifies which wallet to use.
|
|
||||||
const [walletSelector, setWalletSelector] = useLocalStorageState(
|
|
||||||
'walletSelector',
|
|
||||||
DEFAULT_WALLET_SELECTOR,
|
|
||||||
);
|
|
||||||
|
|
||||||
const wallet = useMemo(() => {
|
|
||||||
if (!seed) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const account =
|
|
||||||
walletSelector.walletIndex !== undefined
|
|
||||||
? Wallet.getAccountFromSeed(
|
|
||||||
Buffer.from(seed, 'hex'),
|
|
||||||
walletSelector.walletIndex,
|
|
||||||
)
|
|
||||||
: new Account(
|
|
||||||
(() => {
|
|
||||||
const { nonce, ciphertext } = privateKeyImports[
|
|
||||||
walletSelector.importedPubkey
|
|
||||||
];
|
|
||||||
return nacl.secretbox.open(
|
|
||||||
bs58.decode(ciphertext),
|
|
||||||
bs58.decode(nonce),
|
|
||||||
importsEncryptionKey,
|
|
||||||
);
|
|
||||||
})(),
|
|
||||||
);
|
|
||||||
return new Wallet(connection, account);
|
|
||||||
}, [
|
|
||||||
connection,
|
|
||||||
seed,
|
|
||||||
walletSelector,
|
|
||||||
privateKeyImports,
|
|
||||||
importsEncryptionKey,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WalletContext.Provider
|
<WalletContext.Provider
|
||||||
value={{
|
value={{ wallet, walletIndex, setWalletIndex, seed, mnemonic }}
|
||||||
wallet,
|
|
||||||
seed,
|
|
||||||
mnemonic,
|
|
||||||
importsEncryptionKey,
|
|
||||||
walletSelector,
|
|
||||||
setWalletSelector,
|
|
||||||
privateKeyImports,
|
|
||||||
setPrivateKeyImports,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</WalletContext.Provider>
|
</WalletContext.Provider>
|
||||||
|
@ -297,66 +244,34 @@ export function useBalanceInfo(publicKey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useWalletSelector() {
|
export function useWalletSelector() {
|
||||||
const {
|
const { walletIndex, setWalletIndex, seed } = useContext(WalletContext);
|
||||||
seed,
|
|
||||||
importsEncryptionKey,
|
|
||||||
walletSelector,
|
|
||||||
setWalletSelector,
|
|
||||||
privateKeyImports,
|
|
||||||
setPrivateKeyImports,
|
|
||||||
} = useContext(WalletContext);
|
|
||||||
|
|
||||||
// `walletCount` is the number of HD wallets.
|
|
||||||
const [walletCount, setWalletCount] = useLocalStorageState('walletCount', 1);
|
const [walletCount, setWalletCount] = useLocalStorageState('walletCount', 1);
|
||||||
|
function selectWallet(walletIndex, name) {
|
||||||
function addAccount({ name, importedAccount }) {
|
if (walletIndex >= walletCount) {
|
||||||
if (importedAccount === undefined) {
|
name && localStorage.setItem(`name${walletIndex}`, name);
|
||||||
name && localStorage.setItem(`name${walletCount}`, name);
|
setWalletCount(walletIndex + 1);
|
||||||
setWalletCount(walletCount + 1);
|
|
||||||
} else {
|
|
||||||
const nonce = nacl.randomBytes(nacl.secretbox.nonceLength);
|
|
||||||
const plaintext = importedAccount.secretKey;
|
|
||||||
const ciphertext = nacl.secretbox(plaintext, nonce, importsEncryptionKey);
|
|
||||||
// `useLocalStorageState` requires a new object.
|
|
||||||
let newPrivateKeyImports = { ...privateKeyImports };
|
|
||||||
newPrivateKeyImports[importedAccount.publicKey.toString()] = {
|
|
||||||
name,
|
|
||||||
ciphertext: bs58.encode(ciphertext),
|
|
||||||
nonce: bs58.encode(nonce),
|
|
||||||
};
|
|
||||||
setPrivateKeyImports(newPrivateKeyImports);
|
|
||||||
}
|
}
|
||||||
|
setWalletIndex(walletIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
const accounts = useMemo(() => {
|
const accounts = useMemo(() => {
|
||||||
if (!seed) {
|
if (!seed) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const seedBuffer = Buffer.from(seed, 'hex');
|
const seedBuffer = Buffer.from(seed, 'hex');
|
||||||
const derivedAccounts = [...Array(walletCount).keys()].map((idx) => {
|
return [...Array(walletCount).keys()].map((walletIndex) => {
|
||||||
let address = Wallet.getAccountFromSeed(seedBuffer, idx).publicKey;
|
let address = Wallet.getAccountFromSeed(seedBuffer, walletIndex)
|
||||||
let name = localStorage.getItem(`name${idx}`);
|
.publicKey;
|
||||||
return {
|
let name = localStorage.getItem(`name${walletIndex}`);
|
||||||
selector: { walletIndex: idx, importedPubkey: undefined },
|
return { index: walletIndex, address, name };
|
||||||
isSelected: walletSelector.walletIndex === idx,
|
|
||||||
address,
|
|
||||||
name: idx === 0 ? 'Main account' : name || `Account ${idx}`,
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
}, [seed, walletCount]);
|
||||||
const importedAccounts = Object.keys(privateKeyImports).map((pubkey) => {
|
return { accounts, walletIndex, setWalletIndex: selectWallet };
|
||||||
const { name } = privateKeyImports[pubkey];
|
}
|
||||||
return {
|
|
||||||
selector: { walletIndex: undefined, importedPubkey: pubkey },
|
export async function mnemonicToSecretKey(mnemonic) {
|
||||||
address: new PublicKey(bs58.decode(pubkey)),
|
const { mnemonicToSeed } = await import('bip39');
|
||||||
name: `${name} (imported)`, // TODO: do this in the Component with styling.
|
const rootSeed = Buffer.from(await mnemonicToSeed(mnemonic), 'hex');
|
||||||
isSelected: walletSelector.importedPubkey === pubkey,
|
const derivedSeed = bip32.fromSeed(rootSeed).derivePath("m/501'/0'/0/0")
|
||||||
};
|
.privateKey;
|
||||||
});
|
return nacl.sign.keyPair.fromSeed(derivedSeed).secretKey;
|
||||||
|
|
||||||
return derivedAccounts.concat(importedAccounts);
|
|
||||||
}, [seed, walletCount, walletSelector, privateKeyImports]);
|
|
||||||
|
|
||||||
return { accounts, setWalletSelector, addAccount };
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue