Auto create associated token account on withdrawal (#18)
* Auto create associated token account on withdrawal * simplify withdraw and default to USDC
This commit is contained in:
parent
da7af24d2b
commit
7558165c27
|
@ -32,7 +32,7 @@ import { MarginAccount, uiToNative } from '@blockworks-foundation/mango-client'
|
|||
import Select from './Select'
|
||||
|
||||
const WithdrawModal = ({ isOpen, onClose }) => {
|
||||
const [withdrawTokenSymbol, setWithdrawTokenSymbol] = useState('')
|
||||
const [withdrawTokenSymbol, setWithdrawTokenSymbol] = useState('USDC')
|
||||
const [inputAmount, setInputAmount] = useState(0)
|
||||
const [invalidAmountMessage, setInvalidAmountMessage] = useState('')
|
||||
const [maxAmount, setMaxAmount] = useState(0)
|
||||
|
@ -44,21 +44,12 @@ const WithdrawModal = ({ isOpen, onClose }) => {
|
|||
const [maxButtonTransition, setMaxButtonTransition] = useState(false)
|
||||
const { getTokenIndex, symbols } = useMarketList()
|
||||
const { connection, programId } = useConnection()
|
||||
const walletAccounts = useMangoStore((s) => s.wallet.balances)
|
||||
const prices = useMangoStore((s) => s.selectedMangoGroup.prices)
|
||||
const selectedMangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
const selectedMarginAccount = useMangoStore(
|
||||
(s) => s.selectedMarginAccount.current
|
||||
)
|
||||
const actions = useMangoStore((s) => s.actions)
|
||||
const withdrawAccounts = useMemo(
|
||||
() =>
|
||||
walletAccounts.filter((acc) =>
|
||||
Object.values(symbols).includes(acc.account.mint.toString())
|
||||
),
|
||||
[symbols, walletAccounts]
|
||||
)
|
||||
const [selectedAccount, setSelectedAccount] = useState(withdrawAccounts[0])
|
||||
const tokenIndex = useMemo(
|
||||
() => getTokenIndex(symbols[withdrawTokenSymbol]),
|
||||
[withdrawTokenSymbol, getTokenIndex]
|
||||
|
@ -130,6 +121,7 @@ const WithdrawModal = ({ isOpen, onClose }) => {
|
|||
prices
|
||||
)
|
||||
const leverage = 1 / Math.max(0, collateralRatio - 1)
|
||||
|
||||
setSimulation({
|
||||
equity,
|
||||
assetsVal,
|
||||
|
@ -160,8 +152,7 @@ const WithdrawModal = ({ isOpen, onClose }) => {
|
|||
mangoGroup,
|
||||
marginAccount,
|
||||
wallet,
|
||||
selectedAccount.account.mint,
|
||||
selectedAccount.publicKey,
|
||||
new PublicKey(symbols[withdrawTokenSymbol]),
|
||||
Number(inputAmount)
|
||||
)
|
||||
.then((_transSig: string) => {
|
||||
|
@ -188,8 +179,7 @@ const WithdrawModal = ({ isOpen, onClose }) => {
|
|||
mangoGroup,
|
||||
marginAccount,
|
||||
wallet,
|
||||
selectedAccount.account.mint,
|
||||
selectedAccount.publicKey,
|
||||
new PublicKey(symbols[withdrawTokenSymbol]),
|
||||
Number(inputAmount)
|
||||
)
|
||||
.then((_transSig: string) => {
|
||||
|
@ -343,7 +333,7 @@ const WithdrawModal = ({ isOpen, onClose }) => {
|
|||
}
|
||||
}, [withdrawTokenSymbol])
|
||||
|
||||
if (!selectedAccount) return null
|
||||
if (!withdrawTokenSymbol) return null
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
|
|
|
@ -203,7 +203,7 @@ const useMangoStore = create<MangoStore>((set, get) => ({
|
|||
const mangoClient = get().mangoClient
|
||||
const set = get().set
|
||||
|
||||
if (wallet?.publicKey && connected) {
|
||||
if (wallet?.publicKey && connected && selectedMangoGroup) {
|
||||
const usersMangoSrmAccounts =
|
||||
await mangoClient.getMangoSrmAccountsForOwner(
|
||||
connection,
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
import {
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
SYSVAR_RENT_PUBKEY,
|
||||
TransactionInstruction,
|
||||
} from '@solana/web3.js'
|
||||
import { TOKEN_PROGRAM_ID } from '@solana/spl-token'
|
||||
|
||||
const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID: PublicKey = new PublicKey(
|
||||
'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL'
|
||||
)
|
||||
|
||||
export async function findAssociatedTokenAddress(
|
||||
walletAddress: PublicKey,
|
||||
tokenMintAddress: PublicKey
|
||||
): Promise<PublicKey> {
|
||||
return (
|
||||
await PublicKey.findProgramAddress(
|
||||
[
|
||||
walletAddress.toBuffer(),
|
||||
TOKEN_PROGRAM_ID.toBuffer(),
|
||||
tokenMintAddress.toBuffer(),
|
||||
],
|
||||
SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID
|
||||
)
|
||||
)[0]
|
||||
}
|
||||
|
||||
export async function createAssociatedTokenAccount(
|
||||
fundingAddress: PublicKey,
|
||||
walletAddress: PublicKey,
|
||||
splTokenMintAddress: PublicKey
|
||||
): Promise<TransactionInstruction> {
|
||||
const associatedTokenAddress = await findAssociatedTokenAddress(
|
||||
walletAddress,
|
||||
splTokenMintAddress
|
||||
)
|
||||
const keys = [
|
||||
{
|
||||
pubkey: fundingAddress,
|
||||
isSigner: true,
|
||||
isWritable: true,
|
||||
},
|
||||
{
|
||||
pubkey: associatedTokenAddress,
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
},
|
||||
{
|
||||
pubkey: walletAddress,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
{
|
||||
pubkey: splTokenMintAddress,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
{
|
||||
pubkey: SystemProgram.programId,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
{
|
||||
pubkey: TOKEN_PROGRAM_ID,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
{
|
||||
pubkey: SYSVAR_RENT_PUBKEY,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
]
|
||||
return new TransactionInstruction({
|
||||
keys,
|
||||
programId: SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
|
||||
data: Buffer.from([]),
|
||||
})
|
||||
}
|
|
@ -52,6 +52,10 @@ import {
|
|||
} from '@project-serum/serum/lib/token-instructions'
|
||||
import { MangoSrmAccount } from '@blockworks-foundation/mango-client/lib/client'
|
||||
import { capitalize } from './index'
|
||||
import {
|
||||
findAssociatedTokenAddress,
|
||||
createAssociatedTokenAccount,
|
||||
} from './associated'
|
||||
|
||||
export const DEFAULT_MANGO_GROUP = 'BTC_ETH_SOL_SRM_USDC'
|
||||
|
||||
|
@ -348,21 +352,21 @@ export async function withdraw(
|
|||
marginAccount: MarginAccount,
|
||||
wallet: Wallet,
|
||||
token: PublicKey,
|
||||
tokenAcc: PublicKey,
|
||||
|
||||
quantity: number
|
||||
): Promise<TransactionSignature> {
|
||||
const transaction = new Transaction()
|
||||
const signers = []
|
||||
let tokenAcc = await findAssociatedTokenAddress(wallet.publicKey, token)
|
||||
|
||||
let wrappedSolAccount: Account | null = null
|
||||
if (token.equals(WRAPPED_SOL_MINT)) {
|
||||
wrappedSolAccount = new Account()
|
||||
tokenAcc = wrappedSolAccount.publicKey
|
||||
const lamports = Math.round(quantity * LAMPORTS_PER_SOL) + 1e7
|
||||
transaction.add(
|
||||
SystemProgram.createAccount({
|
||||
fromPubkey: wallet.publicKey,
|
||||
newAccountPubkey: wrappedSolAccount.publicKey,
|
||||
newAccountPubkey: tokenAcc,
|
||||
lamports,
|
||||
space: 165,
|
||||
programId: TOKEN_PROGRAM_ID,
|
||||
|
@ -370,12 +374,23 @@ export async function withdraw(
|
|||
)
|
||||
transaction.add(
|
||||
initializeAccount({
|
||||
account: wrappedSolAccount.publicKey,
|
||||
account: tokenAcc,
|
||||
mint: WRAPPED_SOL_MINT,
|
||||
owner: wallet.publicKey,
|
||||
})
|
||||
)
|
||||
signers.push(wrappedSolAccount)
|
||||
} else {
|
||||
const tokenAccExists = await connection.getAccountInfo(tokenAcc, 'recent')
|
||||
if (!tokenAccExists) {
|
||||
transaction.add(
|
||||
await createAssociatedTokenAccount(
|
||||
wallet.publicKey,
|
||||
wallet.publicKey,
|
||||
token
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const tokenIndex = mangoGroup.getTokenIndex(token)
|
||||
|
@ -391,7 +406,7 @@ export async function withdraw(
|
|||
{
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
pubkey: wrappedSolAccount?.publicKey ?? tokenAcc,
|
||||
pubkey: tokenAcc,
|
||||
},
|
||||
{
|
||||
isSigner: false,
|
||||
|
@ -448,21 +463,21 @@ export async function borrowAndWithdraw(
|
|||
marginAccount: MarginAccount,
|
||||
wallet: Wallet,
|
||||
token: PublicKey,
|
||||
tokenAcc: PublicKey,
|
||||
|
||||
withdrawQuantity: number
|
||||
): Promise<TransactionSignature> {
|
||||
const transaction = new Transaction()
|
||||
const signers = []
|
||||
let tokenAcc = await findAssociatedTokenAddress(wallet.publicKey, token)
|
||||
|
||||
let wrappedSolAccount: Account | null = null
|
||||
if (token.equals(WRAPPED_SOL_MINT)) {
|
||||
wrappedSolAccount = new Account()
|
||||
tokenAcc = wrappedSolAccount.publicKey
|
||||
const lamports = Math.round(withdrawQuantity * LAMPORTS_PER_SOL) + 1e7
|
||||
transaction.add(
|
||||
SystemProgram.createAccount({
|
||||
fromPubkey: wallet.publicKey,
|
||||
newAccountPubkey: wrappedSolAccount.publicKey,
|
||||
newAccountPubkey: tokenAcc,
|
||||
lamports,
|
||||
space: 165,
|
||||
programId: TOKEN_PROGRAM_ID,
|
||||
|
@ -476,6 +491,17 @@ export async function borrowAndWithdraw(
|
|||
})
|
||||
)
|
||||
signers.push(wrappedSolAccount)
|
||||
} else {
|
||||
const tokenAccExists = await connection.getAccountInfo(tokenAcc, 'recent')
|
||||
if (!tokenAccExists) {
|
||||
transaction.add(
|
||||
await createAssociatedTokenAccount(
|
||||
wallet.publicKey,
|
||||
wallet.publicKey,
|
||||
token
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const tokenIndex = mangoGroup.getTokenIndex(token)
|
||||
|
@ -839,7 +865,8 @@ export async function placeOrderAndSettle(
|
|||
) {
|
||||
// open orders missing for this market; create a new one now
|
||||
const openOrdersSpace = OpenOrders.getLayout(mangoGroup.dexProgramId).span
|
||||
const openOrdersLamports = await connection.getMinimumBalanceForRentExemption(
|
||||
const openOrdersLamports =
|
||||
await connection.getMinimumBalanceForRentExemption(
|
||||
openOrdersSpace,
|
||||
'singleGossip'
|
||||
)
|
||||
|
@ -1057,7 +1084,8 @@ export async function placeAndSettle(
|
|||
) {
|
||||
// open orders missing for this market; create a new one now
|
||||
const openOrdersSpace = OpenOrders.getLayout(mangoGroup.dexProgramId).span
|
||||
const openOrdersLamports = await connection.getMinimumBalanceForRentExemption(
|
||||
const openOrdersLamports =
|
||||
await connection.getMinimumBalanceForRentExemption(
|
||||
openOrdersSpace,
|
||||
'singleGossip'
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue