This commit is contained in:
Nathaniel Parke 2020-10-26 18:56:58 +08:00
parent 3bc4480259
commit 07a72c999a
3 changed files with 82 additions and 50 deletions

View File

@ -1,31 +1,35 @@
import React, {useEffect, useRef, useState} from 'react';
import React, { useEffect, useRef, useState } from 'react';
import DialogActions from '@material-ui/core/DialogActions';
import Button from '@material-ui/core/Button';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogContent from '@material-ui/core/DialogContent';
import TextField from '@material-ui/core/TextField';
import DialogForm from './DialogForm';
import {useWallet, useWalletAddressForMint} from '../utils/wallet';
import {PublicKey} from '@solana/web3.js';
import {abbreviateAddress} from '../utils/utils';
import { useWallet, useWalletAddressForMint } from '../utils/wallet';
import { PublicKey } from '@solana/web3.js';
import { abbreviateAddress } from '../utils/utils';
import InputAdornment from '@material-ui/core/InputAdornment';
import {useCallAsync, useSendTransaction} from '../utils/notifications';
import {swapApiRequest, useSwapApiGet} from '../utils/swap/api';
import {showSwapAddress} from '../utils/config';
import { useCallAsync, useSendTransaction } from '../utils/notifications';
import { swapApiRequest, useSwapApiGet } from '../utils/swap/api';
import { showSwapAddress } from '../utils/config';
import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';
import DialogContentText from '@material-ui/core/DialogContentText';
import {ConnectToMetamaskButton, useEthAccount, withdrawEth,} from '../utils/swap/eth';
import {useConnection, useIsProdNetwork} from '../utils/connection';
import {
ConnectToMetamaskButton,
useEthAccount,
withdrawEth,
} from '../utils/swap/eth';
import { useConnection, useIsProdNetwork } from '../utils/connection';
import Stepper from '@material-ui/core/Stepper';
import Step from '@material-ui/core/Step';
import StepLabel from '@material-ui/core/StepLabel';
import Link from '@material-ui/core/Link';
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 {TOKEN_PROGRAM_ID} from "../utils/tokens/instructions";
import {parseTokenAccountData} from "../utils/tokens/data";
import { TOKEN_PROGRAM_ID } from '../utils/tokens/instructions';
import { parseTokenAccountData } from '../utils/tokens/data';
const WUSDC_MINT = new PublicKey(
'BXXkv6z8ykpG1yuvUDPgh732wzVHB69RnB9YgSYh3itW',
@ -133,7 +137,9 @@ function SendSplDialog({ onClose, publicKey, balanceInfo, onSubmitRef }) {
const defaultAddressHelperText = 'Enter SPL token or Solana address';
const wallet = useWallet();
const [sendTransaction, sending] = useSendTransaction();
const [addressHelperText, setAddressHelperText] = useState(defaultAddressHelperText);
const [addressHelperText, setAddressHelperText] = useState(
defaultAddressHelperText,
);
const [passValidation, setPassValidation] = useState();
const {
fields,
@ -152,10 +158,14 @@ function SendSplDialog({ onClose, publicKey, balanceInfo, onSubmitRef }) {
return;
}
try {
const destinationAccountInfo = await wallet.connection.getAccountInfo(new PublicKey(destinationAddress));
const destinationAccountInfo = await wallet.connection.getAccountInfo(
new PublicKey(destinationAddress),
);
if (destinationAccountInfo.owner.equals(TOKEN_PROGRAM_ID)) {
const accountInfo = parseTokenAccountData(destinationAccountInfo.data);
const accountInfo = parseTokenAccountData(
destinationAccountInfo.data,
);
if (accountInfo.mint.toBase58() === mintString) {
setPassValidation(true);
setAddressHelperText('Address is a valid SPL token address');
@ -173,7 +183,7 @@ function SendSplDialog({ onClose, publicKey, balanceInfo, onSubmitRef }) {
setPassValidation(undefined);
}
})();
}, [destinationAddress, wallet, mintString])
}, [destinationAddress, wallet, mintString]);
async function makeTransaction() {
let amount = Math.round(parseFloat(transferAmountString) * 10 ** decimals);
@ -433,7 +443,11 @@ function useForm(balanceInfo, addressHelperText, passAddressValidation) {
value={destinationAddress}
onChange={(e) => setDestinationAddress(e.target.value.trim())}
helperText={addressHelperText}
id={!passAddressValidation && passAddressValidation !== undefined ? 'outlined-error-helper-text' : undefined}
id={
!passAddressValidation && passAddressValidation !== undefined
? 'outlined-error-helper-text'
: undefined
}
error={!passAddressValidation && passAddressValidation !== undefined}
/>
<TextField

View File

@ -1,4 +1,9 @@
import { PublicKey, SystemProgram, Transaction, Account } from '@solana/web3.js';
import {
PublicKey,
SystemProgram,
Transaction,
Account,
} from '@solana/web3.js';
import {
assertOwner,
closeAccount,
@ -9,7 +14,12 @@ import {
TOKEN_PROGRAM_ID,
transfer,
} from './instructions';
import {ACCOUNT_LAYOUT, getOwnedAccountsFilters, MINT_LAYOUT, parseTokenAccountData} from './data';
import {
ACCOUNT_LAYOUT,
getOwnedAccountsFilters,
MINT_LAYOUT,
parseTokenAccountData,
} from './data';
import bs58 from 'bs58';
export async function getOwnedTokenAccounts(connection, publicKey) {
@ -160,7 +170,9 @@ export async function transferTokens({
memo,
mint,
}) {
const destinationAccountInfo = await connection.getAccountInfo(destinationPublicKey);
const destinationAccountInfo = await connection.getAccountInfo(
destinationPublicKey,
);
if (destinationAccountInfo.owner.equals(TOKEN_PROGRAM_ID)) {
return await transferBetweenSplTokenAccounts({
connection,
@ -168,15 +180,19 @@ export async function transferTokens({
sourcePublicKey,
destinationPublicKey,
amount,
memo
memo,
});
}
const destinationSplTokenAccount = (await getOwnedTokenAccounts(connection, destinationPublicKey))
const destinationSplTokenAccount = (
await getOwnedTokenAccounts(connection, destinationPublicKey)
)
.map(({ publicKey, accountInfo }) => {
return {publicKey, parsed: parseTokenAccountData(accountInfo.data) };
return { publicKey, parsed: parseTokenAccountData(accountInfo.data) };
})
.filter(({parsed}) => parsed.mint.equals(mint))
.sort((a, b) => { return b.parsed.amount - a.parsed.amount })[0];
.filter(({ parsed }) => parsed.mint.equals(mint))
.sort((a, b) => {
return b.parsed.amount - a.parsed.amount;
})[0];
if (destinationSplTokenAccount) {
return await transferBetweenSplTokenAccounts({
connection,
@ -184,7 +200,7 @@ export async function transferTokens({
sourcePublicKey,
destinationPublicKey: destinationSplTokenAccount.publicKey,
amount,
memo
memo,
});
}
return await createAndTransferToAccount({
@ -195,7 +211,7 @@ export async function transferTokens({
amount,
memo,
mint,
})
});
}
function createTransferBetweenSplTokenAccountsInstruction({
@ -225,15 +241,15 @@ async function transferBetweenSplTokenAccounts({
sourcePublicKey,
destinationPublicKey,
amount,
memo
memo,
}) {
const transaction = createTransferBetweenSplTokenAccountsInstruction({
owner,
sourcePublicKey,
destinationPublicKey,
amount,
memo
})
memo,
});
let signers = [owner];
return await connection.sendTransaction(transaction, signers, {
preflightCommitment: 'single',
@ -251,10 +267,12 @@ async function createAndTransferToAccount({
}) {
const newAccount = new Account();
let transaction = new Transaction();
transaction.add(assertOwner({
account: destinationPublicKey,
owner: SystemProgram.programId,
}))
transaction.add(
assertOwner({
account: destinationPublicKey,
owner: SystemProgram.programId,
}),
);
transaction.add(
SystemProgram.createAccount({
fromPubkey: owner.publicKey,
@ -273,13 +291,15 @@ async function createAndTransferToAccount({
owner: destinationPublicKey,
}),
);
const transferBetweenAccountsTxn = createTransferBetweenSplTokenAccountsInstruction({
owner,
sourcePublicKey,
destinationPublicKey: newAccount.publicKey,
amount,
memo
});
const transferBetweenAccountsTxn = createTransferBetweenSplTokenAccountsInstruction(
{
owner,
sourcePublicKey,
destinationPublicKey: newAccount.publicKey,
amount,
memo,
},
);
transaction.add(transferBetweenAccountsTxn);
let signers = [owner, newAccount];
return await connection.sendTransaction(transaction, signers, {

View File

@ -4,7 +4,7 @@ import {
SYSVAR_RENT_PUBKEY,
TransactionInstruction,
} from '@solana/web3.js';
import {publicKeyLayout} from "@project-serum/serum/lib/layout";
import { publicKeyLayout } from '@project-serum/serum/lib/layout';
export const TOKEN_PROGRAM_ID = new PublicKey(
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
@ -153,12 +153,12 @@ export function memoInstruction(memo) {
}
export const OWNER_VALIDATION_PROGRAM_ID = new PublicKey(
'4MNPdKu9wFMvEeZBMt3Eipfs5ovVWTJb31pEXDJAAxX5'
'4MNPdKu9wFMvEeZBMt3Eipfs5ovVWTJb31pEXDJAAxX5',
);
export const OWNER_VALIDATION_LAYOUT = BufferLayout.struct([
publicKeyLayout('account')
])
publicKeyLayout('account'),
]);
export function encodeOwnerValidationInstruction(instruction) {
const b = Buffer.alloc(OWNER_VALIDATION_LAYOUT.span);
@ -167,12 +167,10 @@ export function encodeOwnerValidationInstruction(instruction) {
}
export function assertOwner({ account, owner }) {
const keys = [
{ pubkey: account, isSigner: false, isWritable: false }
]
const keys = [{ pubkey: account, isSigner: false, isWritable: false }];
return new TransactionInstruction({
keys,
data: encodeOwnerValidationInstruction({account: owner}),
programId: OWNER_VALIDATION_PROGRAM_ID
data: encodeOwnerValidationInstruction({ account: owner }),
programId: OWNER_VALIDATION_PROGRAM_ID,
});
}