Merge branch 'master' into armani/wormhole
This commit is contained in:
commit
a0a3e377e1
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
Example Solana wallet with support for [SPL tokens](https://spl.solana.com/token) and Serum integration.
|
Example Solana wallet with support for [SPL tokens](https://spl.solana.com/token) and Serum integration.
|
||||||
|
|
||||||
See [sollet.io](https://www.sollet.io) for a demo.
|
See [sollet.io](https://www.sollet.io) or the [Sollet Chrome Extension](https://chrome.google.com/webstore/detail/sollet/fhmfendgdocmcbmfikdcogofphimnkno) for a demo.
|
||||||
|
|
||||||
Wallet keys are stored in `localStorage`, optionally encrypted by a password.
|
Wallet keys are stored in `localStorage`, optionally encrypted by a password.
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const responseHandlers = new Map();
|
const responseHandlers = new Map();
|
||||||
|
let unlockedMnemonic = '';
|
||||||
|
|
||||||
function launchPopup(message, sender, sendResponse) {
|
function launchPopup(message, sender, sendResponse) {
|
||||||
const searchParams = new URLSearchParams();
|
const searchParams = new URLSearchParams();
|
||||||
|
@ -66,5 +67,11 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||||
const responseHandler = responseHandlers.get(message.data.id);
|
const responseHandler = responseHandlers.get(message.data.id);
|
||||||
responseHandlers.delete(message.data.id);
|
responseHandlers.delete(message.data.id);
|
||||||
responseHandler(message.data);
|
responseHandler(message.data);
|
||||||
|
} else if (message.channel === 'sollet_extension_mnemonic_channel') {
|
||||||
|
if (message.method === 'set') {
|
||||||
|
unlockedMnemonic = message.data;
|
||||||
|
} else if (message.method === 'get') {
|
||||||
|
sendResponse(unlockedMnemonic);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -167,7 +167,7 @@ export default function AddTokenDialog({ open, onClose }) {
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
) : tab === 'popular' ? (
|
) : tab === 'popular' ? (
|
||||||
<List disablePadding>
|
<List disablePadding>
|
||||||
{popularTokens.map((tokenInfo) => (
|
{popularTokens.filter(tokenInfo => tokenInfo.address).map((tokenInfo) => (
|
||||||
<TokenListItem
|
<TokenListItem
|
||||||
key={tokenInfo.address}
|
key={tokenInfo.address}
|
||||||
tokenInfo={tokenInfo}
|
tokenInfo={tokenInfo}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import Tooltip from '@material-ui/core/Tooltip';
|
||||||
import EditIcon from '@material-ui/icons/Edit';
|
import EditIcon from '@material-ui/icons/Edit';
|
||||||
import MergeType from '@material-ui/icons/MergeType';
|
import MergeType from '@material-ui/icons/MergeType';
|
||||||
import SortIcon from '@material-ui/icons/Sort';
|
import SortIcon from '@material-ui/icons/Sort';
|
||||||
|
import DeleteIcon from '@material-ui/icons/Delete';
|
||||||
import AddTokenDialog from './AddTokenDialog';
|
import AddTokenDialog from './AddTokenDialog';
|
||||||
import ExportAccountDialog from './ExportAccountDialog';
|
import ExportAccountDialog from './ExportAccountDialog';
|
||||||
import SendDialog from './SendDialog';
|
import SendDialog from './SendDialog';
|
||||||
|
@ -539,7 +540,7 @@ function BalanceListItemDetails({
|
||||||
return <LoadingIndicator delay={0} />;
|
return <LoadingIndicator delay={0} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { mint, tokenName, tokenSymbol, owner } = balanceInfo;
|
let { mint, tokenName, tokenSymbol, owner, amount } = balanceInfo;
|
||||||
|
|
||||||
// Only show the export UI for the native SOL coin.
|
// Only show the export UI for the native SOL coin.
|
||||||
const exportNeedsDisplay =
|
const exportNeedsDisplay =
|
||||||
|
@ -664,6 +665,18 @@ function BalanceListItemDetails({
|
||||||
>
|
>
|
||||||
Send
|
Send
|
||||||
</Button>
|
</Button>
|
||||||
|
{localStorage.getItem('warning-close-account') && mint && amount === 0 ? (
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
color="secondary"
|
||||||
|
size="small"
|
||||||
|
startIcon={<DeleteIcon />}
|
||||||
|
onClick={() => setCloseTokenAccountDialogOpen(true)}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{additionalInfo}
|
{additionalInfo}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import DialogForm from './DialogForm';
|
import DialogForm from './DialogForm';
|
||||||
import { forgetWallet } from '../utils/wallet-seed';
|
import { forgetWallet, normalizeMnemonic, useUnlockedMnemonicAndSeed } from '../utils/wallet-seed';
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||||
import { DialogContentText } from '@material-ui/core';
|
import { DialogContentText } from '@material-ui/core';
|
||||||
import DialogActions from '@material-ui/core/DialogActions';
|
import DialogActions from '@material-ui/core/DialogActions';
|
||||||
|
@ -8,7 +8,8 @@ import TextField from '@material-ui/core/TextField';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
|
|
||||||
export default function DeleteMnemonicDialog({ open, onClose }) {
|
export default function DeleteMnemonicDialog({ open, onClose }) {
|
||||||
const [deleteCheck, setDeleteCheck] = useState('');
|
const [seedCheck, setSeedCheck] = useState('');
|
||||||
|
const [mnemKey] = useUnlockedMnemonicAndSeed();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DialogForm
|
<DialogForm
|
||||||
|
@ -35,16 +36,17 @@ export default function DeleteMnemonicDialog({ open, onClose }) {
|
||||||
<br />
|
<br />
|
||||||
<strong>
|
<strong>
|
||||||
To prevent loss of funds, please ensure you have the seed phrase
|
To prevent loss of funds, please ensure you have the seed phrase
|
||||||
and the private key for all current accounts.
|
and the private key for all current accounts. You can view it by selecting
|
||||||
|
"Export Mnemonic" in the user menu.
|
||||||
</strong>
|
</strong>
|
||||||
</div>
|
</div>
|
||||||
<TextField
|
<TextField
|
||||||
label={`Please type "delete" to confirm`}
|
label={`Please type your seed phrase to confirm`}
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
margin="normal"
|
margin="normal"
|
||||||
value={deleteCheck}
|
value={seedCheck}
|
||||||
onChange={(e) => setDeleteCheck(e.target.value.trim())}
|
onChange={(e) => setSeedCheck(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
|
@ -52,7 +54,7 @@ export default function DeleteMnemonicDialog({ open, onClose }) {
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
disabled={deleteCheck !== 'delete'}
|
disabled={normalizeMnemonic(seedCheck) !== mnemKey.mnemonic}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -8,7 +8,7 @@ import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||||
import Switch from '@material-ui/core/Switch';
|
import Switch from '@material-ui/core/Switch';
|
||||||
import DialogForm from './DialogForm';
|
import DialogForm from './DialogForm';
|
||||||
import { useWallet } from '../utils/wallet';
|
import { useWallet } from '../utils/wallet';
|
||||||
import { getUnlockedMnemonicAndSeed } from '../utils/wallet-seed';
|
import { useUnlockedMnemonicAndSeed } from '../utils/wallet-seed';
|
||||||
|
|
||||||
export default function ExportAccountDialog({ open, onClose }) {
|
export default function ExportAccountDialog({ open, onClose }) {
|
||||||
const wallet = useWallet();
|
const wallet = useWallet();
|
||||||
|
@ -45,7 +45,7 @@ export default function ExportAccountDialog({ open, onClose }) {
|
||||||
|
|
||||||
export function ExportMnemonicDialog({ open, onClose }) {
|
export function ExportMnemonicDialog({ open, onClose }) {
|
||||||
const [isHidden, setIsHidden] = useState(true);
|
const [isHidden, setIsHidden] = useState(true);
|
||||||
const mnemKey = getUnlockedMnemonicAndSeed();
|
const [mnemKey] = useUnlockedMnemonicAndSeed();
|
||||||
return (
|
return (
|
||||||
<DialogForm open={open} onClose={onClose} fullWidth>
|
<DialogForm open={open} onClose={onClose} fullWidth>
|
||||||
<DialogTitle>Export mnemonic</DialogTitle>
|
<DialogTitle>Export mnemonic</DialogTitle>
|
||||||
|
|
|
@ -115,6 +115,7 @@ export default function MergeAccountsDialog({ open, onClose }) {
|
||||||
assocTokAddr,
|
assocTokAddr,
|
||||||
mintGroup,
|
mintGroup,
|
||||||
mint,
|
mint,
|
||||||
|
tokenInfo.decimals,
|
||||||
wallet,
|
wallet,
|
||||||
connection,
|
connection,
|
||||||
enqueueSnackbar,
|
enqueueSnackbar,
|
||||||
|
@ -242,11 +243,11 @@ async function mergeMint(
|
||||||
assocTokAddr,
|
assocTokAddr,
|
||||||
mintAccountSet,
|
mintAccountSet,
|
||||||
mint,
|
mint,
|
||||||
|
decimals,
|
||||||
wallet,
|
wallet,
|
||||||
connection,
|
connection,
|
||||||
enqueueSnackbar,
|
enqueueSnackbar,
|
||||||
) {
|
) {
|
||||||
console.log('mint', mint, mint.toString());
|
|
||||||
if (mintAccountSet.length === 0) {
|
if (mintAccountSet.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -292,6 +293,7 @@ async function mergeMint(
|
||||||
associatedTokenAccount,
|
associatedTokenAccount,
|
||||||
tokenAccount.account.amount,
|
tokenAccount.account.amount,
|
||||||
mint,
|
mint,
|
||||||
|
decimals,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,6 +241,7 @@ function NetworkSelector() {
|
||||||
function WalletSelector() {
|
function WalletSelector() {
|
||||||
const {
|
const {
|
||||||
accounts,
|
accounts,
|
||||||
|
derivedAccounts,
|
||||||
hardwareWalletAccount,
|
hardwareWalletAccount,
|
||||||
setHardwareWalletAccount,
|
setHardwareWalletAccount,
|
||||||
setWalletSelector,
|
setWalletSelector,
|
||||||
|
@ -290,7 +291,7 @@ function WalletSelector() {
|
||||||
onAdd={({ name, importedAccount }) => {
|
onAdd={({ name, importedAccount }) => {
|
||||||
addAccount({ name, importedAccount });
|
addAccount({ name, importedAccount });
|
||||||
setWalletSelector({
|
setWalletSelector({
|
||||||
walletIndex: importedAccount ? undefined : accounts.length,
|
walletIndex: importedAccount ? undefined : derivedAccounts.length,
|
||||||
importedPubkey: importedAccount
|
importedPubkey: importedAccount
|
||||||
? importedAccount.publicKey.toString()
|
? importedAccount.publicKey.toString()
|
||||||
: undefined,
|
: undefined,
|
||||||
|
|
|
@ -81,6 +81,13 @@ export default function SendDialog({ open, onClose, publicKey, balanceInfo }) {
|
||||||
<Tab label="SPL USDT" key="wusdtToSplUsdt" value="wusdtToSplUsdt" />,
|
<Tab label="SPL USDT" key="wusdtToSplUsdt" value="wusdtToSplUsdt" />,
|
||||||
<Tab label="ERC20 USDT" key="swap" value="swap" />,
|
<Tab label="ERC20 USDT" key="swap" value="swap" />,
|
||||||
];
|
];
|
||||||
|
} else if (localStorage.getItem('sollet-private') && mint?.equals(USDC_MINT)) {
|
||||||
|
return [
|
||||||
|
<Tab label="SPL USDC" key="spl" value="spl" />,
|
||||||
|
<Tab label="SPL WUSDC" key="usdcToSplWUsdc" value="usdcToSplWUsdc" />,
|
||||||
|
wormholeTab,
|
||||||
|
<Tab label="ERC20 USDC" key="swap" value="swap" />,
|
||||||
|
];
|
||||||
} else {
|
} else {
|
||||||
return [
|
return [
|
||||||
<Tab label={`SPL ${swapCoinInfo.ticker}`} key="spl" value="spl" />,
|
<Tab label={`SPL ${swapCoinInfo.ticker}`} key="spl" value="spl" />,
|
||||||
|
@ -164,6 +171,16 @@ export default function SendDialog({ open, onClose, publicKey, balanceInfo }) {
|
||||||
swapCoinInfo={swapCoinInfo}
|
swapCoinInfo={swapCoinInfo}
|
||||||
onSubmitRef={onSubmitRef}
|
onSubmitRef={onSubmitRef}
|
||||||
/>
|
/>
|
||||||
|
) : tab === 'usdcToSplWUsdc' ? (
|
||||||
|
<SendSwapDialog
|
||||||
|
key={tab}
|
||||||
|
onClose={onClose}
|
||||||
|
publicKey={publicKey}
|
||||||
|
balanceInfo={balanceInfo}
|
||||||
|
swapCoinInfo={swapCoinInfo}
|
||||||
|
onSubmitRef={onSubmitRef}
|
||||||
|
usdcToSplWUsdc
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<SendSwapDialog
|
<SendSwapDialog
|
||||||
key={tab}
|
key={tab}
|
||||||
|
@ -263,6 +280,7 @@ function SendSplDialog({ onClose, publicKey, balanceInfo, onSubmitRef }) {
|
||||||
new PublicKey(destinationAddress),
|
new PublicKey(destinationAddress),
|
||||||
amount,
|
amount,
|
||||||
balanceInfo.mint,
|
balanceInfo.mint,
|
||||||
|
decimals,
|
||||||
null,
|
null,
|
||||||
overrideDestinationCheck,
|
overrideDestinationCheck,
|
||||||
);
|
);
|
||||||
|
@ -313,6 +331,7 @@ function SendSwapDialog({
|
||||||
ethAccount,
|
ethAccount,
|
||||||
wusdcToSplUsdc = false,
|
wusdcToSplUsdc = false,
|
||||||
wusdtToSplUsdt = false,
|
wusdtToSplUsdt = false,
|
||||||
|
usdcToSplWUsdc = false,
|
||||||
onSubmitRef,
|
onSubmitRef,
|
||||||
}) {
|
}) {
|
||||||
const wallet = useWallet();
|
const wallet = useWallet();
|
||||||
|
@ -328,7 +347,7 @@ function SendSwapDialog({
|
||||||
|
|
||||||
const { tokenName, decimals, mint } = balanceInfo;
|
const { tokenName, decimals, mint } = balanceInfo;
|
||||||
const blockchain =
|
const blockchain =
|
||||||
wusdcToSplUsdc || wusdtToSplUsdt
|
wusdcToSplUsdc || wusdtToSplUsdt || usdcToSplWUsdc
|
||||||
? 'sol'
|
? 'sol'
|
||||||
: swapCoinInfo.blockchain === 'sol'
|
: swapCoinInfo.blockchain === 'sol'
|
||||||
? 'eth'
|
? 'eth'
|
||||||
|
@ -366,11 +385,16 @@ function SendSwapDialog({
|
||||||
let splUsdtWalletAddress = useWalletAddressForMint(
|
let splUsdtWalletAddress = useWalletAddressForMint(
|
||||||
wusdtToSplUsdt ? USDT_MINT : null,
|
wusdtToSplUsdt ? USDT_MINT : null,
|
||||||
);
|
);
|
||||||
|
let splWUsdcWalletAddress = useWalletAddressForMint(
|
||||||
|
usdcToSplWUsdc ? WUSDC_MINT : null,
|
||||||
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (wusdcToSplUsdc && splUsdcWalletAddress) {
|
if (wusdcToSplUsdc && splUsdcWalletAddress) {
|
||||||
setDestinationAddress(splUsdcWalletAddress);
|
setDestinationAddress(splUsdcWalletAddress);
|
||||||
} else if (wusdtToSplUsdt && splUsdtWalletAddress) {
|
} else if (wusdtToSplUsdt && splUsdtWalletAddress) {
|
||||||
setDestinationAddress(splUsdtWalletAddress);
|
setDestinationAddress(splUsdtWalletAddress);
|
||||||
|
} else if (usdcToSplWUsdc && splWUsdcWalletAddress) {
|
||||||
|
setDestinationAddress(splWUsdcWalletAddress);
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
setDestinationAddress,
|
setDestinationAddress,
|
||||||
|
@ -378,6 +402,8 @@ function SendSwapDialog({
|
||||||
splUsdcWalletAddress,
|
splUsdcWalletAddress,
|
||||||
wusdtToSplUsdt,
|
wusdtToSplUsdt,
|
||||||
splUsdtWalletAddress,
|
splUsdtWalletAddress,
|
||||||
|
usdcToSplWUsdc,
|
||||||
|
splWUsdcWalletAddress,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
async function makeTransaction() {
|
async function makeTransaction() {
|
||||||
|
@ -397,6 +423,11 @@ function SendSwapDialog({
|
||||||
}
|
}
|
||||||
if (mint?.equals(WUSDC_MINT)) {
|
if (mint?.equals(WUSDC_MINT)) {
|
||||||
params.wusdcToUsdc = true;
|
params.wusdcToUsdc = true;
|
||||||
|
} else if (mint?.equals(USDC_MINT)) {
|
||||||
|
if (usdcToSplWUsdc) {
|
||||||
|
params.usdcToWUsdc = true;
|
||||||
|
params.coin = WUSDC_MINT.toString();
|
||||||
|
}
|
||||||
} else if (mint?.equals(WUSDT_MINT)) {
|
} else if (mint?.equals(WUSDT_MINT)) {
|
||||||
params.wusdtToUsdt = true;
|
params.wusdtToUsdt = true;
|
||||||
}
|
}
|
||||||
|
@ -409,6 +440,7 @@ function SendSwapDialog({
|
||||||
new PublicKey(swapInfo.address),
|
new PublicKey(swapInfo.address),
|
||||||
amount,
|
amount,
|
||||||
balanceInfo.mint,
|
balanceInfo.mint,
|
||||||
|
decimals,
|
||||||
swapInfo.memo,
|
swapInfo.memo,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
generateMnemonicAndSeed,
|
generateMnemonicAndSeed,
|
||||||
hasLockedMnemonicAndSeed,
|
useHasLockedMnemonicAndSeed,
|
||||||
loadMnemonicAndSeed,
|
loadMnemonicAndSeed,
|
||||||
mnemonicToSeed,
|
mnemonicToSeed,
|
||||||
storeMnemonicAndSeed,
|
storeMnemonicAndSeed,
|
||||||
|
normalizeMnemonic,
|
||||||
} from '../utils/wallet-seed';
|
} from '../utils/wallet-seed';
|
||||||
import {
|
import {
|
||||||
getAccountFromSeed,
|
getAccountFromSeed,
|
||||||
|
@ -15,7 +16,7 @@ import LoadingIndicator from '../components/LoadingIndicator';
|
||||||
import { BalanceListItem } from '../components/BalancesList.js';
|
import { BalanceListItem } from '../components/BalancesList.js';
|
||||||
import Card from '@material-ui/core/Card';
|
import Card from '@material-ui/core/Card';
|
||||||
import CardContent from '@material-ui/core/CardContent';
|
import CardContent from '@material-ui/core/CardContent';
|
||||||
import { Typography } from '@material-ui/core';
|
import { DialogActions, DialogContentText, DialogTitle, Typography } from '@material-ui/core';
|
||||||
import TextField from '@material-ui/core/TextField';
|
import TextField from '@material-ui/core/TextField';
|
||||||
import Checkbox from '@material-ui/core/Checkbox';
|
import Checkbox from '@material-ui/core/Checkbox';
|
||||||
import FormControl from '@material-ui/core/FormControl';
|
import FormControl from '@material-ui/core/FormControl';
|
||||||
|
@ -27,16 +28,23 @@ import MenuItem from '@material-ui/core/MenuItem';
|
||||||
import { useCallAsync } from '../utils/notifications';
|
import { useCallAsync } from '../utils/notifications';
|
||||||
import Link from '@material-ui/core/Link';
|
import Link from '@material-ui/core/Link';
|
||||||
import { validateMnemonic } from 'bip39';
|
import { validateMnemonic } from 'bip39';
|
||||||
|
import DialogForm from '../components/DialogForm';
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
const [restore, setRestore] = useState(false);
|
const [restore, setRestore] = useState(false);
|
||||||
|
const [hasLockedMnemonicAndSeed, loading] = useHasLockedMnemonicAndSeed();
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container maxWidth="sm">
|
<Container maxWidth="sm">
|
||||||
{restore ? (
|
{restore ? (
|
||||||
<RestoreWalletForm goBack={() => setRestore(false)} />
|
<RestoreWalletForm goBack={() => setRestore(false)} />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{hasLockedMnemonicAndSeed() ? <LoginForm /> : <CreateWalletForm />}
|
{hasLockedMnemonicAndSeed ? <LoginForm /> : <CreateWalletForm />}
|
||||||
<br />
|
<br />
|
||||||
<Link style={{ cursor: 'pointer' }} onClick={() => setRestore(true)}>
|
<Link style={{ cursor: 'pointer' }} onClick={() => setRestore(true)}>
|
||||||
Restore existing wallet
|
Restore existing wallet
|
||||||
|
@ -91,8 +99,11 @@ function CreateWalletForm() {
|
||||||
|
|
||||||
function SeedWordsForm({ mnemonicAndSeed, goForward }) {
|
function SeedWordsForm({ mnemonicAndSeed, goForward }) {
|
||||||
const [confirmed, setConfirmed] = useState(false);
|
const [confirmed, setConfirmed] = useState(false);
|
||||||
|
const [showDialog, setShowDialog] = useState(false);
|
||||||
|
const [seedCheck, setSeedCheck] = useState('');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography variant="h5" gutterBottom>
|
<Typography variant="h5" gutterBottom>
|
||||||
|
@ -140,11 +151,48 @@ function SeedWordsForm({ mnemonicAndSeed, goForward }) {
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardActions style={{ justifyContent: 'flex-end' }}>
|
<CardActions style={{ justifyContent: 'flex-end' }}>
|
||||||
<Button color="primary" disabled={!confirmed} onClick={goForward}>
|
<Button color="primary" disabled={!confirmed} onClick={() => setShowDialog(true)}>
|
||||||
Continue
|
Continue
|
||||||
</Button>
|
</Button>
|
||||||
</CardActions>
|
</CardActions>
|
||||||
</Card>
|
</Card>
|
||||||
|
<DialogForm
|
||||||
|
open={showDialog}
|
||||||
|
onClose={() => setShowDialog(false)}
|
||||||
|
onSubmit={goForward}
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
<DialogTitle>{'Confirm Mnemonic'}</DialogTitle>
|
||||||
|
<DialogContentText style={{ margin: 20 }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Please re-enter your seed phrase to confirm that you have saved it.
|
||||||
|
</div>
|
||||||
|
<TextField
|
||||||
|
label={`Please type your seed phrase to confirm`}
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
margin="normal"
|
||||||
|
value={seedCheck}
|
||||||
|
onChange={(e) => setSeedCheck(e.target.value)}
|
||||||
|
/>
|
||||||
|
</DialogContentText>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => setShowDialog(false)}>Close</Button>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
color="secondary"
|
||||||
|
disabled={normalizeMnemonic(seedCheck) !== mnemonicAndSeed?.mnemonic}
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</DialogForm>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,12 +253,21 @@ function LoginForm() {
|
||||||
const [stayLoggedIn, setStayLoggedIn] = useState(false);
|
const [stayLoggedIn, setStayLoggedIn] = useState(false);
|
||||||
const callAsync = useCallAsync();
|
const callAsync = useCallAsync();
|
||||||
|
|
||||||
function submit() {
|
const submit = () => {
|
||||||
callAsync(loadMnemonicAndSeed(password, stayLoggedIn), {
|
callAsync(loadMnemonicAndSeed(password, stayLoggedIn), {
|
||||||
progressMessage: 'Unlocking wallet...',
|
progressMessage: 'Unlocking wallet...',
|
||||||
successMessage: 'Wallet unlocked',
|
successMessage: 'Wallet unlocked',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const submitOnEnter = (e) => {
|
||||||
|
if (e.code === "Enter" || e.code === "NumpadEnter") {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
submit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const setPasswordOnChange = (e) => setPassword(e.target.value);
|
||||||
|
const toggleStayLoggedIn = (e) => setStayLoggedIn(e.target.checked);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
|
@ -226,13 +283,14 @@ function LoginForm() {
|
||||||
type="password"
|
type="password"
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={setPasswordOnChange}
|
||||||
|
onKeyDown={submitOnEnter}
|
||||||
/>
|
/>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={stayLoggedIn}
|
checked={stayLoggedIn}
|
||||||
onChange={(e) => setStayLoggedIn(e.target.checked)}
|
onChange={toggleStayLoggedIn}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label="Keep wallet unlocked"
|
label="Keep wallet unlocked"
|
||||||
|
@ -248,11 +306,13 @@ function LoginForm() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function RestoreWalletForm({ goBack }) {
|
function RestoreWalletForm({ goBack }) {
|
||||||
const [mnemonic, setMnemonic] = useState('');
|
const [rawMnemonic, setRawMnemonic] = useState('');
|
||||||
const [seed, setSeed] = useState('');
|
const [seed, setSeed] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [passwordConfirm, setPasswordConfirm] = useState('');
|
const [passwordConfirm, setPasswordConfirm] = useState('');
|
||||||
const [next, setNext] = useState(false);
|
const [next, setNext] = useState(false);
|
||||||
|
|
||||||
|
const mnemonic = normalizeMnemonic(rawMnemonic);
|
||||||
const isNextBtnEnabled =
|
const isNextBtnEnabled =
|
||||||
password === passwordConfirm && validateMnemonic(mnemonic);
|
password === passwordConfirm && validateMnemonic(mnemonic);
|
||||||
|
|
||||||
|
@ -287,8 +347,8 @@ function RestoreWalletForm({ goBack }) {
|
||||||
rows={3}
|
rows={3}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
label="Seed Words"
|
label="Seed Words"
|
||||||
value={mnemonic}
|
value={rawMnemonic}
|
||||||
onChange={(e) => setMnemonic(e.target.value)}
|
onChange={(e) => setRawMnemonic(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
|
|
@ -22,6 +22,7 @@ export async function swapApiRequest(
|
||||||
headers['Content-Type'] = 'application/json';
|
headers['Content-Type'] = 'application/json';
|
||||||
params.body = JSON.stringify(body);
|
params.body = JSON.stringify(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
let resp = await fetch(`https://swap.sollet.io/api/${path}`, params);
|
let resp = await fetch(`https://swap.sollet.io/api/${path}`, params);
|
||||||
return await handleSwapApiResponse(resp, ignoreUserErrors);
|
return await handleSwapApiResponse(resp, ignoreUserErrors);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
memoInstruction,
|
memoInstruction,
|
||||||
mintTo,
|
mintTo,
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
transfer,
|
transferChecked,
|
||||||
} from './instructions';
|
} from './instructions';
|
||||||
import {
|
import {
|
||||||
ACCOUNT_LAYOUT,
|
ACCOUNT_LAYOUT,
|
||||||
|
@ -300,6 +300,7 @@ export async function transferTokens({
|
||||||
amount,
|
amount,
|
||||||
memo,
|
memo,
|
||||||
mint,
|
mint,
|
||||||
|
decimals,
|
||||||
overrideDestinationCheck,
|
overrideDestinationCheck,
|
||||||
}) {
|
}) {
|
||||||
const destinationAccountInfo = await connection.getAccountInfo(
|
const destinationAccountInfo = await connection.getAccountInfo(
|
||||||
|
@ -312,6 +313,8 @@ export async function transferTokens({
|
||||||
return await transferBetweenSplTokenAccounts({
|
return await transferBetweenSplTokenAccounts({
|
||||||
connection,
|
connection,
|
||||||
owner,
|
owner,
|
||||||
|
mint,
|
||||||
|
decimals,
|
||||||
sourcePublicKey,
|
sourcePublicKey,
|
||||||
destinationPublicKey,
|
destinationPublicKey,
|
||||||
amount,
|
amount,
|
||||||
|
@ -339,6 +342,8 @@ export async function transferTokens({
|
||||||
return await transferBetweenSplTokenAccounts({
|
return await transferBetweenSplTokenAccounts({
|
||||||
connection,
|
connection,
|
||||||
owner,
|
owner,
|
||||||
|
mint,
|
||||||
|
decimals,
|
||||||
sourcePublicKey,
|
sourcePublicKey,
|
||||||
destinationPublicKey: destinationSplTokenAccount.publicKey,
|
destinationPublicKey: destinationSplTokenAccount.publicKey,
|
||||||
amount,
|
amount,
|
||||||
|
@ -353,44 +358,24 @@ export async function transferTokens({
|
||||||
amount,
|
amount,
|
||||||
memo,
|
memo,
|
||||||
mint,
|
mint,
|
||||||
|
decimals,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPL tokens only.
|
|
||||||
export async function transferAndClose({
|
|
||||||
connection,
|
|
||||||
owner,
|
|
||||||
sourcePublicKey,
|
|
||||||
destinationPublicKey,
|
|
||||||
amount,
|
|
||||||
}) {
|
|
||||||
const tx = createTransferBetweenSplTokenAccountsInstruction({
|
|
||||||
ownerPublicKey: owner.publicKey,
|
|
||||||
sourcePublicKey,
|
|
||||||
destinationPublicKey,
|
|
||||||
amount,
|
|
||||||
});
|
|
||||||
tx.add(
|
|
||||||
closeAccount({
|
|
||||||
source: sourcePublicKey,
|
|
||||||
destination: owner.publicKey,
|
|
||||||
owner: owner.publicKey,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
let signers = [];
|
|
||||||
return await signAndSendTransaction(connection, tx, owner, signers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createTransferBetweenSplTokenAccountsInstruction({
|
function createTransferBetweenSplTokenAccountsInstruction({
|
||||||
ownerPublicKey,
|
ownerPublicKey,
|
||||||
|
mint,
|
||||||
|
decimals,
|
||||||
sourcePublicKey,
|
sourcePublicKey,
|
||||||
destinationPublicKey,
|
destinationPublicKey,
|
||||||
amount,
|
amount,
|
||||||
memo,
|
memo,
|
||||||
}) {
|
}) {
|
||||||
let transaction = new Transaction().add(
|
let transaction = new Transaction().add(
|
||||||
transfer({
|
transferChecked({
|
||||||
source: sourcePublicKey,
|
source: sourcePublicKey,
|
||||||
|
mint,
|
||||||
|
decimals,
|
||||||
destination: destinationPublicKey,
|
destination: destinationPublicKey,
|
||||||
owner: ownerPublicKey,
|
owner: ownerPublicKey,
|
||||||
amount,
|
amount,
|
||||||
|
@ -405,6 +390,8 @@ function createTransferBetweenSplTokenAccountsInstruction({
|
||||||
async function transferBetweenSplTokenAccounts({
|
async function transferBetweenSplTokenAccounts({
|
||||||
connection,
|
connection,
|
||||||
owner,
|
owner,
|
||||||
|
mint,
|
||||||
|
decimals,
|
||||||
sourcePublicKey,
|
sourcePublicKey,
|
||||||
destinationPublicKey,
|
destinationPublicKey,
|
||||||
amount,
|
amount,
|
||||||
|
@ -412,6 +399,8 @@ async function transferBetweenSplTokenAccounts({
|
||||||
}) {
|
}) {
|
||||||
const transaction = createTransferBetweenSplTokenAccountsInstruction({
|
const transaction = createTransferBetweenSplTokenAccountsInstruction({
|
||||||
ownerPublicKey: owner.publicKey,
|
ownerPublicKey: owner.publicKey,
|
||||||
|
mint,
|
||||||
|
decimals,
|
||||||
sourcePublicKey,
|
sourcePublicKey,
|
||||||
destinationPublicKey,
|
destinationPublicKey,
|
||||||
amount,
|
amount,
|
||||||
|
@ -429,6 +418,7 @@ async function createAndTransferToAccount({
|
||||||
amount,
|
amount,
|
||||||
memo,
|
memo,
|
||||||
mint,
|
mint,
|
||||||
|
decimals,
|
||||||
}) {
|
}) {
|
||||||
const [
|
const [
|
||||||
createAccountInstruction,
|
createAccountInstruction,
|
||||||
|
@ -449,6 +439,8 @@ async function createAndTransferToAccount({
|
||||||
const transferBetweenAccountsTxn = createTransferBetweenSplTokenAccountsInstruction(
|
const transferBetweenAccountsTxn = createTransferBetweenSplTokenAccountsInstruction(
|
||||||
{
|
{
|
||||||
ownerPublicKey: owner.publicKey,
|
ownerPublicKey: owner.publicKey,
|
||||||
|
mint,
|
||||||
|
decimals,
|
||||||
sourcePublicKey,
|
sourcePublicKey,
|
||||||
destinationPublicKey: newAddress,
|
destinationPublicKey: newAddress,
|
||||||
amount,
|
amount,
|
||||||
|
|
|
@ -29,11 +29,6 @@ LAYOUT.addVariant(
|
||||||
'initializeMint',
|
'initializeMint',
|
||||||
);
|
);
|
||||||
LAYOUT.addVariant(1, BufferLayout.struct([]), 'initializeAccount');
|
LAYOUT.addVariant(1, BufferLayout.struct([]), 'initializeAccount');
|
||||||
LAYOUT.addVariant(
|
|
||||||
3,
|
|
||||||
BufferLayout.struct([BufferLayout.nu64('amount')]),
|
|
||||||
'transfer',
|
|
||||||
);
|
|
||||||
LAYOUT.addVariant(
|
LAYOUT.addVariant(
|
||||||
7,
|
7,
|
||||||
BufferLayout.struct([BufferLayout.nu64('amount')]),
|
BufferLayout.struct([BufferLayout.nu64('amount')]),
|
||||||
|
@ -45,6 +40,11 @@ LAYOUT.addVariant(
|
||||||
'burn',
|
'burn',
|
||||||
);
|
);
|
||||||
LAYOUT.addVariant(9, BufferLayout.struct([]), 'closeAccount');
|
LAYOUT.addVariant(9, BufferLayout.struct([]), 'closeAccount');
|
||||||
|
LAYOUT.addVariant(
|
||||||
|
12,
|
||||||
|
BufferLayout.struct([BufferLayout.nu64('amount'), BufferLayout.u8('decimals')]),
|
||||||
|
'transferChecked',
|
||||||
|
);
|
||||||
|
|
||||||
const instructionMaxSpan = Math.max(
|
const instructionMaxSpan = Math.max(
|
||||||
...Object.values(LAYOUT.registry).map((r) => r.span),
|
...Object.values(LAYOUT.registry).map((r) => r.span),
|
||||||
|
@ -96,16 +96,17 @@ export function initializeAccount({ account, mint, owner }) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transfer({ source, destination, amount, owner }) {
|
export function transferChecked({ source, mint, destination, amount, decimals, owner }) {
|
||||||
let keys = [
|
let keys = [
|
||||||
{ pubkey: source, isSigner: false, isWritable: true },
|
{ pubkey: source, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: mint, isSigner: false, isWritable: false },
|
||||||
{ pubkey: destination, isSigner: false, isWritable: true },
|
{ pubkey: destination, isSigner: false, isWritable: true },
|
||||||
{ pubkey: owner, isSigner: true, isWritable: false },
|
{ pubkey: owner, isSigner: true, isWritable: false },
|
||||||
];
|
];
|
||||||
return new TransactionInstruction({
|
return new TransactionInstruction({
|
||||||
keys,
|
keys,
|
||||||
data: encodeTokenInstructionData({
|
data: encodeTokenInstructionData({
|
||||||
transfer: { amount },
|
transferChecked: { amount, decimals },
|
||||||
}),
|
}),
|
||||||
programId: TOKEN_PROGRAM_ID,
|
programId: TOKEN_PROGRAM_ID,
|
||||||
});
|
});
|
||||||
|
|
|
@ -239,6 +239,27 @@ const POPULAR_TOKENS = {
|
||||||
icon:
|
icon:
|
||||||
'https://raw.githubusercontent.com/nathanielparke/awesome-serum-markets/master/icons/oxy.svg',
|
'https://raw.githubusercontent.com/nathanielparke/awesome-serum-markets/master/icons/oxy.svg',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
tokenSymbol: 'COPE',
|
||||||
|
mintAddress: '3K6rftdAaQYMPunrtNRHgnK2UAtjm2JwyT2oCiTDouYE',
|
||||||
|
tokenName: 'COPE',
|
||||||
|
icon:
|
||||||
|
'https://cdn.jsdelivr.net/gh/solana-labs/token-list/assets/mainnet/3K6rftdAaQYMPunrtNRHgnK2UAtjm2JwyT2oCiTDouYE/logo.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tokenSymbol: 'BRZ',
|
||||||
|
mintAddress: 'FtgGSFADXBtroxq8VCausXRr2of47QBf5AS1NtZCu4GD',
|
||||||
|
tokenName: 'Brazilian Digital Token',
|
||||||
|
icon:
|
||||||
|
'https://cdn.jsdelivr.net/gh/solana-labs/explorer/public/tokens/brz.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tokenSymbol: 'STEP',
|
||||||
|
mintAddress: 'StepAscQoEioFxxWGnh2sLBDFp9d8rvKz2Yp39iDpyT',
|
||||||
|
tokenName: 'Step',
|
||||||
|
icon:
|
||||||
|
'https://cdn.jsdelivr.net/gh/solana-labs/token-list/assets/mainnet/StepAscQoEioFxxWGnh2sLBDFp9d8rvKz2Yp39iDpyT/logo.png',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,11 @@ import * as bip32 from 'bip32';
|
||||||
import bs58 from 'bs58';
|
import bs58 from 'bs58';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { isExtension } from './utils';
|
import { isExtension } from './utils';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export function normalizeMnemonic(mnemonic) {
|
||||||
|
return mnemonic.trim().split(/\s+/g).join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
export async function generateMnemonicAndSeed() {
|
export async function generateMnemonicAndSeed() {
|
||||||
const bip39 = await import('bip39');
|
const bip39 = await import('bip39');
|
||||||
|
@ -21,38 +26,75 @@ export async function mnemonicToSeed(mnemonic) {
|
||||||
return Buffer.from(seed).toString('hex');
|
return Buffer.from(seed).toString('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
let unlockedMnemonicAndSeed = (() => {
|
async function getExtensionUnlockedMnemonic() {
|
||||||
const unlockedExpiration = localStorage.getItem('unlockedExpiration');
|
if (!isExtension) {
|
||||||
if (unlockedExpiration && Number(unlockedExpiration) < Date.now()) {
|
return null;
|
||||||
localStorage.removeItem('unlocked');
|
|
||||||
localStorage.removeItem('unlockedExpiration');
|
|
||||||
}
|
}
|
||||||
const stored = JSON.parse(
|
|
||||||
sessionStorage.getItem('unlocked') ||
|
return new Promise((resolve) => {
|
||||||
localStorage.getItem('unlocked') ||
|
chrome.runtime.sendMessage({
|
||||||
'null',
|
channel: 'sollet_extension_mnemonic_channel',
|
||||||
);
|
method: 'get',
|
||||||
if (stored === null) {
|
}, resolve);
|
||||||
return {
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const EMPTY_MNEMONIC = {
|
||||||
mnemonic: null,
|
mnemonic: null,
|
||||||
seed: null,
|
seed: null,
|
||||||
importsEncryptionKey: null,
|
importsEncryptionKey: null,
|
||||||
derivationPath: null,
|
derivationPath: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let unlockedMnemonicAndSeed = (async () => {
|
||||||
|
const unlockedExpiration = localStorage.getItem('unlockedExpiration');
|
||||||
|
// Left here to clean up stored mnemonics from previous method
|
||||||
|
if (unlockedExpiration && Number(unlockedExpiration) < Date.now()) {
|
||||||
|
localStorage.removeItem('unlocked');
|
||||||
|
localStorage.removeItem('unlockedExpiration');
|
||||||
|
}
|
||||||
|
const stored = JSON.parse(
|
||||||
|
(await getExtensionUnlockedMnemonic()) ||
|
||||||
|
sessionStorage.getItem('unlocked') ||
|
||||||
|
localStorage.getItem('unlocked') ||
|
||||||
|
'null',
|
||||||
|
);
|
||||||
|
if (stored === null) {
|
||||||
|
return EMPTY_MNEMONIC;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
importsEncryptionKey: deriveImportsEncryptionKey(stored.seed),
|
importsEncryptionKey: deriveImportsEncryptionKey(stored.seed),
|
||||||
...stored,
|
...stored,
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
export const walletSeedChanged = new EventEmitter();
|
export const walletSeedChanged = new EventEmitter();
|
||||||
|
|
||||||
export function getUnlockedMnemonicAndSeed() {
|
export function getUnlockedMnemonicAndSeed() {
|
||||||
return unlockedMnemonicAndSeed;
|
return unlockedMnemonicAndSeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hasLockedMnemonicAndSeed() {
|
// returns [mnemonic, loading]
|
||||||
return !!localStorage.getItem('locked');
|
export function useUnlockedMnemonicAndSeed() {
|
||||||
|
const [currentUnlockedMnemonic, setCurrentUnlockedMnemonic] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
walletSeedChanged.addListener('change', setCurrentUnlockedMnemonic);
|
||||||
|
unlockedMnemonicAndSeed.then(setCurrentUnlockedMnemonic);
|
||||||
|
return () => {
|
||||||
|
walletSeedChanged.removeListener('change', setCurrentUnlockedMnemonic);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return !currentUnlockedMnemonic
|
||||||
|
? [EMPTY_MNEMONIC, true]
|
||||||
|
: [currentUnlockedMnemonic, false];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useHasLockedMnemonicAndSeed() {
|
||||||
|
const [unlockedMnemonic, loading] = useUnlockedMnemonicAndSeed();
|
||||||
|
|
||||||
|
return [!unlockedMnemonic.seed && !!localStorage.getItem('locked'), loading];
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUnlockedMnemonicAndSeed(
|
function setUnlockedMnemonicAndSeed(
|
||||||
|
@ -61,13 +103,14 @@ function setUnlockedMnemonicAndSeed(
|
||||||
importsEncryptionKey,
|
importsEncryptionKey,
|
||||||
derivationPath,
|
derivationPath,
|
||||||
) {
|
) {
|
||||||
unlockedMnemonicAndSeed = {
|
const data = {
|
||||||
mnemonic,
|
mnemonic,
|
||||||
seed,
|
seed,
|
||||||
importsEncryptionKey,
|
importsEncryptionKey,
|
||||||
derivationPath,
|
derivationPath,
|
||||||
};
|
};
|
||||||
walletSeedChanged.emit('change', unlockedMnemonicAndSeed);
|
unlockedMnemonicAndSeed = Promise.resolve(data);
|
||||||
|
walletSeedChanged.emit('change', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function storeMnemonicAndSeed(
|
export async function storeMnemonicAndSeed(
|
||||||
|
@ -97,11 +140,17 @@ export async function storeMnemonicAndSeed(
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
localStorage.removeItem('unlocked');
|
localStorage.removeItem('unlocked');
|
||||||
sessionStorage.removeItem('unlocked');
|
|
||||||
} else {
|
} else {
|
||||||
localStorage.setItem('unlocked', plaintext);
|
localStorage.setItem('unlocked', plaintext);
|
||||||
localStorage.removeItem('locked');
|
localStorage.removeItem('locked');
|
||||||
|
}
|
||||||
sessionStorage.removeItem('unlocked');
|
sessionStorage.removeItem('unlocked');
|
||||||
|
if (isExtension) {
|
||||||
|
chrome.runtime.sendMessage({
|
||||||
|
channel: 'sollet_extension_mnemonic_channel',
|
||||||
|
method: 'set',
|
||||||
|
data: '',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const importsEncryptionKey = deriveImportsEncryptionKey(seed);
|
const importsEncryptionKey = deriveImportsEncryptionKey(seed);
|
||||||
setUnlockedMnemonicAndSeed(
|
setUnlockedMnemonicAndSeed(
|
||||||
|
@ -132,12 +181,15 @@ export async function loadMnemonicAndSeed(password, stayLoggedIn) {
|
||||||
const { mnemonic, seed, derivationPath } = JSON.parse(decodedPlaintext);
|
const { mnemonic, seed, derivationPath } = JSON.parse(decodedPlaintext);
|
||||||
if (stayLoggedIn) {
|
if (stayLoggedIn) {
|
||||||
if (isExtension) {
|
if (isExtension) {
|
||||||
const expireMs = 1000 * 60 * 60 * 24;
|
chrome.runtime.sendMessage({
|
||||||
localStorage.setItem('unlockedExpiration', Date.now() + expireMs);
|
channel: 'sollet_extension_mnemonic_channel',
|
||||||
localStorage.setItem('unlocked', decodedPlaintext);
|
method: 'set',
|
||||||
}
|
data: decodedPlaintext,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
sessionStorage.setItem('unlocked', decodedPlaintext);
|
sessionStorage.setItem('unlocked', decodedPlaintext);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const importsEncryptionKey = deriveImportsEncryptionKey(seed);
|
const importsEncryptionKey = deriveImportsEncryptionKey(seed);
|
||||||
setUnlockedMnemonicAndSeed(
|
setUnlockedMnemonicAndSeed(
|
||||||
mnemonic,
|
mnemonic,
|
||||||
|
@ -175,6 +227,13 @@ function deriveImportsEncryptionKey(seed) {
|
||||||
export function forgetWallet() {
|
export function forgetWallet() {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
sessionStorage.removeItem('unlocked');
|
sessionStorage.removeItem('unlocked');
|
||||||
|
if (isExtension) {
|
||||||
|
chrome.runtime.sendMessage({
|
||||||
|
channel: 'sollet_extension_mnemonic_channel',
|
||||||
|
method: 'set',
|
||||||
|
data: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
unlockedMnemonicAndSeed = {
|
unlockedMnemonicAndSeed = {
|
||||||
mnemonic: null,
|
mnemonic: null,
|
||||||
seed: null,
|
seed: null,
|
||||||
|
|
|
@ -14,7 +14,6 @@ import {
|
||||||
getOwnedTokenAccounts,
|
getOwnedTokenAccounts,
|
||||||
nativeTransfer,
|
nativeTransfer,
|
||||||
transferTokens,
|
transferTokens,
|
||||||
transferAndClose,
|
|
||||||
} from './tokens';
|
} from './tokens';
|
||||||
import { TOKEN_PROGRAM_ID } from './tokens/instructions';
|
import { TOKEN_PROGRAM_ID } from './tokens/instructions';
|
||||||
import {
|
import {
|
||||||
|
@ -25,7 +24,7 @@ import {
|
||||||
import { useListener, useLocalStorageState, useRefEqual } from './utils';
|
import { useListener, useLocalStorageState, useRefEqual } from './utils';
|
||||||
import { useTokenInfo } from './tokens/names';
|
import { useTokenInfo } from './tokens/names';
|
||||||
import { refreshCache, useAsyncData } from './fetch-loop';
|
import { refreshCache, useAsyncData } from './fetch-loop';
|
||||||
import { getUnlockedMnemonicAndSeed, walletSeedChanged } from './wallet-seed';
|
import { useUnlockedMnemonicAndSeed, walletSeedChanged } from './wallet-seed';
|
||||||
import { WalletProviderFactory } from './walletProvider/factory';
|
import { WalletProviderFactory } from './walletProvider/factory';
|
||||||
import { getAccountFromSeed } from './walletProvider/localStorage';
|
import { getAccountFromSeed } from './walletProvider/localStorage';
|
||||||
import { useSnackbar } from 'notistack';
|
import { useSnackbar } from 'notistack';
|
||||||
|
@ -99,6 +98,7 @@ export class Wallet {
|
||||||
destination,
|
destination,
|
||||||
amount,
|
amount,
|
||||||
mint,
|
mint,
|
||||||
|
decimals,
|
||||||
memo = null,
|
memo = null,
|
||||||
overrideDestinationCheck = false,
|
overrideDestinationCheck = false,
|
||||||
) => {
|
) => {
|
||||||
|
@ -116,6 +116,7 @@ export class Wallet {
|
||||||
amount,
|
amount,
|
||||||
memo,
|
memo,
|
||||||
mint,
|
mint,
|
||||||
|
decimals,
|
||||||
overrideDestinationCheck,
|
overrideDestinationCheck,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -133,16 +134,6 @@ export class Wallet {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
transferAndClose = async (source, destination, amount) => {
|
|
||||||
return await transferAndClose({
|
|
||||||
connection: this.connection,
|
|
||||||
owner: this,
|
|
||||||
sourcePublicKey: source,
|
|
||||||
destinationPublicKey: destination,
|
|
||||||
amount,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
signTransaction = async (transaction) => {
|
signTransaction = async (transaction) => {
|
||||||
return this.provider.signTransaction(transaction);
|
return this.provider.signTransaction(transaction);
|
||||||
};
|
};
|
||||||
|
@ -156,12 +147,12 @@ const WalletContext = React.createContext(null);
|
||||||
|
|
||||||
export function WalletProvider({ children }) {
|
export function WalletProvider({ children }) {
|
||||||
useListener(walletSeedChanged, 'change');
|
useListener(walletSeedChanged, 'change');
|
||||||
const {
|
const [{
|
||||||
mnemonic,
|
mnemonic,
|
||||||
seed,
|
seed,
|
||||||
importsEncryptionKey,
|
importsEncryptionKey,
|
||||||
derivationPath,
|
derivationPath,
|
||||||
} = getUnlockedMnemonicAndSeed();
|
}] = useUnlockedMnemonicAndSeed();
|
||||||
const { enqueueSnackbar } = useSnackbar();
|
const { enqueueSnackbar } = useSnackbar();
|
||||||
const connection = useConnection();
|
const connection = useConnection();
|
||||||
const [wallet, setWallet] = useState();
|
const [wallet, setWallet] = useState();
|
||||||
|
@ -289,9 +280,9 @@ export function WalletProvider({ children }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const accounts = useMemo(() => {
|
const [accounts, derivedAccounts] = useMemo(() => {
|
||||||
if (!seed) {
|
if (!seed) {
|
||||||
return [];
|
return [[], []];
|
||||||
}
|
}
|
||||||
|
|
||||||
const seedBuffer = Buffer.from(seed, 'hex');
|
const seedBuffer = Buffer.from(seed, 'hex');
|
||||||
|
@ -325,7 +316,8 @@ export function WalletProvider({ children }) {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return derivedAccounts.concat(importedAccounts);
|
const accounts = derivedAccounts.concat(importedAccounts);
|
||||||
|
return [accounts, derivedAccounts];
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [seed, walletCount, walletSelector, privateKeyImports, walletNames]);
|
}, [seed, walletCount, walletSelector, privateKeyImports, walletNames]);
|
||||||
|
|
||||||
|
@ -358,6 +350,7 @@ export function WalletProvider({ children }) {
|
||||||
privateKeyImports,
|
privateKeyImports,
|
||||||
setPrivateKeyImports,
|
setPrivateKeyImports,
|
||||||
accounts,
|
accounts,
|
||||||
|
derivedAccounts,
|
||||||
addAccount,
|
addAccount,
|
||||||
setAccountName,
|
setAccountName,
|
||||||
derivationPath,
|
derivationPath,
|
||||||
|
@ -475,6 +468,7 @@ export function useBalanceInfo(publicKey) {
|
||||||
export function useWalletSelector() {
|
export function useWalletSelector() {
|
||||||
const {
|
const {
|
||||||
accounts,
|
accounts,
|
||||||
|
derivedAccounts,
|
||||||
addAccount,
|
addAccount,
|
||||||
setWalletSelector,
|
setWalletSelector,
|
||||||
setAccountName,
|
setAccountName,
|
||||||
|
@ -484,6 +478,7 @@ export function useWalletSelector() {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accounts,
|
accounts,
|
||||||
|
derivedAccounts,
|
||||||
setWalletSelector,
|
setWalletSelector,
|
||||||
addAccount,
|
addAccount,
|
||||||
setAccountName,
|
setAccountName,
|
||||||
|
|
|
@ -40,8 +40,11 @@ function deriveSeed(seed, walletIndex, derivationPath, accountIndex) {
|
||||||
|
|
||||||
export class LocalStorageWalletProvider {
|
export class LocalStorageWalletProvider {
|
||||||
constructor(args) {
|
constructor(args) {
|
||||||
const { seed } = getUnlockedMnemonicAndSeed();
|
|
||||||
this.account = args.account;
|
this.account = args.account;
|
||||||
|
}
|
||||||
|
|
||||||
|
init = async () => {
|
||||||
|
const { seed } = await getUnlockedMnemonicAndSeed();
|
||||||
this.listAddresses = async (walletCount) => {
|
this.listAddresses = async (walletCount) => {
|
||||||
const seedBuffer = Buffer.from(seed, 'hex');
|
const seedBuffer = Buffer.from(seed, 'hex');
|
||||||
return [...Array(walletCount).keys()].map((walletIndex) => {
|
return [...Array(walletCount).keys()].map((walletIndex) => {
|
||||||
|
@ -50,9 +53,6 @@ export class LocalStorageWalletProvider {
|
||||||
return { index: walletIndex, address, name };
|
return { index: walletIndex, address, name };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
init = async () => {
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue