implement withdraw max with borrow
This commit is contained in:
parent
a11758784c
commit
74d9f70dbf
|
@ -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') {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
|
@ -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) {
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
121
utils/mango.tsx
121
utils/mango.tsx
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue