- {isAssociatedToken && (
+ const subtitle =
+ isExtensionWidth || !publicKey.equals(balanceInfo.owner) ? undefined : (
+
-
+ {publicKey.toBase58()}
- )}
-
- {publicKey.toBase58()}
-
- );
+ );
const usdValue =
price === undefined // Not yet loaded.
@@ -450,7 +438,12 @@ export function BalanceListItem({ publicKey, expandable, setUsdValue }) {
<>
expandable && setOpen((open) => !open)}>
-
+
{expandable ? open ? : : <>>}
-
-
-
+ {expandable && (
+
+
+
+ )}
>
);
}
-function BalanceListItemDetails({ publicKey, serumMarkets, balanceInfo }) {
+function BalanceListItemDetails({
+ publicKey,
+ serumMarkets,
+ balanceInfo,
+ isAssociatedToken,
+}) {
const urlSuffix = useSolanaExplorerUrlSuffix();
const classes = useStyles();
const [sendDialogOpen, setSendDialogOpen] = useState(false);
@@ -529,7 +530,7 @@ function BalanceListItemDetails({ publicKey, serumMarkets, balanceInfo }) {
return ;
}
- let { mint, tokenName, tokenSymbol, owner, amount } = balanceInfo;
+ let { mint, tokenName, tokenSymbol, owner } = balanceInfo;
// Only show the export UI for the native SOL coin.
const exportNeedsDisplay =
@@ -540,12 +541,9 @@ function BalanceListItemDetails({ publicKey, serumMarkets, balanceInfo }) {
? serumMarkets[tokenSymbol.toUpperCase()].publicKey
: undefined
: undefined;
-
+ const isSolAddress = publicKey.equals(owner);
const additionalInfo = isExtensionWidth ? undefined : (
<>
-
- Deposit Address: {publicKey.toBase58()}
-
Token Name: {tokenName ?? 'Unknown'}
@@ -557,6 +555,17 @@ function BalanceListItemDetails({ publicKey, serumMarkets, balanceInfo }) {
Token Address: {mint.toBase58()}
) : null}
+ {!isSolAddress && (
+
+ {isAssociatedToken ? 'Associated' : ''} Token Metadata:{' '}
+ {publicKey.toBase58()}
+
+ )}
+ {!isSolAddress && isAssociatedToken === false && (
+
+ This is an auxiliary token account.
+
+ )}
@@ -646,17 +655,6 @@ function BalanceListItemDetails({ publicKey, serumMarkets, balanceInfo }) {
>
Send
- {mint && amount === 0 ? (
- }
- onClick={() => setCloseTokenAccountDialogOpen(true)}
- >
- Delete
-
- ) : null}
{additionalInfo}
@@ -672,6 +670,7 @@ function BalanceListItemDetails({ publicKey, serumMarkets, balanceInfo }) {
balanceInfo={balanceInfo}
publicKey={publicKey}
swapInfo={swapInfo}
+ isAssociatedToken={isAssociatedToken}
/>
);
}
-
+ const displaySolAddress = publicKey.equals(owner) || isAssociatedToken;
+ const depositAddressStr = displaySolAddress
+ ? owner.toBase58()
+ : publicKey.toBase58();
return (
-
+
Deposit {tokenName ?? mint.toBase58()}
{tokenSymbol ? ` (${tokenSymbol})` : null}
@@ -84,20 +88,20 @@ export default function DepositDialog({
{tab === 0 ? (
<>
- {publicKey.equals(owner) ? (
-
- This address can only be used to receive SOL. Do not send other
- tokens to this address.
-
- ) : (
+ {!displaySolAddress && isAssociatedToken === false ? (
This address can only be used to receive{' '}
{tokenSymbol ?? abbreviateAddress(mint)}. Do not send SOL to
this address.
+ ) : (
+
+ This address can be used to receive{' '}
+ {tokenSymbol ?? abbreviateAddress(mint)}.
+
)}
) : (
<>
- Are you sure you want to merge accounts?
+ Are you sure you want to merge tokens?
WARNING : This action may break apps that depend on your
- existing accounts.
+ existing token accounts.
Merging sends all tokens to{' '}
diff --git a/src/components/NavigationFrame.js b/src/components/NavigationFrame.js
index 7d1a517..e996566 100644
--- a/src/components/NavigationFrame.js
+++ b/src/components/NavigationFrame.js
@@ -184,7 +184,7 @@ function ConnectionsButton() {
function NetworkSelector() {
const { endpoint, setEndpoint } = useConnectionConfig();
- const cluster = useMemo(() => clusterForEndpoint(endpoint), [endpoint])
+ const cluster = useMemo(() => clusterForEndpoint(endpoint), [endpoint]);
const [anchorEl, setAnchorEl] = useState(null);
const classes = useStyles();
@@ -226,7 +226,9 @@ function NetworkSelector() {
selected={cluster.apiUrl === endpoint}
>
- {cluster.apiUrl === endpoint ? : null}
+ {cluster.apiUrl === endpoint ? (
+
+ ) : null}
{cluster.apiUrl}
@@ -237,7 +239,13 @@ function NetworkSelector() {
}
function WalletSelector() {
- const { accounts, setWalletSelector, addAccount } = useWalletSelector();
+ const {
+ accounts,
+ hardwareWalletAccount,
+ setHardwareWalletAccount,
+ setWalletSelector,
+ addAccount,
+ } = useWalletSelector();
const [anchorEl, setAnchorEl] = useState(null);
const [addAccountOpen, setAddAccountOpen] = useState(false);
const [
@@ -251,22 +259,28 @@ function WalletSelector() {
if (accounts.length === 0) {
return null;
}
-
return (
<>
setAddHardwareWalletDialogOpen(false)}
- onAdd={(pubKey) => {
- addAccount({
+ onAdd={({ publicKey, derivationPath, account, change }) => {
+ setHardwareWalletAccount({
name: 'Hardware wallet',
- importedAccount: pubKey.toString(),
+ publicKey,
+ importedAccount: publicKey.toString(),
ledger: true,
+ derivationPath,
+ account,
+ change,
});
setWalletSelector({
walletIndex: undefined,
- importedPubkey: pubKey.toString(),
+ importedPubkey: publicKey.toString(),
ledger: true,
+ derivationPath,
+ account,
+ change,
});
}}
/>
@@ -319,27 +333,25 @@ function WalletSelector() {
}}
getContentAnchorEl={null}
>
- {accounts.map(({ isSelected, selector, address, name, label }) => (
- {
- setAnchorEl(null);
- setWalletSelector(selector);
- }}
- selected={isSelected}
- component="div"
- >
-
- {isSelected ? : null}
-
-
- {name}
-
- {address.toBase58()}
-
-
-
+ {accounts.map((account) => (
+
))}
+ {hardwareWalletAccount && (
+ <>
+
+
+ >
+ )}
setAddHardwareWalletDialogOpen(true)}>
@@ -411,3 +423,27 @@ function Footer() {
);
}
+
+function AccountListItem({ account, classes, setAnchorEl, setWalletSelector }) {
+ return (
+ {
+ setAnchorEl(null);
+ setWalletSelector(account.selector);
+ }}
+ selected={account.isSelected}
+ component="div"
+ >
+
+ {account.isSelected ? : null}
+
+
+ {account.name}
+
+ {account.address.toBase58()}
+
+
+
+ );
+}
diff --git a/src/pages/LoginPage.js b/src/pages/LoginPage.js
index b058d41..1b38fa8 100644
--- a/src/pages/LoginPage.js
+++ b/src/pages/LoginPage.js
@@ -10,7 +10,6 @@ import {
getAccountFromSeed,
DERIVATION_PATH,
} from '../utils/walletProvider/localStorage.js';
-import { useSolanaExplorerUrlSuffix } from '../utils/connection';
import Container from '@material-ui/core/Container';
import LoadingIndicator from '../components/LoadingIndicator';
import { BalanceListItem } from '../components/BalancesList.js';
@@ -276,6 +275,11 @@ function RestoreWalletForm({ goBack }) {
Restore your wallet using your twelve or twenty-four seed words.
Note that this will delete any existing wallet on this device.
+
+
+ Do not enter your hardware wallet seedphrase here. Hardware
+ wallets can be optionally connected after a web wallet is created.
+
{
return getAccountFromSeed(
Buffer.from(seed, 'hex'),
@@ -356,52 +358,12 @@ function DerivedAccounts({ goBack, mnemonic, seed, password }) {
return (
-
-
-
- Derivable Accounts
-
-
- setDPathMenuItem(e.target.value)}
- >
-
- {`m/44'/501'/0'/0'`}
-
-
- {`m/44'/501'/0'`}
-
-
- {`m/501'/0'/0/0 (deprecated)`}
-
-
-
-
- {accounts.map((acc) => {
- return (
-
-
-
- );
- })}
-
+
Back
@@ -412,18 +374,80 @@ function DerivedAccounts({ goBack, mnemonic, seed, password }) {
);
}
+export function AccountsSelector({
+ showRoot,
+ showDeprecated,
+ accounts,
+ dPathMenuItem,
+ setDPathMenuItem,
+ onClick,
+}) {
+ return (
+
+
+
+ Derivable Accounts
+
+
+ {
+ setDPathMenuItem(e.target.value);
+ }}
+ >
+ {showRoot && (
+
+ {`m/44'/501'`}
+
+ )}
+
+ {`m/44'/501'/0'`}
+
+
+ {`m/44'/501'/0'/0'`}
+
+ {showDeprecated && (
+
+ {`m/501'/0'/0/0 (deprecated)`}
+
+ )}
+
+
+
+ {accounts.map((acc) => {
+ return (
+ onClick(acc) : {}}>
+
+
+ );
+ })}
+
+ );
+}
+
// Material UI's Select doesn't render properly when using an `undefined` value,
// so we define this type and the subsequent `toDerivationPath` translator as a
// workaround.
//
// DERIVATION_PATH.deprecated is always undefined.
-const DerivationPathMenuItem = {
+export const DerivationPathMenuItem = {
Deprecated: 0,
Bip44: 1,
Bip44Change: 2,
+ Bip44Root: 3, // Ledger only.
};
-function toDerivationPath(dPathMenuItem) {
+export function toDerivationPath(dPathMenuItem) {
switch (dPathMenuItem) {
case DerivationPathMenuItem.Deprecated:
return DERIVATION_PATH.deprecated;
@@ -431,6 +455,8 @@ function toDerivationPath(dPathMenuItem) {
return DERIVATION_PATH.bip44;
case DerivationPathMenuItem.Bip44Change:
return DERIVATION_PATH.bip44Change;
+ case DerivationPathMenuItem.Bip44Root:
+ return DERIVATION_PATH.bip44Root;
default:
throw new Error(`invalid derivation path: ${dPathMenuItem}`);
}
diff --git a/src/pages/PopupPage.js b/src/pages/PopupPage.js
index 00e1c69..452166d 100644
--- a/src/pages/PopupPage.js
+++ b/src/pages/PopupPage.js
@@ -49,7 +49,6 @@ export default function PopupPage({ opener }) {
const [connectedAccount, setConnectedAccount] = useState(null);
const hasConnectedAccount = !!connectedAccount;
-
const [requests, setRequests] = useState(getInitialRequests);
const [autoApprove, setAutoApprove] = useState(false);
const postMessage = useCallback(
@@ -66,6 +65,22 @@ export default function PopupPage({ opener }) {
[opener, origin],
);
+ // Hack to keep selectedWallet and wallet in sync. TODO: remove this block.
+ useEffect(() => {
+ if (!isExtension) {
+ if (!wallet) {
+ setWallet(selectedWallet);
+ } else if (!wallet.publicKey.equals(selectedWallet.publicKey)) {
+ setWallet(selectedWallet);
+ }
+ }
+ }, [
+ wallet,
+ wallet.publicKey,
+ selectedWallet,
+ selectedWallet.publicKey,
+ ]);
+
// (Extension only) Fetch connected wallet for site from local storage.
useEffect(() => {
if (isExtension) {
@@ -262,9 +277,19 @@ export default function PopupPage({ opener }) {
}
async function sendAllSignatures(messages) {
- const signatures = await Promise.all(
- messages.map((m) => wallet.createSignature(m)),
- );
+ console.log('wallet', wallet);
+ let signatures;
+ // Ledger must sign one by one.
+ if (wallet.type === 'ledger') {
+ signatures = [];
+ for (let k = 0; k < messages.length; k += 1) {
+ signatures.push(await wallet.createSignature(messages[k]));
+ }
+ } else {
+ signatures = await Promise.all(
+ messages.map((m) => wallet.createSignature(m)),
+ );
+ }
postMessage({
result: {
signatures,
@@ -352,11 +377,11 @@ const useStyles = makeStyles((theme) => ({
function ApproveConnectionForm({ origin, onApprove }) {
const wallet = useWallet();
- const { accounts } = useWalletSelector();
+ const { accounts, hardwareWalletAccount } = useWalletSelector();
// TODO better way to do this
- const account = accounts.find((account) =>
- account.address.equals(wallet.publicKey),
- );
+ const account = accounts
+ .concat([hardwareWalletAccount])
+ .find((account) => account && account.address.equals(wallet.publicKey));
const classes = useStyles();
const [autoApprove, setAutoApprove] = useState(false);
let [dismissed, setDismissed] = useLocalStorageState(
diff --git a/src/utils/markets.ts b/src/utils/markets.ts
index 8b65fae..bd7e1ef 100644
--- a/src/utils/markets.ts
+++ b/src/utils/markets.ts
@@ -7,7 +7,7 @@ interface Markets {
publicKey: PublicKey;
name: string;
deprecated?: boolean;
- }
+ };
}
export const serumMarkets = (() => {
@@ -34,7 +34,7 @@ export const serumMarkets = (() => {
// Create a cached API wrapper to avoid rate limits.
class PriceStore {
- cache: {}
+ cache: {};
constructor() {
this.cache = {};
@@ -50,7 +50,12 @@ class PriceStore {
fetch(`https://serum-api.bonfida.com/orderbooks/${marketName}`).then(
(resp) => {
resp.json().then((resp) => {
- if (resp.data.asks.length === 0 && resp.data.bids.length === 0) {
+ if (resp.data.asks === null || resp.data.bids === null) {
+ resolve(undefined);
+ } else if (
+ resp.data.asks.length === 0 &&
+ resp.data.bids.length === 0
+ ) {
resolve(undefined);
} else if (resp.data.asks.length === 0) {
resolve(resp.data.bids[0].price);
diff --git a/src/utils/wallet.js b/src/utils/wallet.js
index bddbded..18667e4 100644
--- a/src/utils/wallet.js
+++ b/src/utils/wallet.js
@@ -172,17 +172,20 @@ export function WalletProvider({ children }) {
{},
);
// `walletSelector` identifies which wallet to use.
- const [walletSelector, setWalletSelector] = useLocalStorageState(
+ let [walletSelector, setWalletSelector] = useLocalStorageState(
'walletSelector',
DEFAULT_WALLET_SELECTOR,
);
- const [ledgerPubKey, setLedgerPubKey] = useState(
- walletSelector.ledger ? walletSelector.importedPubkey : undefined,
- );
+ const [_hardwareWalletAccount, setHardwareWalletAccount] = useState(null);
// `walletCount` is the number of HD wallets.
const [walletCount, setWalletCount] = useLocalStorageState('walletCount', 1);
+ if (walletSelector.ledger && !_hardwareWalletAccount) {
+ walletSelector = DEFAULT_WALLET_SELECTOR;
+ setWalletSelector(DEFAULT_WALLET_SELECTOR);
+ }
+
useEffect(() => {
(async () => {
if (!seed) {
@@ -193,9 +196,15 @@ export function WalletProvider({ children }) {
try {
const onDisconnect = () => {
setWalletSelector(DEFAULT_WALLET_SELECTOR);
- setLedgerPubKey(undefined);
+ setHardwareWalletAccount(null);
};
- wallet = await Wallet.create(connection, 'ledger', { onDisconnect });
+ const args = {
+ onDisconnect,
+ derivationPath: walletSelector.derivationPath,
+ account: walletSelector.account,
+ change: walletSelector.change,
+ };
+ wallet = await Wallet.create(connection, 'ledger', args);
} catch (e) {
console.log(`received error using ledger wallet: ${e}`);
let message = 'Received error unlocking ledger';
@@ -204,7 +213,7 @@ export function WalletProvider({ children }) {
}
enqueueSnackbar(message, { variant: 'error' });
setWalletSelector(DEFAULT_WALLET_SELECTOR);
- setLedgerPubKey(undefined);
+ setHardwareWalletAccount(null);
return;
}
}
@@ -242,11 +251,8 @@ export function WalletProvider({ children }) {
enqueueSnackbar,
derivationPath,
]);
-
function addAccount({ name, importedAccount, ledger }) {
- if (ledger) {
- setLedgerPubKey(importedAccount);
- } else if (importedAccount === undefined) {
+ if (importedAccount === undefined) {
name && localStorage.setItem(`name${walletCount}`, name);
setWalletCount(walletCount + 1);
} else {
@@ -319,29 +325,26 @@ export function WalletProvider({ children }) {
};
});
- if (ledgerPubKey) {
- derivedAccounts.push({
- selector: {
- walletIndex: undefined,
- importedPubkey: ledgerPubKey,
- ledger: true,
- },
- address: new PublicKey(ledgerPubKey), // todo: get the ledger address
- name: 'Hardware wallet',
- isSelected: walletSelector.ledger,
- });
- }
-
return derivedAccounts.concat(importedAccounts);
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [
- seed,
- walletCount,
- walletSelector,
- privateKeyImports,
- walletNames,
- ledgerPubKey,
- ]);
+ }, [seed, walletCount, walletSelector, privateKeyImports, walletNames]);
+
+ let hardwareWalletAccount;
+ if (_hardwareWalletAccount) {
+ hardwareWalletAccount = {
+ ..._hardwareWalletAccount,
+ selector: {
+ walletIndex: undefined,
+ ledger: true,
+ importedPubkey: _hardwareWalletAccount.publicKey,
+ derivationPath: _hardwareWalletAccount.derivationPath,
+ account: _hardwareWalletAccount.account,
+ change: _hardwareWalletAccount.change,
+ },
+ address: _hardwareWalletAccount.publicKey,
+ isSelected: walletSelector.ledger,
+ };
+ }
return (
{children}
@@ -473,7 +478,16 @@ export function useWalletSelector() {
addAccount,
setWalletSelector,
setAccountName,
+ hardwareWalletAccount,
+ setHardwareWalletAccount,
} = useContext(WalletContext);
- return { accounts, setWalletSelector, addAccount, setAccountName };
+ return {
+ accounts,
+ setWalletSelector,
+ addAccount,
+ setAccountName,
+ hardwareWalletAccount,
+ setHardwareWalletAccount,
+ };
}
diff --git a/src/utils/walletProvider/ledger-core.js b/src/utils/walletProvider/ledger-core.js
index 428dfb6..47f0710 100644
--- a/src/utils/walletProvider/ledger-core.js
+++ b/src/utils/walletProvider/ledger-core.js
@@ -1,4 +1,5 @@
import { PublicKey } from '@solana/web3.js';
+import { DERIVATION_PATH } from './localStorage';
const bs58 = require('bs58');
const INS_GET_PUBKEY = 0x05;
@@ -60,21 +61,43 @@ function _harden(n) {
return (n | BIP32_HARDENED_BIT) >>> 0;
}
-export function solana_derivation_path(account, change) {
- const length = 4;
+export function solana_derivation_path(account, change, derivationPath) {
let useAccount = account ? account : 0;
let useChange = change ? change : 0;
+ derivationPath = derivationPath
+ ? derivationPath
+ : DERIVATION_PATH.bip44Change;
- var derivation_path = Buffer.alloc(1 + length * 4);
- // eslint-disable-next-line
- var offset = 0;
- offset = derivation_path.writeUInt8(length, offset);
- offset = derivation_path.writeUInt32BE(_harden(44), offset); // Using BIP44
- offset = derivation_path.writeUInt32BE(_harden(501), offset); // Solana's BIP44 path
- offset = derivation_path.writeUInt32BE(_harden(useAccount), offset);
- derivation_path.writeUInt32BE(_harden(useChange), offset);
-
- return derivation_path;
+ if (derivationPath === DERIVATION_PATH.bip44Root) {
+ const length = 2;
+ const derivation_path = Buffer.alloc(1 + length * 4);
+ let offset = 0;
+ offset = derivation_path.writeUInt8(length, offset);
+ offset = derivation_path.writeUInt32BE(_harden(44), offset); // Using BIP44
+ derivation_path.writeUInt32BE(_harden(501), offset); // Solana's BIP44 path
+ return derivation_path;
+ } else if (derivationPath === DERIVATION_PATH.bip44) {
+ const length = 3;
+ const derivation_path = Buffer.alloc(1 + length * 4);
+ let offset = 0;
+ offset = derivation_path.writeUInt8(length, offset);
+ offset = derivation_path.writeUInt32BE(_harden(44), offset); // Using BIP44
+ offset = derivation_path.writeUInt32BE(_harden(501), offset); // Solana's BIP44 path
+ derivation_path.writeUInt32BE(_harden(useAccount), offset);
+ return derivation_path;
+ } else if (derivationPath === DERIVATION_PATH.bip44Change) {
+ const length = 4;
+ const derivation_path = Buffer.alloc(1 + length * 4);
+ let offset = 0;
+ offset = derivation_path.writeUInt8(length, offset);
+ offset = derivation_path.writeUInt32BE(_harden(44), offset); // Using BIP44
+ offset = derivation_path.writeUInt32BE(_harden(501), offset); // Solana's BIP44 path
+ offset = derivation_path.writeUInt32BE(_harden(useAccount), offset);
+ derivation_path.writeUInt32BE(_harden(useChange), offset);
+ return derivation_path;
+ } else {
+ throw new Error('Invalid derivation path');
+ }
}
async function solana_ledger_get_pubkey(transport, derivation_path) {
@@ -102,7 +125,6 @@ export async function solana_ledger_sign_bytes(
) {
var num_paths = Buffer.alloc(1);
num_paths.writeUInt8(1);
-
const payload = Buffer.concat([num_paths, derivation_path, msg_bytes]);
return solana_send(transport, INS_SIGN_MESSAGE, P1_CONFIRM, payload);
@@ -123,3 +145,15 @@ export async function getPublicKey(transport, path) {
return new PublicKey(from_pubkey_string);
}
+
+export async function solana_ledger_confirm_public_key(
+ transport,
+ derivation_path,
+) {
+ return await solana_send(
+ transport,
+ INS_GET_PUBKEY,
+ P1_CONFIRM,
+ derivation_path,
+ );
+}
diff --git a/src/utils/walletProvider/ledger.js b/src/utils/walletProvider/ledger.js
index facd708..16c77d8 100644
--- a/src/utils/walletProvider/ledger.js
+++ b/src/utils/walletProvider/ledger.js
@@ -4,17 +4,29 @@ import {
solana_derivation_path,
solana_ledger_sign_bytes,
solana_ledger_sign_transaction,
+ solana_ledger_confirm_public_key,
} from './ledger-core';
+import { DERIVATION_PATH } from './localStorage';
import bs58 from 'bs58';
export class LedgerWalletProvider {
constructor(args) {
this.onDisconnect = (args && args.onDisconnect) || (() => {});
+ this.derivationPath = args
+ ? args.derivationPath
+ : DERIVATION_PATH.bip44Change;
+ this.account = args ? args.account : undefined;
+ this.change = args ? args.change : undefined;
+ this.solanaDerivationPath = solana_derivation_path(
+ this.account,
+ this.change,
+ this.derivationPath,
+ );
}
init = async () => {
this.transport = await TransportWebUsb.create();
- this.pubKey = await getPublicKey(this.transport);
+ this.pubKey = await getPublicKey(this.transport, this.solanaDerivationPath);
this.transport.on('disconnect', this.onDisconnect);
this.listAddresses = async (walletCount) => {
// TODO: read accounts from ledger
@@ -28,10 +40,9 @@ export class LedgerWalletProvider {
}
signTransaction = async (transaction) => {
- const from_derivation_path = solana_derivation_path();
const sig_bytes = await solana_ledger_sign_transaction(
this.transport,
- from_derivation_path,
+ this.solanaDerivationPath,
transaction,
);
transaction.addSignature(this.publicKey, sig_bytes);
@@ -39,12 +50,18 @@ export class LedgerWalletProvider {
};
createSignature = async (message) => {
- const from_derivation_path = solana_derivation_path();
const sig_bytes = await solana_ledger_sign_bytes(
this.transport,
- from_derivation_path,
+ this.solanaDerivationPath,
message,
);
return bs58.encode(sig_bytes);
};
+
+ confirmPublicKey = async () => {
+ return await solana_ledger_confirm_public_key(
+ this.transport,
+ this.solanaDerivationPath,
+ );
+ };
}
diff --git a/src/utils/walletProvider/localStorage.js b/src/utils/walletProvider/localStorage.js
index d2a425f..adb8c39 100644
--- a/src/utils/walletProvider/localStorage.js
+++ b/src/utils/walletProvider/localStorage.js
@@ -9,6 +9,7 @@ export const DERIVATION_PATH = {
deprecated: undefined,
bip44: 'bip44',
bip44Change: 'bip44Change',
+ bip44Root: 'bip44Root', // Ledger only.
};
export function getAccountFromSeed(
diff --git a/yarn.lock b/yarn.lock
index 45ca7de..ff03540 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1862,10 +1862,10 @@
schema-utils "^2.6.5"
source-map "^0.7.3"
-"@project-serum/serum@^0.13.24":
- version "0.13.24"
- resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.24.tgz#42ab28052924c4b754ed078790a192ae23fffab9"
- integrity sha512-ux2MuO2kGGUV/QYBVx+e3roM/zATI6Fi2ThVaEo4aBdQKosbuNA60uAfSnYK9Du0kz7UjGoZHv2FnkCq8web3A==
+"@project-serum/serum@^0.13.33":
+ version "0.13.33"
+ resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.33.tgz#03ce8219c1bb458f56c09dc8aa621d76538e70f5"
+ integrity sha512-g2ztZwhQAvhGE9u4/Md6uEFBpaOMV2Xa/H/FGhgTx3iBv2sikW5fheHRJ8Vy7yEA9ZhZCuzbCkw8Wz1fq82VAg==
dependencies:
"@solana/web3.js" "^0.90.0"
bn.js "^5.1.2"