implement withdraw max with borrow

This commit is contained in:
Tyler Shipe 2021-04-20 10:09:25 -04:00
parent a11758784c
commit 74d9f70dbf
10 changed files with 213 additions and 78 deletions

View File

@ -8,6 +8,7 @@ import { notify } from '../utils/notifications'
const BalancesTable = () => {
const balances = useBalances()
const { programId, connection } = useConnection()
const actions = useMangoStore((s) => s.actions)
async function handleSettleAll() {
const markets = Object.values(
@ -26,6 +27,7 @@ const BalancesTable = () => {
markets,
wallet
)
actions.fetchMarginAccounts()
} catch (e) {
console.warn('Error settling all:', e)
if (e.message === 'No unsettled funds') {

View File

@ -16,6 +16,8 @@ const ConnectWalletButton = () => {
DEFAULT_PROVIDER.url
)
if (!wallet) return null
return (
<div className="flex justify-between border border-th-primary rounded-md h-11 w-48">
<button

View File

@ -7,8 +7,6 @@ import FloatingElement from './FloatingElement'
import Tooltip from './Tooltip'
const calculatePNL = (tradeHistory, prices, mangoGroup) => {
console.log('calculate pnl, trade history:', tradeHistory)
if (!tradeHistory.length) return '0.00'
const profitAndLoss = {}
const groupedTrades = groupBy(tradeHistory, (trade) => trade.marketName)
@ -75,8 +73,6 @@ export default function MarginInfo() {
useEffect(() => {
if (selectedMangoGroup) {
selectedMangoGroup.getPrices(connection).then((prices) => {
console.log('pricessss', prices)
const collateralRatio = selectedMarginAccount
? selectedMarginAccount.getCollateralRatio(selectedMangoGroup, prices)
: 200

View File

@ -55,7 +55,7 @@ const Modal = ({ isOpen, onClose, children, hideClose = false }) => {
const Header = ({ children }) => {
return (
<div className={`flex justify-center bg-th-bkg-2 p-4 pt-0`}>{children}</div>
<div className={`flex justify-center bg-th-bkg-2 p-2 pt-0`}>{children}</div>
)
}

View File

@ -5,9 +5,12 @@ import AccountSelect from './AccountSelect'
import { ElementTitle } from './styles'
import useMangoStore from '../stores/useMangoStore'
import useMarketList from '../hooks/useMarketList'
import { getSymbolForTokenMintAddress } from '../utils/index'
import {
getSymbolForTokenMintAddress,
formatBalanceDisplay,
} from '../utils/index'
import useConnection from '../hooks/useConnection'
import { withdraw } from '../utils/mango'
import { borrowAndWithdraw, withdraw } from '../utils/mango'
import Loading from './Loading'
import Button from './Button'
import { notify } from '../utils/notifications'
@ -19,6 +22,7 @@ const WithdrawModal = ({ isOpen, onClose }) => {
const { connection, programId } = useConnection()
const walletAccounts = useMangoStore((s) => s.wallet.balances)
const selectedMangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const mintDecimals = useMangoStore((s) => s.selectedMangoGroup.mintDecimals)
const selectedMarginAccount = useMangoStore(
(s) => s.selectedMarginAccount.current
)
@ -39,6 +43,11 @@ const WithdrawModal = ({ isOpen, onClose }) => {
getTokenIndex,
])
const handleSetSelectedAccount = (val) => {
setInputAmount('')
setSelectedAccount(val)
}
const withdrawDisabled = Number(inputAmount) <= 0
const getMaxForSelectedAccount = () => {
@ -53,21 +62,27 @@ const WithdrawModal = ({ isOpen, onClose }) => {
const setMaxBorrowForSelectedAccount = async () => {
const prices = await selectedMangoGroup.getPrices(connection)
console.log('prices', prices)
const assetsVal = selectedMarginAccount.getAssetsVal(
const assetsValBeforeTokenBal = selectedMarginAccount.getAssetsVal(
selectedMangoGroup,
prices
)
const assetsVal = assetsValBeforeTokenBal - getMaxForSelectedAccount()
const currentLiabs = selectedMarginAccount.getLiabsVal(
selectedMangoGroup,
prices
)
const liabsAvail = 0.99 * (assetsVal / 1.2 - currentLiabs)
console.log('assetsVal', assetsVal)
const liabsAvail = (assetsVal / 1.2 - currentLiabs) * 0.99 - 0.01
console.log('selected token deposits', getMaxForSelectedAccount())
console.log('prices', prices)
console.log('assetsVal', assetsVal)
console.log('currentLiabs', currentLiabs)
console.log('liabsAvail', liabsAvail)
const amountToWithdraw =
liabsAvail / prices[tokenIndex] + getMaxForSelectedAccount()
const decimals = mintDecimals[getTokenIndex(mintAddress)]
setInputAmount(formatBalanceDisplay(amountToWithdraw, decimals).toString())
}
const handleWithdraw = () => {
@ -75,7 +90,11 @@ const WithdrawModal = ({ isOpen, onClose }) => {
const marginAccount = useMangoStore.getState().selectedMarginAccount.current
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
const wallet = useMangoStore.getState().wallet.current
if (marginAccount && mangoGroup) {
if (!marginAccount || !mangoGroup) return
if (Number(inputAmount) <= getMaxForSelectedAccount()) {
console.log('=withdraw without borrow=')
withdraw(
connection,
programId,
@ -90,13 +109,44 @@ const WithdrawModal = ({ isOpen, onClose }) => {
setSubmitting(false)
actions.fetchMangoGroup()
actions.fetchMarginAccounts()
actions.fetchWalletBalances()
onClose()
})
.catch((err) => {
setSubmitting(false)
console.warn('Error withdrawing:', err)
notify({
message: 'Could not perform withdraw operation',
message: 'Could not perform withdraw',
description: `${err}`,
type: 'error',
})
onClose()
})
} else {
console.log('-withdraw with borrow-')
borrowAndWithdraw(
connection,
programId,
mangoGroup,
marginAccount,
wallet,
selectedAccount.account.mint,
selectedAccount.publicKey,
Number(inputAmount)
)
.then((_transSig: string) => {
setSubmitting(false)
actions.fetchMangoGroup()
actions.fetchMarginAccounts()
actions.fetchWalletBalances()
onClose()
})
.catch((err) => {
setSubmitting(false)
console.warn('Error borrowing and withdrawing:', err)
notify({
message: 'Could not perform borrow and withdraw',
description: `${err}`,
type: 'error',
})
@ -112,18 +162,18 @@ const WithdrawModal = ({ isOpen, onClose }) => {
<Modal.Header>
<ElementTitle noMarignBottom>Withdraw Funds</ElementTitle>
</Modal.Header>
<div className={`pb-6 px-8`}>
<div className={`text-th-fgd-1 pb-2`}>Token Account</div>
<div className="pb-6 px-8">
<div className="text-th-fgd-1 pb-2">Token Account</div>
<AccountSelect
hideAddress
accounts={withdrawAccounts}
selectedAccount={selectedAccount}
onSelectAccount={setSelectedAccount}
onSelectAccount={handleSetSelectedAccount}
getBalance={getMaxForSelectedAccount}
/>
<div className="flex justify-between pb-2 pt-4">
<div className={`text-th-fgd-1`}>Amount</div>
<div>
<div className="text-th-fgd-1">Amount</div>
<div className="flex space-x-4">
<div
className="text-th-fgd-1 underline cursor-pointer default-transition hover:text-th-primary hover:no-underline"
onClick={setMaxForSelectedAccount}

View File

@ -1,41 +0,0 @@
import { useEffect } from 'react'
import useMangoStore from '../stores/useMangoStore'
import useInterval from './useInterval'
const useMarginAccount = () => {
const mangoClient = useMangoStore((s) => s.mangoClient)
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const actions = useMangoStore((s) => s.actions)
const selectedMarginAccount = useMangoStore(
(s) => s.selectedMarginAccount.current
)
const connected = useMangoStore((s) => s.wallet.connected)
const setMangoStore = useMangoStore((s) => s.set)
useEffect(() => {
actions.fetchMangoGroup()
}, [actions])
useEffect(() => {
if (connected) {
actions.fetchMarginAccounts()
actions.fetchMangoSrmAccounts()
}
}, [connected, actions])
useInterval(() => {
if (connected) {
actions.fetchMarginAccounts()
}
// fetchMangoGroup()
}, 9000)
return {
mangoClient,
setMangoStore,
mangoGroup,
marginAccount: selectedMarginAccount,
}
}
export default useMarginAccount

View File

@ -2,7 +2,6 @@ import { useEffect, useRef } from 'react'
import useMangoStore from '../stores/useMangoStore'
import useSerumStore from '../stores/useSerumStore'
import useMarket from './useMarket'
import useInterval from './useInterval'
const byTimestamp = (a, b) => {
return (
@ -55,14 +54,6 @@ const useFills = () => {
export const useTradeHistory = () => {
const eventQueueFills = useFills()
const tradeHistory = useMangoStore((s) => s.tradeHistory)
const marginAccount = useMangoStore((s) => s.selectedMarginAccount.current)
const actions = useMangoStore((s) => s.actions)
useInterval(() => {
if (marginAccount) {
actions.fetchTradeHistory()
}
}, 60000)
const allTrades = []
if (eventQueueFills && eventQueueFills.length > 0) {

View File

@ -8,6 +8,7 @@ import {
SolletExtensionAdapter,
} from '../utils/wallet-adapters'
import { WalletAdapter } from '../@types/types'
import useInterval from './useInterval'
const ASSET_URL =
'https://cdn.jsdelivr.net/gh/solana-labs/oyster@main/assets/wallets'
@ -42,6 +43,7 @@ export default function useWallet() {
providerUrl: selectedProviderUrl,
} = useMangoStore((state) => state.wallet)
const endpoint = useMangoStore((state) => state.connection.endpoint)
const marginAccount = useMangoStore((s) => s.selectedMarginAccount.current)
const actions = useMangoStore((s) => s.actions)
const [savedProviderUrl, setSavedProviderUrl] = useLocalStorageState(
'walletProvider',
@ -89,7 +91,7 @@ export default function useWallet() {
useEffect(() => {
if (!wallet) return
wallet.on('connect', () => {
wallet.on('connect', async () => {
console.log('connected')
setMangoStore((state) => {
@ -103,10 +105,11 @@ export default function useWallet() {
'...' +
wallet.publicKey.toString().substr(-5),
})
actions.fetchMangoGroup()
actions.fetchWalletBalances()
actions.fetchMangoSrmAccounts()
actions.fetchMarginAccounts()
actions.fetchMangoGroup()
await actions.fetchMarginAccounts()
actions.fetchTradeHistory()
})
wallet.on('disconnect', () => {
console.log('on disconnect')
@ -134,5 +137,18 @@ export default function useWallet() {
}
}, [wallet, setMangoStore])
useInterval(() => {
if (connected && marginAccount) {
actions.fetchMarginAccounts()
actions.fetchWalletBalances()
}
}, 15000)
useInterval(() => {
if (connected && marginAccount) {
actions.fetchTradeHistory()
}
}, 60000)
return { connected, wallet }
}

View File

@ -13,6 +13,7 @@ import { AccountInfo, Connection, PublicKey } from '@solana/web3.js'
import { EndpointInfo, WalletAdapter } from '../@types/types'
import { getOwnedTokenAccounts } from '../utils/tokens'
import { isDefined } from '../utils/index'
import { notify } from '../utils/notifications'
export const ENDPOINTS: EndpointInfo[] = [
{
@ -221,7 +222,6 @@ const useMangoStore = create<MangoStore>((set, get) => ({
const cluster = get().connection.cluster
const mangoClient = get().mangoClient
const programId = IDS[cluster].mango_program_id
const actions = get().actions
const set = get().set
if (!wallet?.publicKey || !wallet.publicKey) return
@ -239,7 +239,6 @@ const useMangoStore = create<MangoStore>((set, get) => ({
state.marginAccounts = marginAccounts
state.selectedMarginAccount.current = marginAccounts[0]
})
actions.fetchTradeHistory(marginAccounts[0])
}
})
.catch((err) => {
@ -275,7 +274,12 @@ const useMangoStore = create<MangoStore>((set, get) => ({
})
})
.catch((err) => {
console.error('Could not get mango group: ', err)
notify({
message: 'Could not get mango group: ',
description: `${err}`,
type: 'error',
})
console.log('Could not get mango group: ', err)
})
},
async fetchTradeHistory(marginAccount = null) {

View File

@ -318,9 +318,12 @@ export async function borrow(
quantity: number
): Promise<TransactionSignature> {
const tokenIndex = mangoGroup.getTokenIndex(token)
const nativeQuantity = uiToNative(
quantity,
mangoGroup.mintDecimals[tokenIndex]
// const nativeQuantity = uiToNative(
// quantity,
// mangoGroup.mintDecimals[tokenIndex]
// )
const nativeQuantity = new BN(
Math.floor(quantity * Math.pow(10, mangoGroup.mintDecimals[tokenIndex]))
)
const keys = [
@ -363,6 +366,118 @@ export async function borrow(
})
}
export async function borrowAndWithdraw(
connection: Connection,
programId: PublicKey,
mangoGroup: MangoGroup,
marginAccount: MarginAccount,
wallet: Wallet,
token: PublicKey,
tokenAcc: PublicKey,
withdrawQuantity: number
): Promise<TransactionSignature> {
const transaction = new Transaction()
const tokenIndex = mangoGroup.getTokenIndex(token)
const tokenBalance = marginAccount.getUiDeposit(mangoGroup, tokenIndex)
const borrowQuantity = withdrawQuantity - tokenBalance
console.log('token balance: ', tokenBalance)
console.log('withdraw quantity', withdrawQuantity)
console.log('borrow quantity', borrowQuantity)
const nativeBorrowQuantity = uiToNative(
borrowQuantity,
mangoGroup.mintDecimals[tokenIndex]
)
console.log('nativeBorrowQuantity', nativeBorrowQuantity)
const borrowKeys = [
{ isSigner: false, isWritable: true, pubkey: mangoGroup.publicKey },
{ isSigner: false, isWritable: true, pubkey: marginAccount.publicKey },
{ isSigner: true, isWritable: false, pubkey: wallet.publicKey },
{ isSigner: false, isWritable: false, pubkey: SYSVAR_CLOCK_PUBKEY },
...marginAccount.openOrders.map((pubkey) => ({
isSigner: false,
isWritable: false,
pubkey,
})),
...mangoGroup.oracles.map((pubkey) => ({
isSigner: false,
isWritable: false,
pubkey,
})),
]
const borrowData = encodeMangoInstruction({
Borrow: { tokenIndex: new BN(tokenIndex), quantity: nativeBorrowQuantity },
})
const borrowInstruction = new TransactionInstruction({
keys: borrowKeys,
data: borrowData,
programId,
})
transaction.add(borrowInstruction)
const withdrawKeys = [
{ isSigner: false, isWritable: true, pubkey: mangoGroup.publicKey },
{ isSigner: false, isWritable: true, pubkey: marginAccount.publicKey },
{ isSigner: true, isWritable: false, pubkey: wallet.publicKey },
{ isSigner: false, isWritable: true, pubkey: tokenAcc },
{
isSigner: false,
isWritable: true,
pubkey: mangoGroup.vaults[tokenIndex],
},
{ isSigner: false, isWritable: false, pubkey: mangoGroup.signerKey },
{ isSigner: false, isWritable: false, pubkey: TOKEN_PROGRAM_ID },
{ isSigner: false, isWritable: false, pubkey: SYSVAR_CLOCK_PUBKEY },
...marginAccount.openOrders.map((pubkey) => ({
isSigner: false,
isWritable: false,
pubkey,
})),
...mangoGroup.oracles.map((pubkey) => ({
isSigner: false,
isWritable: false,
pubkey,
})),
]
const nativeWithdrawQuantity = new BN(
Math.floor(
withdrawQuantity * Math.pow(10, mangoGroup.mintDecimals[tokenIndex])
) * 0.98
)
// const nativeWithdrawQuantity = uiToNative(
// withdrawQuantity,
// mangoGroup.mintDecimals[tokenIndex]
// )
const withdrawData = encodeMangoInstruction({
Withdraw: { quantity: nativeWithdrawQuantity },
})
const withdrawInstruction = new TransactionInstruction({
keys: withdrawKeys,
data: withdrawData,
programId,
})
transaction.add(withdrawInstruction)
const signers = []
const functionName = 'Borrow And Withdraw'
const sendingMessage = `Sending ${functionName} instruction...`
const sentMessage = `${functionName} instruction sent`
const successMessage = `${functionName} instruction success`
return await sendTransaction({
transaction,
wallet,
signers,
connection,
sendingMessage,
sentMessage,
successMessage,
})
}
export async function settleBorrow(
connection: Connection,
programId: PublicKey,