Merge remote-tracking branch 'origin' into in_memory_wallet_unlock

This commit is contained in:
jhl-alameda 2021-04-26 21:21:59 +08:00
commit 0096fe95a0
9 changed files with 100 additions and 84 deletions

View File

@ -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.

View File

@ -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}

View File

@ -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,
); );
} }
} }

View File

@ -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,

View File

@ -43,12 +43,10 @@ const WUSDC_MINT = new PublicKey(
const USDC_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); const USDC_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
const WUSDT_MINT = new PublicKey( const WUSDT_MINT = new PublicKey(
'BQcdHdAQW1hczDbBi9hiegXAR7A98Q9jx3X3iBBBDiq4' 'BQcdHdAQW1hczDbBi9hiegXAR7A98Q9jx3X3iBBBDiq4',
); );
const USDT_MINT = new PublicKey( const USDT_MINT = new PublicKey('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB');
'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'
);
export default function SendDialog({ open, onClose, publicKey, balanceInfo }) { export default function SendDialog({ open, onClose, publicKey, balanceInfo }) {
const isProdNetwork = useIsProdNetwork(); const isProdNetwork = useIsProdNetwork();
@ -68,40 +66,34 @@ export default function SendDialog({ open, onClose, publicKey, balanceInfo }) {
if (mint?.equals(WUSDC_MINT)) { if (mint?.equals(WUSDC_MINT)) {
return [ return [
<Tab label="SPL WUSDC" key="spl" value="spl" />, <Tab label="SPL WUSDC" key="spl" value="spl" />,
<Tab <Tab label="SPL USDC" key="wusdcToSplUsdc" value="wusdcToSplUsdc" />,
label="SPL USDC"
key="wusdcToSplUsdc"
value="wusdcToSplUsdc"
/>,
<Tab label="ERC20 USDC" key="swap" value="swap" />, <Tab label="ERC20 USDC" key="swap" value="swap" />,
] ];
} else if (mint?.equals(WUSDT_MINT)) { } else if (mint?.equals(WUSDT_MINT)) {
return [ return [
<Tab label="SPL WUSDT" key="spl" value="spl" />, <Tab label="SPL WUSDT" key="spl" value="spl" />,
<Tab <Tab label="SPL USDT" key="wusdtToSplUsdt" value="wusdtToSplUsdt" />,
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" />,
<Tab label="ERC20 USDC" key="swap" value="swap" />,
];
} else { } else {
return [ return [
<Tab label={`SPL ${swapCoinInfo.ticker}`} key="spl" value="spl" />,
<Tab <Tab
label={`SPL ${swapCoinInfo.ticker}`} label={`${swapCoinInfo.erc20Contract ? 'ERC20' : 'Native'} ${
key="spl" swapCoinInfo.ticker
value="spl" }`}
/>,
<Tab
label={`${
swapCoinInfo.erc20Contract ? 'ERC20' : 'Native'
} ${swapCoinInfo.ticker}`}
key="swap" key="swap"
value="swap" value="swap"
/>, />,
] ];
}
} }
};
return ( return (
<> <>
@ -160,6 +152,16 @@ export default function SendDialog({ open, onClose, publicKey, balanceInfo }) {
onSubmitRef={onSubmitRef} onSubmitRef={onSubmitRef}
wusdtToSplUsdt wusdtToSplUsdt
/> />
) : tab === 'usdcToSplWUsdc' ? (
<SendSwapDialog
key={tab}
onClose={onClose}
publicKey={publicKey}
balanceInfo={balanceInfo}
swapCoinInfo={swapCoinInfo}
onSubmitRef={onSubmitRef}
usdcToSplWUsdc
/>
) : ( ) : (
<SendSwapDialog <SendSwapDialog
key={tab} key={tab}
@ -259,6 +261,7 @@ function SendSplDialog({ onClose, publicKey, balanceInfo, onSubmitRef }) {
new PublicKey(destinationAddress), new PublicKey(destinationAddress),
amount, amount,
balanceInfo.mint, balanceInfo.mint,
decimals,
null, null,
overrideDestinationCheck, overrideDestinationCheck,
); );
@ -309,6 +312,7 @@ function SendSwapDialog({
ethAccount, ethAccount,
wusdcToSplUsdc = false, wusdcToSplUsdc = false,
wusdtToSplUsdt = false, wusdtToSplUsdt = false,
usdcToSplWUsdc = false,
onSubmitRef, onSubmitRef,
}) { }) {
const wallet = useWallet(); const wallet = useWallet();
@ -323,7 +327,8 @@ function SendSwapDialog({
} = useForm(balanceInfo); } = useForm(balanceInfo);
const { tokenName, decimals, mint } = balanceInfo; const { tokenName, decimals, mint } = balanceInfo;
const blockchain = wusdcToSplUsdc || wusdtToSplUsdt const blockchain =
wusdcToSplUsdc || wusdtToSplUsdt || usdcToSplWUsdc
? 'sol' ? 'sol'
: swapCoinInfo.blockchain === 'sol' : swapCoinInfo.blockchain === 'sol'
? 'eth' ? 'eth'
@ -361,13 +366,26 @@ 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, wusdcToSplUsdc, splUsdcWalletAddress, wusdtToSplUsdt, splUsdtWalletAddress]); }, [
setDestinationAddress,
wusdcToSplUsdc,
splUsdcWalletAddress,
wusdtToSplUsdt,
splUsdtWalletAddress,
usdcToSplWUsdc,
splWUsdcWalletAddress,
]);
async function makeTransaction() { async function makeTransaction() {
let amount = Math.round(parseFloat(transferAmountString) * 10 ** decimals); let amount = Math.round(parseFloat(transferAmountString) * 10 ** decimals);
@ -386,6 +404,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;
} }
@ -398,6 +421,7 @@ function SendSwapDialog({
new PublicKey(swapInfo.address), new PublicKey(swapInfo.address),
amount, amount,
balanceInfo.mint, balanceInfo.mint,
decimals,
swapInfo.memo, swapInfo.memo,
); );
} }

View File

@ -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);
} }

View File

@ -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,

View File

@ -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,
}); });

View File

@ -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 {
@ -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);
}; };
@ -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,