fee discount tables and more theming
This commit is contained in:
parent
c076413e80
commit
9e67c147bf
|
@ -5,6 +5,7 @@ import useMarketList from '../hooks/useMarketList'
|
||||||
import { nativeToUi } from '@blockworks-foundation/mango-client/lib/utils'
|
import { nativeToUi } from '@blockworks-foundation/mango-client/lib/utils'
|
||||||
import useMangoStore from '../stores/useMangoStore'
|
import useMangoStore from '../stores/useMangoStore'
|
||||||
import { tokenPrecision } from '../utils/index'
|
import { tokenPrecision } from '../utils/index'
|
||||||
|
import { SRM_DECIMALS } from '@project-serum/serum/lib/token-instructions'
|
||||||
|
|
||||||
const AccountSelect = ({
|
const AccountSelect = ({
|
||||||
accounts,
|
accounts,
|
||||||
|
@ -21,13 +22,15 @@ const AccountSelect = ({
|
||||||
|
|
||||||
const getBalanceForAccount = (account) => {
|
const getBalanceForAccount = (account) => {
|
||||||
const mintAddress = account?.account.mint.toString()
|
const mintAddress = account?.account.mint.toString()
|
||||||
|
const symbol = getSymbolForTokenMintAddress(mintAddress)
|
||||||
const balance = nativeToUi(
|
const balance = nativeToUi(
|
||||||
account?.account?.amount,
|
account?.account?.amount,
|
||||||
mintDecimals[getTokenIndex(mintAddress)]
|
symbol !== 'SRM' ? mintDecimals[getTokenIndex(mintAddress)] : SRM_DECIMALS
|
||||||
)
|
)
|
||||||
const symbol = getSymbolForTokenMintAddress(mintAddress)
|
|
||||||
|
|
||||||
return balance.toFixed(tokenPrecision[symbol])
|
return balance.toFixed(
|
||||||
|
symbol !== 'SRM' ? tokenPrecision[symbol] : SRM_DECIMALS
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -58,44 +58,44 @@ const BalancesTable = () => {
|
||||||
) : null}
|
) : null}
|
||||||
{balances.length ? (
|
{balances.length ? (
|
||||||
<div
|
<div
|
||||||
className={`shadow overflow-hidden border-b border-mango-dark-light sm:rounded-md`}
|
className={`overflow-hidden border-b border-th-bkg-2 sm:rounded-md`}
|
||||||
>
|
>
|
||||||
<table className={`min-w-full divide-y divide-mango-dark-light`}>
|
<table className={`min-w-full divide-y divide-th-bkg-2`}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className={`px-6 py-3 text-left text-base font-medium text-gray-300 tracking-wider`}
|
className={`px-6 py-3 text-left text-base font-medium text-th-fgd-4 tracking-wider`}
|
||||||
>
|
>
|
||||||
Coin
|
Coin
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className={`px-6 py-3 text-left text-base font-medium text-gray-300 tracking-wider`}
|
className={`px-6 py-3 text-left text-base font-medium text-th-fgd-4 tracking-wider`}
|
||||||
>
|
>
|
||||||
Deposits
|
Deposits
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className={`px-6 py-3 text-left text-base font-medium text-gray-300 tracking-wider`}
|
className={`px-6 py-3 text-left text-base font-medium text-th-fgd-4 tracking-wider`}
|
||||||
>
|
>
|
||||||
Borrows
|
Borrows
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className={`px-6 py-3 text-left text-base font-medium text-gray-300 tracking-wider`}
|
className={`px-6 py-3 text-left text-base font-medium text-th-fgd-4 tracking-wider`}
|
||||||
>
|
>
|
||||||
In Orders
|
In Orders
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className={`px-6 py-3 text-left text-base font-medium text-gray-300 tracking-wider`}
|
className={`px-6 py-3 text-left text-base font-medium text-th-fgd-4 tracking-wider`}
|
||||||
>
|
>
|
||||||
Unsettled
|
Unsettled
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className={`px-6 py-3 text-left text-base font-medium text-gray-300 tracking-wider`}
|
className={`px-6 py-3 text-left text-base font-medium text-th-fgd-4 tracking-wider`}
|
||||||
>
|
>
|
||||||
Net
|
Net
|
||||||
</th>
|
</th>
|
||||||
|
@ -106,40 +106,36 @@ const BalancesTable = () => {
|
||||||
<tr
|
<tr
|
||||||
key={`${index}`}
|
key={`${index}`}
|
||||||
className={`
|
className={`
|
||||||
${
|
${index % 2 === 0 ? `bg-th-bkg-1` : `bg-th-bkg-3`}
|
||||||
index % 2 === 0
|
|
||||||
? `bg-mango-dark-light`
|
|
||||||
: `bg-mango-dark-lighter`
|
|
||||||
}
|
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<td
|
<td
|
||||||
className={`px-6 py-4 whitespace-nowrap text-sm text-gray-300 font-light`}
|
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-4 font-light`}
|
||||||
>
|
>
|
||||||
{balance.coin}
|
{balance.coin}
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
className={`px-6 py-4 whitespace-nowrap text-sm text-gray-300 font-light`}
|
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-4 font-light`}
|
||||||
>
|
>
|
||||||
{balance.marginDeposits}
|
{balance.marginDeposits}
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
className={`px-6 py-4 whitespace-nowrap text-sm text-gray-300 font-light`}
|
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-4 font-light`}
|
||||||
>
|
>
|
||||||
{balance.borrows}
|
{balance.borrows}
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
className={`px-6 py-4 whitespace-nowrap text-sm text-gray-300 font-light`}
|
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-4 font-light`}
|
||||||
>
|
>
|
||||||
{balance.orders}
|
{balance.orders}
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
className={`px-6 py-4 whitespace-nowrap text-sm text-gray-300 font-light`}
|
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-4 font-light`}
|
||||||
>
|
>
|
||||||
{balance.unsettled}
|
{balance.unsettled}
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
className={`px-6 py-4 whitespace-nowrap text-sm text-gray-300 font-light`}
|
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-4 font-light`}
|
||||||
>
|
>
|
||||||
{balance.net}
|
{balance.net}
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -10,12 +10,13 @@ import { deposit, initMarginAccountAndDeposit } from '../utils/mango'
|
||||||
import { PublicKey } from '@solana/web3.js'
|
import { PublicKey } from '@solana/web3.js'
|
||||||
import Loading from './Loading'
|
import Loading from './Loading'
|
||||||
import Button from './Button'
|
import Button from './Button'
|
||||||
|
import { notify } from '../utils/notifications'
|
||||||
|
import { XIcon } from '@heroicons/react/outline'
|
||||||
|
|
||||||
const DepositModal = ({ isOpen, onClose }) => {
|
const DepositModal = ({ isOpen, onClose }) => {
|
||||||
const [inputAmount, setInputAmount] = useState('')
|
const [inputAmount, setInputAmount] = useState('')
|
||||||
const [submitting, setSubmitting] = useState(false)
|
const [submitting, setSubmitting] = useState(false)
|
||||||
const { symbols } = useMarketList()
|
const { getTokenIndex, symbols } = useMarketList()
|
||||||
const { getTokenIndex } = useMarketList()
|
|
||||||
const { connection, programId } = useConnection()
|
const { connection, programId } = useConnection()
|
||||||
const mintDecimals = useMangoStore((s) => s.selectedMangoGroup.mintDecimals)
|
const mintDecimals = useMangoStore((s) => s.selectedMangoGroup.mintDecimals)
|
||||||
const walletAccounts = useMangoStore((s) => s.wallet.balances)
|
const walletAccounts = useMangoStore((s) => s.wallet.balances)
|
||||||
|
@ -64,13 +65,20 @@ const DepositModal = ({ isOpen, onClose }) => {
|
||||||
.then((response: Array<any>) => {
|
.then((response: Array<any>) => {
|
||||||
actions.fetchWalletBalances()
|
actions.fetchWalletBalances()
|
||||||
setSubmitting(false)
|
setSubmitting(false)
|
||||||
console.log('success', response)
|
notify({
|
||||||
|
message: `Deposited ${inputAmount} into your account`,
|
||||||
|
description: `Hash of transaction is ${response[1]}`,
|
||||||
|
})
|
||||||
onClose()
|
onClose()
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setSubmitting(false)
|
setSubmitting(false)
|
||||||
console.error(err)
|
console.error(err)
|
||||||
alert('error depositing')
|
notify({
|
||||||
|
message:
|
||||||
|
'Could not perform init margin account and deposit operation',
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
onClose()
|
onClose()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -87,13 +95,19 @@ const DepositModal = ({ isOpen, onClose }) => {
|
||||||
.then((response: string) => {
|
.then((response: string) => {
|
||||||
actions.fetchWalletBalances()
|
actions.fetchWalletBalances()
|
||||||
setSubmitting(false)
|
setSubmitting(false)
|
||||||
console.log('success', response)
|
notify({
|
||||||
|
message: `Deposited ${inputAmount} into your account`,
|
||||||
|
description: `Hash of transaction is ${response}`,
|
||||||
|
})
|
||||||
onClose()
|
onClose()
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setSubmitting(false)
|
setSubmitting(false)
|
||||||
console.error(err)
|
console.error(err)
|
||||||
alert('error depositing')
|
notify({
|
||||||
|
message: 'Could not perform deposit operation',
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
onClose()
|
onClose()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -102,9 +116,9 @@ const DepositModal = ({ isOpen, onClose }) => {
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={isOpen} onClose={onClose}>
|
<Modal isOpen={isOpen} onClose={onClose}>
|
||||||
<Modal.Header>
|
<Modal.Header>
|
||||||
<div className={`text-mango-med-light flex-shrink invisible`}>X</div>
|
<div className={`text-th-fgd-3 flex-shrink invisible`}>X</div>
|
||||||
<div
|
<div
|
||||||
className={`text-mango-med-light flex-grow text-center flex items-center justify-center`}
|
className={`text-th-fgd-3 flex-grow text-center flex items-center justify-center`}
|
||||||
>
|
>
|
||||||
<div className={`flex-initial`}>Select: </div>
|
<div className={`flex-initial`}>Select: </div>
|
||||||
<div className={`ml-4 flex-grow`}>
|
<div className={`ml-4 flex-grow`}>
|
||||||
|
@ -115,21 +129,13 @@ const DepositModal = ({ isOpen, onClose }) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={`text-th-fgd-4 flex-shrink ml-6 mr-2 text-lg`}>
|
|
||||||
<button onClick={onClose} className={`hover:text-mango-yellow`}>
|
<button
|
||||||
<svg
|
onClick={onClose}
|
||||||
viewBox="64 64 896 896"
|
className={`text-th-fgd-3 mr-2 ml-4 hover:text-th-primary`}
|
||||||
focusable="false"
|
>
|
||||||
data-icon="close"
|
<XIcon className={`h-6 w-6`} />
|
||||||
width="1em"
|
</button>
|
||||||
height="1em"
|
|
||||||
fill="currentColor"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</Modal.Header>
|
</Modal.Header>
|
||||||
<div className={`pb-6 px-8`}>
|
<div className={`pb-6 px-8`}>
|
||||||
<div className={`mt-3 text-center sm:mt-5`}>
|
<div className={`mt-3 text-center sm:mt-5`}>
|
||||||
|
@ -146,7 +152,7 @@ const DepositModal = ({ isOpen, onClose }) => {
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
className={`outline-none bg-th-bkg-3 w-full py-4 mx-3 text-2xl text-th-fgd-4 flex-grow`}
|
className={`outline-none bg-th-bkg-3 w-full py-4 mx-3 text-2xl text-th-fgd-2 flex-grow`}
|
||||||
placeholder="0.00"
|
placeholder="0.00"
|
||||||
value={inputAmount}
|
value={inputAmount}
|
||||||
onChange={(e) => setInputAmount(e.target.value)}
|
onChange={(e) => setInputAmount(e.target.value)}
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
import React, { useMemo, useState } from 'react'
|
||||||
|
import { nativeToUi } from '@blockworks-foundation/mango-client/lib/utils'
|
||||||
|
import Modal from './Modal'
|
||||||
|
import AccountSelect from './AccountSelect'
|
||||||
|
import useMangoStore from '../stores/useMangoStore'
|
||||||
|
import { getSymbolForTokenMintAddress } from '../utils/index'
|
||||||
|
import useConnection from '../hooks/useConnection'
|
||||||
|
import { depositSrm } from '../utils/mango'
|
||||||
|
import { PublicKey } from '@solana/web3.js'
|
||||||
|
import Loading from './Loading'
|
||||||
|
import Button from './Button'
|
||||||
|
import { notify } from '../utils/notifications'
|
||||||
|
import { SRM_DECIMALS } from '@project-serum/serum/lib/token-instructions'
|
||||||
|
import { XIcon } from '@heroicons/react/outline'
|
||||||
|
|
||||||
|
const DepositSrmModal = ({ isOpen, onClose }) => {
|
||||||
|
const [inputAmount, setInputAmount] = useState('')
|
||||||
|
const [submitting, setSubmitting] = useState(false)
|
||||||
|
const { connection, programId } = useConnection()
|
||||||
|
const walletAccounts = useMangoStore((s) => s.wallet.balances)
|
||||||
|
const actions = useMangoStore((s) => s.actions)
|
||||||
|
const srmMintAddress = useMangoStore((s) => s.connection.srmMint)
|
||||||
|
const mangoSrmAccountsForOwner = useMangoStore(
|
||||||
|
(s) => s.selectedMangoGroup.srmAccountsForOwner
|
||||||
|
)
|
||||||
|
const depositAccounts = useMemo(
|
||||||
|
() =>
|
||||||
|
walletAccounts.filter(
|
||||||
|
(acc) => srmMintAddress === acc.account.mint.toString()
|
||||||
|
),
|
||||||
|
[walletAccounts, srmMintAddress]
|
||||||
|
)
|
||||||
|
const [selectedAccount, setSelectedAccount] = useState(depositAccounts[0])
|
||||||
|
|
||||||
|
// TODO: remove duplication in AccountSelect
|
||||||
|
const getBalanceForAccount = (account) => {
|
||||||
|
const balance = nativeToUi(account?.account?.amount, SRM_DECIMALS)
|
||||||
|
|
||||||
|
return balance.toFixed(SRM_DECIMALS)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setMaxForSelectedAccount = () => {
|
||||||
|
const max = getBalanceForAccount(selectedAccount)
|
||||||
|
setInputAmount(max)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDeposit = () => {
|
||||||
|
setSubmitting(true)
|
||||||
|
const marginAccount = useMangoStore.getState().selectedMarginAccount.current
|
||||||
|
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
|
||||||
|
const wallet = useMangoStore.getState().wallet.current
|
||||||
|
if (marginAccount && mangoGroup) {
|
||||||
|
depositSrm(
|
||||||
|
connection,
|
||||||
|
new PublicKey(programId),
|
||||||
|
mangoGroup,
|
||||||
|
wallet,
|
||||||
|
selectedAccount.publicKey,
|
||||||
|
Number(inputAmount),
|
||||||
|
mangoSrmAccountsForOwner?.length
|
||||||
|
? mangoSrmAccountsForOwner[0].publicKey
|
||||||
|
: undefined
|
||||||
|
)
|
||||||
|
.then((_mangoSrmAcct: PublicKey) => {
|
||||||
|
setSubmitting(false)
|
||||||
|
actions.fetchMangoSrmAccounts()
|
||||||
|
actions.fetchWalletBalances()
|
||||||
|
actions.fetchMangoGroup()
|
||||||
|
notify({
|
||||||
|
message: `Deposited ${inputAmount} SRM into your account`,
|
||||||
|
description: ``,
|
||||||
|
type: 'info',
|
||||||
|
})
|
||||||
|
onClose()
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
setSubmitting(false)
|
||||||
|
console.error(err)
|
||||||
|
notify({
|
||||||
|
message: 'Could not perform SRM deposit operation',
|
||||||
|
description: '',
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
onClose()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} onClose={onClose}>
|
||||||
|
<Modal.Header>
|
||||||
|
<div className={`text-th-fgd-3 flex-shrink invisible`}>X</div>
|
||||||
|
<div
|
||||||
|
className={`text-th-fgd-3 flex-grow text-center flex items-center justify-center`}
|
||||||
|
>
|
||||||
|
<div className={`flex-initial`}>Select: </div>
|
||||||
|
<div className={`ml-4 flex-grow`}>
|
||||||
|
<AccountSelect
|
||||||
|
accounts={depositAccounts}
|
||||||
|
selectedAccount={selectedAccount}
|
||||||
|
onSelectAccount={setSelectedAccount}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className={`text-th-fgd-3 mr-2 ml-4 hover:text-th-primary`}
|
||||||
|
>
|
||||||
|
<XIcon className={`h-6 w-6`} />
|
||||||
|
</button>
|
||||||
|
</Modal.Header>
|
||||||
|
<div className={`pb-6 px-8`}>
|
||||||
|
<div className={`mt-3 text-center sm:mt-5`}>
|
||||||
|
<div className={`mt-6 bg-th-bkg-3 rounded-md flex items-center`}>
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
src={`/assets/icons/${getSymbolForTokenMintAddress(
|
||||||
|
selectedAccount?.account?.mint.toString()
|
||||||
|
).toLowerCase()}.svg`}
|
||||||
|
className={`ml-3`}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
className={`outline-none bg-th-bkg-3 w-full py-4 mx-3 text-2xl text-th-fgd-2 flex-grow`}
|
||||||
|
placeholder="0.00"
|
||||||
|
value={inputAmount}
|
||||||
|
onChange={(e) => setInputAmount(e.target.value)}
|
||||||
|
></input>
|
||||||
|
<Button
|
||||||
|
onClick={setMaxForSelectedAccount}
|
||||||
|
className={`m-2 rounded py-1`}
|
||||||
|
>
|
||||||
|
Max
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={`mt-5 sm:mt-6 flex justify-center`}>
|
||||||
|
<Button onClick={handleDeposit}>
|
||||||
|
<div className={`flex items-center`}>
|
||||||
|
{submitting && <Loading />}
|
||||||
|
Deposit
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(DepositSrmModal)
|
|
@ -6,10 +6,6 @@ const DropdownExample = () => {
|
||||||
const { spotMarkets } = useMarketList()
|
const { spotMarkets } = useMarketList()
|
||||||
const { marketName } = useMarket()
|
const { marketName } = useMarket()
|
||||||
|
|
||||||
// const handleChange = (e) => {
|
|
||||||
// console.log('new market selected', e)
|
|
||||||
// }
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`ml-4`}>
|
<div className={`ml-4`}>
|
||||||
<Menu>
|
<Menu>
|
||||||
|
|
|
@ -1,11 +1,27 @@
|
||||||
|
import { useCallback, useState } from 'react'
|
||||||
import { percentFormat } from '../utils/index'
|
import { percentFormat } from '../utils/index'
|
||||||
import useSrmAccount from '../hooks/useSrmAccount'
|
import useSrmAccount from '../hooks/useSrmAccount'
|
||||||
import useMangoStore from '../stores/useMangoStore'
|
import useMangoStore from '../stores/useMangoStore'
|
||||||
import Button from './Button'
|
import Button from './Button'
|
||||||
|
import DepositSrmModal from './DepositSrmModal'
|
||||||
|
import WithdrawSrmModal from './WithdrawSrmModal'
|
||||||
|
|
||||||
const FeeDiscountsTable = () => {
|
const FeeDiscountsTable = () => {
|
||||||
|
const [showDeposit, setShowDeposit] = useState(false)
|
||||||
|
const [showWithdraw, setShowWithdraw] = useState(false)
|
||||||
const { totalSrm, rates } = useSrmAccount()
|
const { totalSrm, rates } = useSrmAccount()
|
||||||
const connected = useMangoStore((s) => s.wallet.connected)
|
const connected = useMangoStore((s) => s.wallet.connected)
|
||||||
|
const contributedSrm = useMangoStore(
|
||||||
|
(s) => s.selectedMangoGroup.contributedSrm
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleCloseDeposit = useCallback(() => {
|
||||||
|
setShowDeposit(false)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleCloseWithdraw = useCallback(() => {
|
||||||
|
setShowWithdraw(false)
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -16,9 +32,7 @@ const FeeDiscountsTable = () => {
|
||||||
>
|
>
|
||||||
<div className="px-4">
|
<div className="px-4">
|
||||||
<div>Total SRM in Mango</div>
|
<div>Total SRM in Mango</div>
|
||||||
<div className="text-th-fgd-1 text-lg font-semibold">
|
<div className="text-th-fgd-1 text-lg font-semibold">{totalSrm}</div>
|
||||||
{totalSrm.toFixed(0)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="px-4 mt-4 sm:mt-0">
|
<div className="px-4 mt-4 sm:mt-0">
|
||||||
<div>Maker Fee</div>
|
<div>Maker Fee</div>
|
||||||
|
@ -37,17 +51,23 @@ const FeeDiscountsTable = () => {
|
||||||
{connected ? (
|
{connected ? (
|
||||||
<div className="bg-th-bkg-2 p-6">
|
<div className="bg-th-bkg-2 p-6">
|
||||||
<div className="text-th-fgd-4 text-center">
|
<div className="text-th-fgd-4 text-center">
|
||||||
Your contributed SRM: 0
|
Your contributed SRM: {contributedSrm}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex space-x-4 mt-8">
|
<div className="flex space-x-4 mt-8">
|
||||||
<Button>Deposit</Button>
|
<Button onClick={() => setShowDeposit(true)}>Deposit</Button>
|
||||||
<Button>Withdraw</Button>
|
<Button onClick={() => setShowWithdraw(true)}>Withdraw</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Button disabled>Connect wallet to deposit SRM</Button>
|
<Button disabled>Connect wallet to deposit SRM</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{showDeposit && (
|
||||||
|
<DepositSrmModal isOpen={showDeposit} onClose={handleCloseDeposit} />
|
||||||
|
)}
|
||||||
|
{showWithdraw && (
|
||||||
|
<WithdrawSrmModal isOpen={showWithdraw} onClose={handleCloseWithdraw} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
import { Listbox } from '@headlessui/react'
|
||||||
|
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/solid'
|
||||||
|
import { abbreviateAddress } from '../utils'
|
||||||
|
import { nativeToUi } from '@blockworks-foundation/mango-client/lib/utils'
|
||||||
|
import { SRM_DECIMALS } from '@project-serum/serum/lib/token-instructions'
|
||||||
|
|
||||||
|
const MangoSrmAccountSelector = ({
|
||||||
|
accounts,
|
||||||
|
selectedAccount,
|
||||||
|
onSelectAccount,
|
||||||
|
}) => {
|
||||||
|
const handleChange = (value) => {
|
||||||
|
const newAccount = accounts.find((a) => a.publicKey.toString() === value)
|
||||||
|
onSelectAccount(newAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getBalanceForAccount = (account) => {
|
||||||
|
const balance = nativeToUi(account.amount, SRM_DECIMALS)
|
||||||
|
return balance.toFixed(SRM_DECIMALS)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`relative inline-block w-full`}>
|
||||||
|
<Listbox
|
||||||
|
value={selectedAccount?.publicKey.toString()}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
{({ open }) => (
|
||||||
|
<>
|
||||||
|
<Listbox.Button
|
||||||
|
className={`border border-th-fgd-4 focus:outline-none focus:ring-1 focus:ring-mango-yellow p-2 w-full`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`flex items-center text-base justify-between font-light`}
|
||||||
|
>
|
||||||
|
<div className={`flex items-center flex-grow`}>
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
src={`/assets/icons/SRM.svg`}
|
||||||
|
className={`mr-4`}
|
||||||
|
/>
|
||||||
|
{abbreviateAddress(selectedAccount?.publicKey)}
|
||||||
|
<div className={`ml-4 text-sm text-right flex-grow`}>
|
||||||
|
({getBalanceForAccount(selectedAccount)})
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{open ? (
|
||||||
|
<ChevronUpIcon className={`h-5 w-5 ml-2`} />
|
||||||
|
) : (
|
||||||
|
<ChevronDownIcon className={`h-5 w-5 ml-2`} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Listbox.Button>
|
||||||
|
{open ? (
|
||||||
|
<Listbox.Options
|
||||||
|
static
|
||||||
|
className={`z-20 p-1 absolute left-0 w-full mt-1 bg-th-bkg-3 origin-top-left divide-y divide-th-fgd-4 shadow-lg outline-none border border-th-fgd-4`}
|
||||||
|
>
|
||||||
|
<div className={`opacity-50 p-2`}>Your Mango SRM Accounts</div>
|
||||||
|
{accounts.map((account) => {
|
||||||
|
return (
|
||||||
|
<Listbox.Option
|
||||||
|
key={account?.publicKey.toString()}
|
||||||
|
value={account?.publicKey.toString()}
|
||||||
|
>
|
||||||
|
{({ selected }) => (
|
||||||
|
<div
|
||||||
|
className={`p-2 text-sm hover:bg-th-fgd-4 hover:cursor-pointer tracking-wider font-light ${
|
||||||
|
selected && 'text-mango-yellow bg-th-fgd-4'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className={`flex items-center space-x-2`}>
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
src={`/assets/icons/SRM.svg`}
|
||||||
|
/>
|
||||||
|
<div className={`flex-grow text-left`}>
|
||||||
|
{abbreviateAddress(account?.publicKey)}
|
||||||
|
</div>
|
||||||
|
<div className={`text-sm`}>
|
||||||
|
{getBalanceForAccount(account)} (SRM)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Listbox.Option>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Listbox.Options>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Listbox>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MangoSrmAccountSelector
|
|
@ -24,12 +24,14 @@ const NotificationList = () => {
|
||||||
}
|
}
|
||||||
}, [notifications, setMangoStore])
|
}, [notifications, setMangoStore])
|
||||||
|
|
||||||
|
const reversedNotifications = [...notifications].reverse()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`fixed inset-0 flex items-end px-4 py-6 pointer-events-none sm:p-6`}
|
className={`fixed inset-0 flex items-end px-4 py-6 pointer-events-none sm:p-6`}
|
||||||
>
|
>
|
||||||
<div className={`flex flex-col w-full`}>
|
<div className={`flex flex-col w-full`}>
|
||||||
{notifications.map((n, idx) => (
|
{reversedNotifications.map((n, idx) => (
|
||||||
<Notification
|
<Notification
|
||||||
key={`${n.message}${idx}`}
|
key={`${n.message}${idx}`}
|
||||||
type={n.type}
|
type={n.type}
|
||||||
|
@ -50,9 +52,9 @@ const Notification = ({ type, message, description, txid }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`max-w-sm w-full bg-white shadow-lg rounded-lg mt-2 pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden`}
|
className={`max-w-sm w-full bg-white opacity-80 shadow-lg rounded-lg mt-2 pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden`}
|
||||||
>
|
>
|
||||||
<div className={`p-4`}>
|
<div className={`px-4 py-2`}>
|
||||||
<div className={`flex items-start`}>
|
<div className={`flex items-start`}>
|
||||||
<div className={`flex-shrink-0`}>
|
<div className={`flex-shrink-0`}>
|
||||||
{type === 'success' ? (
|
{type === 'success' ? (
|
||||||
|
@ -66,8 +68,10 @@ const Notification = ({ type, message, description, txid }) => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={`ml-2 w-0 flex-1`}>
|
<div className={`ml-2 w-0 flex-1`}>
|
||||||
<p className={`text-lg font-medium text-gray-900`}>{message}</p>
|
<div className={`text-lg font-medium text-gray-900`}>{message}</div>
|
||||||
<p className={`mt-0.5 text-base text-gray-500`}>{description}</p>
|
{description ? (
|
||||||
|
<p className={`mt-0.5 text-base text-gray-500`}>{description}</p>
|
||||||
|
) : null}
|
||||||
{txid ? (
|
{txid ? (
|
||||||
<a
|
<a
|
||||||
href={'https://explorer.solana.com/tx/' + txid}
|
href={'https://explorer.solana.com/tx/' + txid}
|
||||||
|
|
|
@ -1,8 +1,52 @@
|
||||||
|
import { useState } from 'react'
|
||||||
import { TrashIcon } from '@heroicons/react/solid'
|
import { TrashIcon } from '@heroicons/react/solid'
|
||||||
import { useOpenOrders } from '../hooks/useOpenOrders'
|
import { useOpenOrders } from '../hooks/useOpenOrders'
|
||||||
|
import { cancelOrderAndSettle } from '../utils/mango'
|
||||||
|
import Button from './Button'
|
||||||
|
import Loading from './Loading'
|
||||||
|
import { PublicKey } from '@solana/web3.js'
|
||||||
|
import useConnection from '../hooks/useConnection'
|
||||||
|
import useMangoStore from '../stores/useMangoStore'
|
||||||
|
import { notify } from '../utils/notifications'
|
||||||
|
|
||||||
const OpenOrdersTable = () => {
|
const OpenOrdersTable = () => {
|
||||||
const openOrders = useOpenOrders()
|
const openOrders = useOpenOrders()
|
||||||
|
const [cancelId, setCancelId] = useState(null)
|
||||||
|
const { connection, programId } = useConnection()
|
||||||
|
|
||||||
|
const handleCancelOrder = async (order) => {
|
||||||
|
const wallet = useMangoStore.getState().wallet.current
|
||||||
|
const selectedMangoGroup = useMangoStore.getState().selectedMangoGroup
|
||||||
|
.current
|
||||||
|
const selectedMarginAccount = useMangoStore.getState().selectedMarginAccount
|
||||||
|
.current
|
||||||
|
setCancelId(order?.orderId)
|
||||||
|
try {
|
||||||
|
if (!selectedMangoGroup || !selectedMarginAccount) return
|
||||||
|
await cancelOrderAndSettle(
|
||||||
|
connection,
|
||||||
|
new PublicKey(programId),
|
||||||
|
selectedMangoGroup,
|
||||||
|
selectedMarginAccount,
|
||||||
|
wallet,
|
||||||
|
order.market,
|
||||||
|
order
|
||||||
|
)
|
||||||
|
notify({
|
||||||
|
message: 'Order cancelled',
|
||||||
|
type: 'success',
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
notify({
|
||||||
|
message: 'Error cancelling order',
|
||||||
|
description: e.message,
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
} finally {
|
||||||
|
setCancelId(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex flex-col py-6`}>
|
<div className={`flex flex-col py-6`}>
|
||||||
|
@ -10,32 +54,32 @@ const OpenOrdersTable = () => {
|
||||||
<div className={`align-middle inline-block min-w-full sm:px-6 lg:px-8`}>
|
<div className={`align-middle inline-block min-w-full sm:px-6 lg:px-8`}>
|
||||||
{openOrders && openOrders.length > 0 ? (
|
{openOrders && openOrders.length > 0 ? (
|
||||||
<div
|
<div
|
||||||
className={`shadow overflow-hidden border-b border-mango-dark-light sm:rounded-md`}
|
className={`shadow overflow-hidden border-b border-th-bkg-2 sm:rounded-md`}
|
||||||
>
|
>
|
||||||
<table className={`min-w-full divide-y divide-mango-dark-light`}>
|
<table className={`min-w-full divide-y divide-th-bkg-2`}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className={`px-6 py-3 text-left text-base font-medium text-gray-300 tracking-wider`}
|
className={`px-6 py-3 text-left text-base font-medium text-th-fgd-4 tracking-wider`}
|
||||||
>
|
>
|
||||||
Market
|
Market
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className={`px-6 py-3 text-left text-base font-medium text-gray-300 tracking-wider`}
|
className={`px-6 py-3 text-left text-base font-medium text-th-fgd-4 tracking-wider`}
|
||||||
>
|
>
|
||||||
Side
|
Side
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className={`px-6 py-3 text-left text-base font-medium text-gray-300 tracking-wider`}
|
className={`px-6 py-3 text-left text-base font-medium text-th-fgd-4 tracking-wider`}
|
||||||
>
|
>
|
||||||
Size
|
Size
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className={`px-6 py-3 text-left text-base font-medium text-gray-300 tracking-wider`}
|
className={`px-6 py-3 text-left text-base font-medium text-th-fgd-4 tracking-wider`}
|
||||||
>
|
>
|
||||||
Price
|
Price
|
||||||
</th>
|
</th>
|
||||||
|
@ -49,20 +93,16 @@ const OpenOrdersTable = () => {
|
||||||
<tr
|
<tr
|
||||||
key={`${order.orderId}${order.side}`}
|
key={`${order.orderId}${order.side}`}
|
||||||
className={`
|
className={`
|
||||||
${
|
${index % 2 === 0 ? `bg-th-bkg-1` : `bg-th-bkg-3`}
|
||||||
index % 2 === 0
|
|
||||||
? `bg-mango-dark-light`
|
|
||||||
: `bg-mango-dark-lighter`
|
|
||||||
}
|
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<td
|
<td
|
||||||
className={`px-6 py-4 whitespace-nowrap text-sm text-gray-300 font-light`}
|
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-4 font-light`}
|
||||||
>
|
>
|
||||||
{order.marketName}
|
{order.marketName}
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
className={`px-6 py-4 whitespace-nowrap text-sm text-gray-300 font-light`}
|
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-4 font-light`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`rounded inline-block bg-mango-green px-2 py-1 text-mango-dark font-bold`}
|
className={`rounded inline-block bg-mango-green px-2 py-1 text-mango-dark font-bold`}
|
||||||
|
@ -71,25 +111,29 @@ const OpenOrdersTable = () => {
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
className={`px-6 py-4 whitespace-nowrap text-sm text-gray-300 font-light`}
|
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-4 font-light`}
|
||||||
>
|
>
|
||||||
{order.size}
|
{order.size}
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
className={`px-6 py-4 whitespace-nowrap text-sm text-gray-300 font-light`}
|
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-4 font-light`}
|
||||||
>
|
>
|
||||||
{order.price}
|
{order.price}
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
className={`px-6 py-4 opacity-75 whitespace-nowrap text-right text-sm font-medium`}
|
className={`px-6 py-4 opacity-75 whitespace-nowrap text-right text-sm font-medium`}
|
||||||
>
|
>
|
||||||
<button
|
<Button
|
||||||
onClick={() => alert('cancel order')}
|
onClick={() => handleCancelOrder(order)}
|
||||||
className={`flex items-center ml-auto text-mango-red border border-mango-red hover:text-red-700 hover:border-red-700 py-1 px-4`}
|
className={`flex items-center ml-auto rounded text-th-red border border-th-red hover:text-th-red hover:border-th-red py-1`}
|
||||||
>
|
>
|
||||||
<TrashIcon className={`h-5 w-5 mr-1`} />
|
{cancelId + '' === order?.orderId + '' ? (
|
||||||
|
<Loading />
|
||||||
|
) : (
|
||||||
|
<TrashIcon className={`h-5 w-5 mr-1`} />
|
||||||
|
)}
|
||||||
<span>Cancel Order</span>
|
<span>Cancel Order</span>
|
||||||
</button>
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
// import styled from '@emotion/styled'
|
|
||||||
import { getDecimalCount } from '../utils'
|
import { getDecimalCount } from '../utils'
|
||||||
import { ChartTradeType } from '../@types/types'
|
import { ChartTradeType } from '../@types/types'
|
||||||
import FloatingElement from './FloatingElement'
|
import FloatingElement from './FloatingElement'
|
||||||
|
@ -13,13 +12,24 @@ export default function PublicTrades() {
|
||||||
const { baseCurrency, quoteCurrency, market, marketAddress } = useMarket()
|
const { baseCurrency, quoteCurrency, market, marketAddress } = useMarket()
|
||||||
const [trades, setTrades] = useState([])
|
const [trades, setTrades] = useState([])
|
||||||
|
|
||||||
useInterval(async () => {
|
const fetchTradesForChart = useCallback(async () => {
|
||||||
const newTrades = await ChartApi.getRecentTrades(marketAddress)
|
const newTrades = await ChartApi.getRecentTrades(marketAddress)
|
||||||
if (trades.length === 0) {
|
if (trades.length === 0) {
|
||||||
setTrades(newTrades)
|
setTrades(newTrades)
|
||||||
} else if (!isEqual(newTrades[0], trades[0], Object.keys(newTrades[0]))) {
|
} else if (
|
||||||
|
newTrades &&
|
||||||
|
!isEqual(newTrades[0], trades[0], Object.keys(newTrades[0]))
|
||||||
|
) {
|
||||||
setTrades(newTrades)
|
setTrades(newTrades)
|
||||||
}
|
}
|
||||||
|
}, [marketAddress, trades])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchTradesForChart()
|
||||||
|
}, [fetchTradesForChart])
|
||||||
|
|
||||||
|
useInterval(async () => {
|
||||||
|
fetchTradesForChart
|
||||||
}, 5000)
|
}, 5000)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -15,16 +15,11 @@ import TradeType from './TradeType'
|
||||||
import NewInput from './Input'
|
import NewInput from './Input'
|
||||||
import NewSwitch from './Switch'
|
import NewSwitch from './Switch'
|
||||||
|
|
||||||
export default function TradeForm({
|
export default function TradeForm() {
|
||||||
setChangeOrderRef,
|
|
||||||
}: {
|
|
||||||
setChangeOrderRef?: (
|
|
||||||
ref: ({ size, price }: { size?: number; price?: number }) => void
|
|
||||||
) => void
|
|
||||||
}) {
|
|
||||||
const { baseCurrency, quoteCurrency, market, marketAddress } = useMarket()
|
const { baseCurrency, quoteCurrency, market, marketAddress } = useMarket()
|
||||||
const set = useMangoStore((s) => s.set)
|
const set = useMangoStore((s) => s.set)
|
||||||
const { connected } = useMangoStore((s) => s.wallet)
|
const { connected } = useMangoStore((s) => s.wallet)
|
||||||
|
const actions = useMangoStore((s) => s.actions)
|
||||||
const { connection, cluster } = useConnection()
|
const { connection, cluster } = useConnection()
|
||||||
const { side, baseSize, quoteSize, price, tradeType } = useMangoStore(
|
const { side, baseSize, quoteSize, price, tradeType } = useMangoStore(
|
||||||
(s) => s.tradeForm
|
(s) => s.tradeForm
|
||||||
|
@ -83,22 +78,21 @@ export default function TradeForm({
|
||||||
|
|
||||||
const sizeDecimalCount =
|
const sizeDecimalCount =
|
||||||
market?.minOrderSize && getDecimalCount(market.minOrderSize)
|
market?.minOrderSize && getDecimalCount(market.minOrderSize)
|
||||||
const priceDecimalCount = market?.tickSize && getDecimalCount(market.tickSize)
|
// const priceDecimalCount = market?.tickSize && getDecimalCount(market.tickSize)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setChangeOrderRef && setChangeOrderRef(doChangeOrder)
|
onSetBaseSize(baseSize)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [price, baseSize])
|
||||||
}, [setChangeOrderRef])
|
|
||||||
|
|
||||||
const onSetBaseSize = (baseSize: number | undefined) => {
|
const onSetBaseSize = (baseSize: number | '') => {
|
||||||
setBaseSize(baseSize)
|
setBaseSize(baseSize)
|
||||||
if (!baseSize) {
|
if (!baseSize) {
|
||||||
setQuoteSize(undefined)
|
setQuoteSize('')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const usePrice = price || markPrice
|
const usePrice = Number(price) || markPrice
|
||||||
if (!usePrice) {
|
if (!usePrice) {
|
||||||
setQuoteSize(undefined)
|
setQuoteSize('')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const rawQuoteSize = baseSize * usePrice
|
const rawQuoteSize = baseSize * usePrice
|
||||||
|
@ -106,15 +100,16 @@ export default function TradeForm({
|
||||||
setQuoteSize(quoteSize)
|
setQuoteSize(quoteSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSetQuoteSize = (quoteSize: number | undefined) => {
|
const onSetQuoteSize = (quoteSize: number | '') => {
|
||||||
setQuoteSize(quoteSize)
|
setQuoteSize(quoteSize)
|
||||||
if (!quoteSize) {
|
if (!quoteSize) {
|
||||||
setBaseSize(undefined)
|
setBaseSize('')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const usePrice = price || markPrice
|
const usePrice = Number(price) || markPrice
|
||||||
|
|
||||||
if (!usePrice) {
|
if (!usePrice) {
|
||||||
setBaseSize(undefined)
|
setBaseSize('')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const rawBaseSize = quoteSize / usePrice
|
const rawBaseSize = quoteSize / usePrice
|
||||||
|
@ -122,19 +117,6 @@ export default function TradeForm({
|
||||||
setBaseSize(baseSize)
|
setBaseSize(baseSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
const doChangeOrder = ({
|
|
||||||
size,
|
|
||||||
price,
|
|
||||||
}: {
|
|
||||||
size?: number
|
|
||||||
price?: number
|
|
||||||
}) => {
|
|
||||||
const formattedSize = size && roundToDecimal(size, sizeDecimalCount)
|
|
||||||
const formattedPrice = price && roundToDecimal(price, priceDecimalCount)
|
|
||||||
formattedSize && onSetBaseSize(formattedSize)
|
|
||||||
formattedPrice && setPrice(formattedPrice)
|
|
||||||
}
|
|
||||||
|
|
||||||
const postOnChange = (checked) => {
|
const postOnChange = (checked) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
setIoc(false)
|
setIoc(false)
|
||||||
|
@ -195,8 +177,9 @@ export default function TradeForm({
|
||||||
)
|
)
|
||||||
console.log('Successfully placed trade!')
|
console.log('Successfully placed trade!')
|
||||||
|
|
||||||
setPrice(undefined)
|
setPrice('')
|
||||||
onSetBaseSize(undefined)
|
onSetBaseSize('')
|
||||||
|
actions.fetchMarginAcccount()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
notify({
|
notify({
|
||||||
message: 'Error placing order',
|
message: 'Error placing order',
|
||||||
|
@ -318,7 +301,7 @@ export default function TradeForm({
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
className="rounded text-lg bg-th-green text-th-bkg-1 hover:bg-th-primary flex-grow"
|
className="rounded text-lg bg-th-green text-th-bkg-1 hover:bg-th-primary flex-grow"
|
||||||
>
|
>
|
||||||
{connected ? `Buy ${baseCurrency}` : 'CONNECT WALLET TO TRADE'}
|
{connected ? `Buy ${baseCurrency}` : 'Connect Wallet To Trade'}
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
|
@ -331,7 +314,7 @@ export default function TradeForm({
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
className="rounded text-lg bg-th-red text-white hover:bg-th-primary flex-grow"
|
className="rounded text-lg bg-th-red text-white hover:bg-th-primary flex-grow"
|
||||||
>
|
>
|
||||||
{connected ? `Sell ${baseCurrency}` : 'CONNECT WALLET TO TRADE'}
|
{connected ? `Sell ${baseCurrency}` : 'Connect Wallet To Trade'}
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -8,32 +8,40 @@ import useConnection from '../hooks/useConnection'
|
||||||
import { withdraw } from '../utils/mango'
|
import { withdraw } from '../utils/mango'
|
||||||
import Loading from './Loading'
|
import Loading from './Loading'
|
||||||
import Button from './Button'
|
import Button from './Button'
|
||||||
|
import { notify } from '../utils/notifications'
|
||||||
|
import { XIcon } from '@heroicons/react/outline'
|
||||||
|
|
||||||
const WithdrawModal = ({ isOpen, onClose }) => {
|
const WithdrawModal = ({ isOpen, onClose }) => {
|
||||||
const [inputAmount, setInputAmount] = useState('')
|
const [inputAmount, setInputAmount] = useState('')
|
||||||
const [submitting, setSubmitting] = useState(false)
|
const [submitting, setSubmitting] = useState(false)
|
||||||
const { symbols } = useMarketList()
|
const { getTokenIndex, symbols } = useMarketList()
|
||||||
const { getTokenIndex } = useMarketList()
|
|
||||||
const { connection, programId } = useConnection()
|
const { connection, programId } = useConnection()
|
||||||
const walletAccounts = useMangoStore((s) => s.wallet.balances)
|
const walletAccounts = useMangoStore((s) => s.wallet.balances)
|
||||||
const actions = useMangoStore((s) => s.actions)
|
const actions = useMangoStore((s) => s.actions)
|
||||||
const depositAccounts = useMemo(
|
const withdrawAccounts = useMemo(
|
||||||
() =>
|
() =>
|
||||||
walletAccounts.filter((acc) =>
|
walletAccounts.filter((acc) =>
|
||||||
Object.values(symbols).includes(acc.account.mint.toString())
|
Object.values(symbols).includes(acc.account.mint.toString())
|
||||||
),
|
),
|
||||||
[symbols, walletAccounts]
|
[symbols, walletAccounts]
|
||||||
)
|
)
|
||||||
const [selectedAccount, setSelectedAccount] = useState(depositAccounts[0])
|
const [selectedAccount, setSelectedAccount] = useState(withdrawAccounts[0])
|
||||||
|
const mintAddress = useMemo(() => selectedAccount?.account.mint.toString(), [
|
||||||
|
selectedAccount.account.mint,
|
||||||
|
])
|
||||||
|
const tokenIndex = useMemo(() => getTokenIndex(mintAddress), [
|
||||||
|
mintAddress,
|
||||||
|
getTokenIndex,
|
||||||
|
])
|
||||||
|
const symbol = useMemo(() => getSymbolForTokenMintAddress(mintAddress), [
|
||||||
|
mintAddress,
|
||||||
|
])
|
||||||
|
|
||||||
const withdrawDisabled = Number(inputAmount) <= 0
|
const withdrawDisabled = Number(inputAmount) <= 0
|
||||||
|
|
||||||
const setMaxForSelectedAccount = () => {
|
const setMaxForSelectedAccount = () => {
|
||||||
const marginAccount = useMangoStore.getState().selectedMarginAccount.current
|
const marginAccount = useMangoStore.getState().selectedMarginAccount.current
|
||||||
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
|
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
|
||||||
const mintAddress = selectedAccount?.account.mint.toString()
|
|
||||||
const tokenIndex = getTokenIndex(mintAddress)
|
|
||||||
const symbol = getSymbolForTokenMintAddress(mintAddress)
|
|
||||||
const max = marginAccount.getUiDeposit(mangoGroup, tokenIndex)
|
const max = marginAccount.getUiDeposit(mangoGroup, tokenIndex)
|
||||||
|
|
||||||
setInputAmount(max.toFixed(tokenPrecision[symbol]))
|
setInputAmount(max.toFixed(tokenPrecision[symbol]))
|
||||||
|
@ -45,8 +53,6 @@ const WithdrawModal = ({ isOpen, onClose }) => {
|
||||||
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
|
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
|
||||||
const wallet = useMangoStore.getState().wallet.current
|
const wallet = useMangoStore.getState().wallet.current
|
||||||
if (marginAccount && mangoGroup) {
|
if (marginAccount && mangoGroup) {
|
||||||
console.log('withdrawing')
|
|
||||||
|
|
||||||
withdraw(
|
withdraw(
|
||||||
connection,
|
connection,
|
||||||
programId,
|
programId,
|
||||||
|
@ -60,12 +66,21 @@ const WithdrawModal = ({ isOpen, onClose }) => {
|
||||||
.then((transSig: string) => {
|
.then((transSig: string) => {
|
||||||
actions.fetchWalletBalances()
|
actions.fetchWalletBalances()
|
||||||
setSubmitting(false)
|
setSubmitting(false)
|
||||||
console.log('Successfull withrawal:', transSig)
|
notify({
|
||||||
|
message: `Withdrew ${inputAmount} ${symbol} into your account`,
|
||||||
|
description: `Hash of transaction is ${transSig}`,
|
||||||
|
type: 'info',
|
||||||
|
})
|
||||||
onClose()
|
onClose()
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setSubmitting(false)
|
setSubmitting(false)
|
||||||
console.warn('Error withdrawing:', err)
|
console.warn('Error withdrawing:', err)
|
||||||
|
notify({
|
||||||
|
message: 'Could not perform withdraw operation',
|
||||||
|
description: err,
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
onClose()
|
onClose()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -74,41 +89,30 @@ const WithdrawModal = ({ isOpen, onClose }) => {
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={isOpen} onClose={onClose}>
|
<Modal isOpen={isOpen} onClose={onClose}>
|
||||||
<Modal.Header>
|
<Modal.Header>
|
||||||
<div className={`text-mango-med-light flex-shrink invisible`}>X</div>
|
<div className={`text-th-fgd-3 flex-shrink invisible`}>X</div>
|
||||||
<div
|
<div
|
||||||
className={`text-mango-med-light flex-grow text-center flex items-center justify-center`}
|
className={`text-th-fgd-3 flex-grow text-center flex items-center justify-center`}
|
||||||
>
|
>
|
||||||
<div className={`flex-initial`}>Select: </div>
|
<div className={`flex-initial`}>Select: </div>
|
||||||
<div className={`ml-4 flex-grow`}>
|
<div className={`ml-4 flex-grow`}>
|
||||||
<AccountSelect
|
<AccountSelect
|
||||||
hideBalance
|
hideBalance
|
||||||
accounts={depositAccounts}
|
accounts={withdrawAccounts}
|
||||||
selectedAccount={selectedAccount}
|
selectedAccount={selectedAccount}
|
||||||
onSelectAccount={setSelectedAccount}
|
onSelectAccount={setSelectedAccount}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={`text-mango-med-light flex-shrink ml-6 mr-2 text-lg`}>
|
<button
|
||||||
<button onClick={onClose} className={`hover:text-mango-yellow`}>
|
onClick={onClose}
|
||||||
<svg
|
className={`text-th-fgd-3 mr-2 ml-4 hover:text-th-primary`}
|
||||||
viewBox="64 64 896 896"
|
>
|
||||||
focusable="false"
|
<XIcon className={`h-6 w-6`} />
|
||||||
data-icon="close"
|
</button>
|
||||||
width="1em"
|
|
||||||
height="1em"
|
|
||||||
fill="currentColor"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</Modal.Header>
|
</Modal.Header>
|
||||||
<div className={`pb-6 px-8`}>
|
<div className={`pb-6 px-8`}>
|
||||||
<div className={`mt-3 text-center sm:mt-5`}>
|
<div className={`mt-3 text-center sm:mt-5`}>
|
||||||
<div
|
<div className={`mt-6 bg-th-bkg-3 rounded-md flex items-center`}>
|
||||||
className={`mt-6 bg-mango-dark-light rounded-md flex items-center`}
|
|
||||||
>
|
|
||||||
<img
|
<img
|
||||||
alt=""
|
alt=""
|
||||||
width="20"
|
width="20"
|
||||||
|
@ -121,7 +125,7 @@ const WithdrawModal = ({ isOpen, onClose }) => {
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
className={`outline-none bg-mango-dark-light w-full py-4 mx-3 text-2xl text-gray-300 flex-grow`}
|
className={`outline-none bg-th-bkg-3 w-full py-4 mx-3 text-2xl text-th-fgd-2 flex-grow`}
|
||||||
placeholder="0.00"
|
placeholder="0.00"
|
||||||
value={inputAmount}
|
value={inputAmount}
|
||||||
onChange={(e) => setInputAmount(e.target.value)}
|
onChange={(e) => setInputAmount(e.target.value)}
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
import React, { useMemo, useState } from 'react'
|
||||||
|
import Modal from './Modal'
|
||||||
|
import MangoSrmAccountSelector from './MangoSrmAccountSelector'
|
||||||
|
import useMangoStore from '../stores/useMangoStore'
|
||||||
|
import useConnection from '../hooks/useConnection'
|
||||||
|
import { withdrawSrm } from '../utils/mango'
|
||||||
|
import Loading from './Loading'
|
||||||
|
import Button from './Button'
|
||||||
|
import { notify } from '../utils/notifications'
|
||||||
|
import { SRM_DECIMALS } from '@project-serum/serum/lib/token-instructions'
|
||||||
|
import { PublicKey } from '@solana/web3.js'
|
||||||
|
import { XIcon } from '@heroicons/react/outline'
|
||||||
|
|
||||||
|
const WithdrawModal = ({ isOpen, onClose }) => {
|
||||||
|
const [inputAmount, setInputAmount] = useState('')
|
||||||
|
const [submitting, setSubmitting] = useState(false)
|
||||||
|
const { connection, programId } = useConnection()
|
||||||
|
const walletAccounts = useMangoStore((s) => s.wallet.balances)
|
||||||
|
const actions = useMangoStore((s) => s.actions)
|
||||||
|
const srmMintAddress = useMangoStore((s) => s.connection.srmMint)
|
||||||
|
const contributedSrm = useMangoStore(
|
||||||
|
(s) => s.selectedMangoGroup.contributedSrm
|
||||||
|
)
|
||||||
|
const mangoSrmAccountsForOwner = useMangoStore(
|
||||||
|
(s) => s.selectedMangoGroup.srmAccountsForOwner
|
||||||
|
)
|
||||||
|
const walletSrmAccount = useMemo(
|
||||||
|
() =>
|
||||||
|
walletAccounts.find(
|
||||||
|
(acc) => srmMintAddress === acc.account.mint.toString()
|
||||||
|
),
|
||||||
|
[walletAccounts, srmMintAddress]
|
||||||
|
)
|
||||||
|
const [selectedAccount, setSelectedAccount] = useState(
|
||||||
|
mangoSrmAccountsForOwner[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
const withdrawDisabled = Number(inputAmount) <= 0
|
||||||
|
|
||||||
|
const setMaxForSelectedAccount = () => {
|
||||||
|
setInputAmount(contributedSrm.toFixed(SRM_DECIMALS))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleWithdraw = () => {
|
||||||
|
setSubmitting(true)
|
||||||
|
const marginAccount = useMangoStore.getState().selectedMarginAccount.current
|
||||||
|
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
|
||||||
|
const wallet = useMangoStore.getState().wallet.current
|
||||||
|
if (marginAccount && mangoGroup) {
|
||||||
|
withdrawSrm(
|
||||||
|
connection,
|
||||||
|
new PublicKey(programId),
|
||||||
|
mangoGroup,
|
||||||
|
selectedAccount,
|
||||||
|
wallet,
|
||||||
|
walletSrmAccount.publicKey,
|
||||||
|
Number(inputAmount)
|
||||||
|
)
|
||||||
|
.then((transSig: string) => {
|
||||||
|
setSubmitting(false)
|
||||||
|
notify({
|
||||||
|
message: `Withdrew ${inputAmount} SRM into your account`,
|
||||||
|
description: `Hash of transaction is ${transSig}`,
|
||||||
|
type: 'info',
|
||||||
|
})
|
||||||
|
onClose()
|
||||||
|
actions.fetchWalletBalances()
|
||||||
|
actions.fetchMangoSrmAccounts()
|
||||||
|
actions.fetchMangoGroup()
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
setSubmitting(false)
|
||||||
|
console.warn('Error withdrawing:', err)
|
||||||
|
notify({
|
||||||
|
message: 'Could not perform withdraw operation',
|
||||||
|
description: `${err}`,
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
onClose()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} onClose={onClose}>
|
||||||
|
<Modal.Header>
|
||||||
|
<div className={`text-th-fgd-4 flex-shrink invisible`}>X</div>
|
||||||
|
<div
|
||||||
|
className={`text-th-fgd-4 flex-grow text-center flex items-center justify-center`}
|
||||||
|
>
|
||||||
|
<div className={`flex-initial`}>Select: </div>
|
||||||
|
<div className={`ml-4 flex-grow`}>
|
||||||
|
<MangoSrmAccountSelector
|
||||||
|
accounts={mangoSrmAccountsForOwner}
|
||||||
|
selectedAccount={selectedAccount}
|
||||||
|
onSelectAccount={setSelectedAccount}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className={`text-th-fgd-3 mr-2 ml-4 hover:text-th-primary`}
|
||||||
|
>
|
||||||
|
<XIcon className={`h-6 w-6`} />
|
||||||
|
</button>
|
||||||
|
</Modal.Header>
|
||||||
|
<div className={`pb-6 px-8`}>
|
||||||
|
<div className={`mt-3 text-center sm:mt-5`}>
|
||||||
|
<div className={`mt-6 bg-th-bkg-3 rounded-md flex items-center`}>
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
src={`/assets/icons/SRM.svg`}
|
||||||
|
className={`ml-3`}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
className={`outline-none bg-th-bkg-3 w-full py-4 mx-3 text-2xl text-th-fgd-2 flex-grow`}
|
||||||
|
placeholder="0.00"
|
||||||
|
value={inputAmount}
|
||||||
|
onChange={(e) => setInputAmount(e.target.value)}
|
||||||
|
></input>
|
||||||
|
<Button
|
||||||
|
onClick={setMaxForSelectedAccount}
|
||||||
|
className={`m-2 rounded py-1`}
|
||||||
|
>
|
||||||
|
Max
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={`mt-5 sm:mt-6 flex justify-center`}>
|
||||||
|
<Button onClick={handleWithdraw} disabled={withdrawDisabled}>
|
||||||
|
<div className={`flex items-center`}>
|
||||||
|
{submitting && <Loading />}
|
||||||
|
Withdraw
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(WithdrawModal)
|
|
@ -1,89 +1,31 @@
|
||||||
import { useCallback, useEffect, useMemo } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { PublicKey } from '@solana/web3.js'
|
|
||||||
import { IDS } from '@blockworks-foundation/mango-client'
|
|
||||||
import useMangoStore from '../stores/useMangoStore'
|
import useMangoStore from '../stores/useMangoStore'
|
||||||
import useConnection from './useConnection'
|
|
||||||
import useInterval from './useInterval'
|
import useInterval from './useInterval'
|
||||||
|
|
||||||
const useMarginAccount = () => {
|
const useMarginAccount = () => {
|
||||||
const mangoClient = useMangoStore((s) => s.mangoClient)
|
const mangoClient = useMangoStore((s) => s.mangoClient)
|
||||||
const mangoGroupName = useMangoStore((s) => s.selectedMangoGroup.name)
|
|
||||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||||
|
const actions = useMangoStore((s) => s.actions)
|
||||||
const selectedMarginAccount = useMangoStore(
|
const selectedMarginAccount = useMangoStore(
|
||||||
(s) => s.selectedMarginAccount.current
|
(s) => s.selectedMarginAccount.current
|
||||||
)
|
)
|
||||||
|
const connected = useMangoStore((s) => s.wallet.connected)
|
||||||
const setMangoStore = useMangoStore((s) => s.set)
|
const setMangoStore = useMangoStore((s) => s.set)
|
||||||
|
|
||||||
const { current: wallet } = useMangoStore((s) => s.wallet)
|
|
||||||
const connected = useMangoStore((s) => s.wallet.connected)
|
|
||||||
|
|
||||||
const { cluster, connection } = useConnection()
|
|
||||||
const clusterIds = useMemo(() => IDS[cluster], [cluster])
|
|
||||||
const mangoGroupIds = useMemo(() => clusterIds.mango_groups[mangoGroupName], [
|
|
||||||
clusterIds,
|
|
||||||
mangoGroupName,
|
|
||||||
])
|
|
||||||
|
|
||||||
const fetchMangoGroup = useCallback(() => {
|
|
||||||
if (!mangoClient) return
|
|
||||||
|
|
||||||
const mangoGroupPk = new PublicKey(mangoGroupIds.mango_group_pk)
|
|
||||||
const srmVaultPk = new PublicKey(mangoGroupIds.srm_vault_pk)
|
|
||||||
|
|
||||||
mangoClient
|
|
||||||
.getMangoGroup(connection, mangoGroupPk, srmVaultPk)
|
|
||||||
.then(async (mangoGroup) => {
|
|
||||||
const srmAccountInfo = await connection.getAccountInfo(
|
|
||||||
mangoGroup.srmVault
|
|
||||||
)
|
|
||||||
// Set the mango group
|
|
||||||
setMangoStore((state) => {
|
|
||||||
state.selectedMangoGroup.current = mangoGroup
|
|
||||||
state.selectedMangoGroup.srmAccount = srmAccountInfo
|
|
||||||
state.selectedMangoGroup.mintDecimals = mangoGroup.mintDecimals
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error('Could not get mango group: ', err)
|
|
||||||
})
|
|
||||||
}, [connection, mangoClient, mangoGroupIds, setMangoStore])
|
|
||||||
|
|
||||||
const fetchMarginAccounts = useCallback(() => {
|
|
||||||
if (!mangoClient || !mangoGroup || !connected || !wallet.publicKey) return
|
|
||||||
|
|
||||||
mangoClient
|
|
||||||
.getMarginAccountsForOwner(
|
|
||||||
connection,
|
|
||||||
new PublicKey(clusterIds.mango_program_id),
|
|
||||||
mangoGroup,
|
|
||||||
wallet
|
|
||||||
)
|
|
||||||
.then((marginAccounts) => {
|
|
||||||
if (marginAccounts.length > 0) {
|
|
||||||
setMangoStore((state) => {
|
|
||||||
state.marginAcccounts = marginAccounts
|
|
||||||
state.selectedMarginAccount.current = marginAccounts[0]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error('Could not get margin accounts for user in effect ', err)
|
|
||||||
})
|
|
||||||
}, [mangoClient, connection, connected, mangoGroup, wallet])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchMangoGroup()
|
actions.fetchMangoGroup()
|
||||||
}, [fetchMangoGroup])
|
}, [actions])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!connected) return
|
if (!connected) return
|
||||||
fetchMarginAccounts()
|
actions.fetchMarginAcccount()
|
||||||
}, [connected, fetchMarginAccounts])
|
actions.fetchMangoSrmAccounts()
|
||||||
|
}, [connected, actions])
|
||||||
|
|
||||||
useInterval(() => {
|
useInterval(() => {
|
||||||
fetchMarginAccounts()
|
actions.fetchMarginAcccount()
|
||||||
// fetchMangoGroup()
|
// fetchMangoGroup()
|
||||||
}, 5000)
|
}, 9000)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mangoClient,
|
mangoClient,
|
||||||
|
|
|
@ -23,14 +23,14 @@ export function useOpenOrders() {
|
||||||
const markets = useMangoStore((s) => s.selectedMangoGroup.markets)
|
const markets = useMangoStore((s) => s.selectedMangoGroup.markets)
|
||||||
const marginAccount = useMangoStore((s) => s.selectedMarginAccount.current)
|
const marginAccount = useMangoStore((s) => s.selectedMarginAccount.current)
|
||||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||||
|
const accountInfos = useMangoStore((s) => s.accountInfos)
|
||||||
const { marketList } = useMarketList()
|
const { marketList } = useMarketList()
|
||||||
|
|
||||||
if (!mangoGroup || !marginAccount) return null
|
if (!mangoGroup || !marginAccount || !accountInfos) return null
|
||||||
|
|
||||||
const openOrders = Object.entries(markets).map(([address, market]) => {
|
const openOrders = Object.entries(markets).map(([address, market]) => {
|
||||||
const marketIndex = mangoGroup.getMarketIndex(market)
|
const marketIndex = mangoGroup.getMarketIndex(market)
|
||||||
const openOrdersAccount = marginAccount.openOrdersAccounts[marketIndex]
|
const openOrdersAccount = marginAccount.openOrdersAccounts[marketIndex]
|
||||||
const accountInfos = useMangoStore.getState().accountInfos
|
|
||||||
const marketName = marketList.find(
|
const marketName = marketList.find(
|
||||||
(mkt) => mkt.address.toString() === address
|
(mkt) => mkt.address.toString() === address
|
||||||
).name
|
).name
|
||||||
|
|
|
@ -7,8 +7,12 @@ import { parseTokenAccountData } from '../utils/tokens'
|
||||||
const useSrmAccount = () => {
|
const useSrmAccount = () => {
|
||||||
const srmAccount = useMangoStore((s) => s.selectedMangoGroup.srmAccount)
|
const srmAccount = useMangoStore((s) => s.selectedMangoGroup.srmAccount)
|
||||||
|
|
||||||
const accountData = parseTokenAccountData(srmAccount.data)
|
const accountData = srmAccount
|
||||||
const totalSrm = nativeToUi(accountData.amount, SRM_DECIMALS)
|
? parseTokenAccountData(srmAccount?.data)
|
||||||
|
: null
|
||||||
|
const totalSrm = accountData
|
||||||
|
? nativeToUi(accountData.amount, SRM_DECIMALS)
|
||||||
|
: 0
|
||||||
const feeTier = getFeeTier(0, totalSrm)
|
const feeTier = getFeeTier(0, totalSrm)
|
||||||
const rates = getFeeRates(feeTier)
|
const rates = getFeeRates(feeTier)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import create, { State } from 'zustand'
|
import create, { State } from 'zustand'
|
||||||
import { devtools } from 'zustand/middleware'
|
|
||||||
import produce from 'immer'
|
import produce from 'immer'
|
||||||
import { Market } from '@project-serum/serum'
|
import { Market } from '@project-serum/serum'
|
||||||
import {
|
import {
|
||||||
|
@ -7,7 +6,9 @@ import {
|
||||||
MangoClient,
|
MangoClient,
|
||||||
MangoGroup,
|
MangoGroup,
|
||||||
MarginAccount,
|
MarginAccount,
|
||||||
|
nativeToUi,
|
||||||
} from '@blockworks-foundation/mango-client'
|
} from '@blockworks-foundation/mango-client'
|
||||||
|
import { SRM_DECIMALS } from '@project-serum/serum/lib/token-instructions'
|
||||||
import { AccountInfo, Connection, PublicKey } from '@solana/web3.js'
|
import { AccountInfo, Connection, PublicKey } from '@solana/web3.js'
|
||||||
import { Wallet } from '@project-serum/sol-wallet-adapter'
|
import { Wallet } from '@project-serum/sol-wallet-adapter'
|
||||||
import { EndpointInfo } from '../@types/types'
|
import { EndpointInfo } from '../@types/types'
|
||||||
|
@ -50,6 +51,7 @@ interface MangoStore extends State {
|
||||||
cluster: string
|
cluster: string
|
||||||
current: Connection
|
current: Connection
|
||||||
endpoint: string
|
endpoint: string
|
||||||
|
srmMint: string
|
||||||
}
|
}
|
||||||
market: {
|
market: {
|
||||||
current: Market | null
|
current: Market | null
|
||||||
|
@ -71,6 +73,8 @@ interface MangoStore extends State {
|
||||||
[key: string]: Market
|
[key: string]: Market
|
||||||
}
|
}
|
||||||
mintDecimals: number[]
|
mintDecimals: number[]
|
||||||
|
srmAccountsForOwner: any[]
|
||||||
|
contributedSrm: number
|
||||||
}
|
}
|
||||||
selectedMarginAccount: {
|
selectedMarginAccount: {
|
||||||
current: MarginAccount | null
|
current: MarginAccount | null
|
||||||
|
@ -91,79 +95,173 @@ interface MangoStore extends State {
|
||||||
actions: any
|
actions: any
|
||||||
}
|
}
|
||||||
|
|
||||||
const useMangoStore = create<MangoStore>(
|
const useMangoStore = create<MangoStore>((set, get) => ({
|
||||||
devtools((set, get) => ({
|
notifications: [],
|
||||||
notifications: [],
|
accountInfos: {},
|
||||||
accountInfos: {},
|
connection: {
|
||||||
connection: {
|
cluster: CLUSTER,
|
||||||
cluster: CLUSTER,
|
current: DEFAULT_CONNECTION,
|
||||||
current: DEFAULT_CONNECTION,
|
endpoint: ENDPOINT_URL,
|
||||||
endpoint: ENDPOINT_URL,
|
srmMint: IDS[CLUSTER].symbols['SRM'],
|
||||||
|
},
|
||||||
|
selectedMangoGroup: {
|
||||||
|
name: DEFAULT_MANGO_GROUP_NAME,
|
||||||
|
current: null,
|
||||||
|
markets: {},
|
||||||
|
srmAccount: null,
|
||||||
|
mintDecimals: [],
|
||||||
|
srmAccountsForOwner: [],
|
||||||
|
contributedSrm: 0,
|
||||||
|
},
|
||||||
|
selectedMarket: {
|
||||||
|
name: Object.entries(
|
||||||
|
IDS[CLUSTER].mango_groups[DEFAULT_MANGO_GROUP_NAME].spot_market_symbols
|
||||||
|
)[0][0],
|
||||||
|
address: Object.entries(
|
||||||
|
IDS[CLUSTER].mango_groups[DEFAULT_MANGO_GROUP_NAME].spot_market_symbols
|
||||||
|
)[0][1],
|
||||||
|
},
|
||||||
|
market: {
|
||||||
|
current: null,
|
||||||
|
mangoProgramId: null,
|
||||||
|
markPrice: 0,
|
||||||
|
orderBook: [],
|
||||||
|
},
|
||||||
|
mangoClient: new MangoClient(),
|
||||||
|
mangoGroups: [],
|
||||||
|
marginAccounts: [],
|
||||||
|
selectedMarginAccount: {
|
||||||
|
current: null,
|
||||||
|
},
|
||||||
|
tradeForm: {
|
||||||
|
side: 'buy',
|
||||||
|
baseSize: '',
|
||||||
|
quoteSize: '',
|
||||||
|
tradeType: 'Limit',
|
||||||
|
price: '',
|
||||||
|
},
|
||||||
|
wallet: {
|
||||||
|
connected: false,
|
||||||
|
current: null,
|
||||||
|
balances: [],
|
||||||
|
},
|
||||||
|
set: (fn) => set(produce(fn)),
|
||||||
|
actions: {
|
||||||
|
async fetchWalletBalances() {
|
||||||
|
const connection = get().connection.current
|
||||||
|
const wallet = get().wallet.current
|
||||||
|
const connected = get().wallet.connected
|
||||||
|
const set = get().set
|
||||||
|
console.log('fetchingWalletBalances', connected, wallet)
|
||||||
|
if (wallet && connected) {
|
||||||
|
const ownerAddress = wallet.publicKey
|
||||||
|
const ownedTokenAccounts = await getOwnedTokenAccounts(
|
||||||
|
connection,
|
||||||
|
ownerAddress
|
||||||
|
)
|
||||||
|
set((state) => {
|
||||||
|
state.wallet.balances = ownedTokenAccounts
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
set((state) => {
|
||||||
|
state.wallet.balances = []
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
selectedMangoGroup: {
|
async fetchMangoSrmAccounts() {
|
||||||
name: DEFAULT_MANGO_GROUP_NAME,
|
const connection = get().connection.current
|
||||||
current: null,
|
const wallet = get().wallet.current
|
||||||
markets: {},
|
const connected = get().wallet.connected
|
||||||
srmAccount: null,
|
const selectedMangoGroup = get().selectedMangoGroup.current
|
||||||
mintDecimals: [],
|
const cluster = get().connection.cluster
|
||||||
},
|
const mangoClient = get().mangoClient
|
||||||
selectedMarket: {
|
const set = get().set
|
||||||
name: Object.entries(
|
|
||||||
IDS[CLUSTER].mango_groups[DEFAULT_MANGO_GROUP_NAME].spot_market_symbols
|
if (wallet && connected) {
|
||||||
)[0][0],
|
const usersMangoSrmAccounts = await mangoClient.getMangoSrmAccountsForOwner(
|
||||||
address: Object.entries(
|
connection,
|
||||||
IDS[CLUSTER].mango_groups[DEFAULT_MANGO_GROUP_NAME].spot_market_symbols
|
new PublicKey(IDS[cluster].mango_program_id),
|
||||||
)[0][1],
|
selectedMangoGroup,
|
||||||
},
|
wallet
|
||||||
market: {
|
)
|
||||||
current: null,
|
if (usersMangoSrmAccounts.length) {
|
||||||
mangoProgramId: null,
|
|
||||||
markPrice: 0,
|
|
||||||
orderBook: [],
|
|
||||||
},
|
|
||||||
mangoClient: new MangoClient(),
|
|
||||||
mangoGroups: [],
|
|
||||||
marginAccounts: [],
|
|
||||||
selectedMarginAccount: {
|
|
||||||
current: null,
|
|
||||||
},
|
|
||||||
tradeForm: {
|
|
||||||
side: 'buy',
|
|
||||||
baseSize: '',
|
|
||||||
quoteSize: '',
|
|
||||||
tradeType: 'Limit',
|
|
||||||
price: '',
|
|
||||||
},
|
|
||||||
wallet: {
|
|
||||||
connected: false,
|
|
||||||
current: null,
|
|
||||||
balances: [],
|
|
||||||
},
|
|
||||||
set: (fn) => set(produce(fn)),
|
|
||||||
actions: {
|
|
||||||
async fetchWalletBalances() {
|
|
||||||
const connection = get().connection.current
|
|
||||||
const wallet = get().wallet.current
|
|
||||||
const connected = get().wallet.connected
|
|
||||||
const set = get().set
|
|
||||||
console.log('fetchingWalletBalances', connected, wallet)
|
|
||||||
if (wallet && connected) {
|
|
||||||
const ownerAddress = wallet.publicKey
|
|
||||||
const ownedTokenAccounts = await getOwnedTokenAccounts(
|
|
||||||
connection,
|
|
||||||
ownerAddress
|
|
||||||
)
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.wallet.balances = ownedTokenAccounts
|
state.selectedMangoGroup.srmAccountsForOwner = usersMangoSrmAccounts
|
||||||
})
|
const totalSrmDeposits = usersMangoSrmAccounts.reduce(
|
||||||
} else {
|
(prev, cur) => prev + cur.amount,
|
||||||
set((state) => {
|
0
|
||||||
state.wallet.balances = []
|
)
|
||||||
|
state.selectedMangoGroup.contributedSrm = nativeToUi(
|
||||||
|
totalSrmDeposits,
|
||||||
|
SRM_DECIMALS
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
}))
|
async fetchMarginAcccount() {
|
||||||
)
|
const connection = get().connection.current
|
||||||
|
const mangoGroup = get().selectedMangoGroup.current
|
||||||
|
const wallet = get().wallet.current
|
||||||
|
const cluster = get().connection.cluster
|
||||||
|
const mangoClient = get().mangoClient
|
||||||
|
const programId = IDS[cluster].mango_program_id
|
||||||
|
const set = get().set
|
||||||
|
|
||||||
|
if (!wallet.publicKey) return
|
||||||
|
|
||||||
|
mangoClient
|
||||||
|
.getMarginAccountsForOwner(
|
||||||
|
connection,
|
||||||
|
new PublicKey(programId),
|
||||||
|
mangoGroup,
|
||||||
|
wallet
|
||||||
|
)
|
||||||
|
.then((marginAccounts) => {
|
||||||
|
if (marginAccounts.length > 0) {
|
||||||
|
set((state) => {
|
||||||
|
state.marginAcccounts = marginAccounts
|
||||||
|
state.selectedMarginAccount.current = marginAccounts[0]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(
|
||||||
|
'Could not get margin accounts for user in effect ',
|
||||||
|
err
|
||||||
|
)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async fetchMangoGroup() {
|
||||||
|
const connection = get().connection.current
|
||||||
|
const mangoGroupName = get().selectedMangoGroup.name
|
||||||
|
const cluster = get().connection.cluster
|
||||||
|
const mangoClient = get().mangoClient
|
||||||
|
const set = get().set
|
||||||
|
const mangoGroupIds = IDS[cluster].mango_groups[mangoGroupName]
|
||||||
|
if (!mangoClient) return
|
||||||
|
|
||||||
|
const mangoGroupPk = new PublicKey(mangoGroupIds.mango_group_pk)
|
||||||
|
const srmVaultPk = new PublicKey(mangoGroupIds.srm_vault_pk)
|
||||||
|
|
||||||
|
mangoClient
|
||||||
|
.getMangoGroup(connection, mangoGroupPk, srmVaultPk)
|
||||||
|
.then(async (mangoGroup) => {
|
||||||
|
const srmAccountInfo = await connection.getAccountInfo(
|
||||||
|
mangoGroup.srmVault
|
||||||
|
)
|
||||||
|
// Set the mango group
|
||||||
|
set((state) => {
|
||||||
|
state.selectedMangoGroup.current = mangoGroup
|
||||||
|
state.selectedMangoGroup.srmAccount = srmAccountInfo
|
||||||
|
state.selectedMangoGroup.mintDecimals = mangoGroup.mintDecimals
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error('Could not get mango group: ', err)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
export default useMangoStore
|
export default useMangoStore
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
"**/*.tsx",
|
"**/*.tsx",
|
||||||
"**/*.js",
|
"**/*.js",
|
||||||
"components/AccountSelect.jsx"
|
"components/AccountSelect.jsx",
|
||||||
|
"components/MangoSrmAccountSelector.jsx"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ export async function initMarginAccount(
|
||||||
const signers = [accInstr.account]
|
const signers = [accInstr.account]
|
||||||
|
|
||||||
const functionName = 'InitMarginAccount'
|
const functionName = 'InitMarginAccount'
|
||||||
const sendingMessage = `sending ${functionName} instruction...`
|
const sendingMessage = `Sending ${functionName} instruction...`
|
||||||
const sentMessage = `${functionName} instruction sent`
|
const sentMessage = `${functionName} instruction sent`
|
||||||
const successMessage = `${functionName} instruction success`
|
const successMessage = `${functionName} instruction success`
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ export async function deposit(
|
||||||
const signers = []
|
const signers = []
|
||||||
|
|
||||||
const functionName = 'Deposit'
|
const functionName = 'Deposit'
|
||||||
const sendingMessage = `sending ${functionName} instruction...`
|
const sendingMessage = `Sending ${functionName} instruction...`
|
||||||
const sentMessage = `${functionName} instruction sent`
|
const sentMessage = `${functionName} instruction sent`
|
||||||
const successMessage = `${functionName} instruction success`
|
const successMessage = `${functionName} instruction success`
|
||||||
return await sendTransaction({
|
return await sendTransaction({
|
||||||
|
@ -228,7 +228,7 @@ export async function initMarginAccountAndDeposit(
|
||||||
// Specify signers in addition to the wallet
|
// Specify signers in addition to the wallet
|
||||||
const signers = [accInstr.account]
|
const signers = [accInstr.account]
|
||||||
const functionName = 'InitMarginAccount'
|
const functionName = 'InitMarginAccount'
|
||||||
const sendingMessage = `sending ${functionName} instruction...`
|
const sendingMessage = `Sending ${functionName} instruction...`
|
||||||
const sentMessage = `${functionName} instruction sent`
|
const sentMessage = `${functionName} instruction sent`
|
||||||
const successMessage = `${functionName} instruction success`
|
const successMessage = `${functionName} instruction success`
|
||||||
|
|
||||||
|
@ -293,7 +293,7 @@ export async function withdraw(
|
||||||
transaction.add(instruction)
|
transaction.add(instruction)
|
||||||
const signers = []
|
const signers = []
|
||||||
const functionName = 'Withdraw'
|
const functionName = 'Withdraw'
|
||||||
const sendingMessage = `sending ${functionName} instruction...`
|
const sendingMessage = `Sending ${functionName} instruction...`
|
||||||
const sentMessage = `${functionName} instruction sent`
|
const sentMessage = `${functionName} instruction sent`
|
||||||
const successMessage = `${functionName} instruction success`
|
const successMessage = `${functionName} instruction success`
|
||||||
return await sendTransaction({
|
return await sendTransaction({
|
||||||
|
@ -349,7 +349,7 @@ export async function borrow(
|
||||||
transaction.add(instruction)
|
transaction.add(instruction)
|
||||||
const signers = []
|
const signers = []
|
||||||
const functionName = 'Borrow'
|
const functionName = 'Borrow'
|
||||||
const sendingMessage = `sending ${functionName} instruction...`
|
const sendingMessage = `Sending ${functionName} instruction...`
|
||||||
const sentMessage = `${functionName} instruction sent`
|
const sentMessage = `${functionName} instruction sent`
|
||||||
const successMessage = `${functionName} instruction success`
|
const successMessage = `${functionName} instruction success`
|
||||||
return await sendTransaction({
|
return await sendTransaction({
|
||||||
|
@ -439,7 +439,7 @@ export async function settleAllBorrows(
|
||||||
transaction.add(instruction)
|
transaction.add(instruction)
|
||||||
})
|
})
|
||||||
const functionName = 'SettleBorrows'
|
const functionName = 'SettleBorrows'
|
||||||
const sendingMessage = `sending ${functionName} instruction...`
|
const sendingMessage = `Sending ${functionName} instruction...`
|
||||||
const sentMessage = `${functionName} instruction sent`
|
const sentMessage = `${functionName} instruction sent`
|
||||||
const successMessage = `${functionName} instruction success`
|
const successMessage = `${functionName} instruction success`
|
||||||
|
|
||||||
|
@ -1062,7 +1062,7 @@ export async function settleFunds(
|
||||||
|
|
||||||
const signers = []
|
const signers = []
|
||||||
const functionName = 'SettleFunds'
|
const functionName = 'SettleFunds'
|
||||||
const sendingMessage = `sending ${functionName} instruction...`
|
const sendingMessage = `Sending ${functionName} instruction...`
|
||||||
const sentMessage = `${functionName} instruction sent`
|
const sentMessage = `${functionName} instruction sent`
|
||||||
const successMessage = `${functionName} instruction success`
|
const successMessage = `${functionName} instruction success`
|
||||||
return await sendTransaction({
|
return await sendTransaction({
|
||||||
|
@ -1306,7 +1306,7 @@ async function packageAndSend(
|
||||||
signers: Account[],
|
signers: Account[],
|
||||||
functionName: string
|
functionName: string
|
||||||
): Promise<TransactionSignature> {
|
): Promise<TransactionSignature> {
|
||||||
const sendingMessage = `sending ${functionName} instruction...`
|
const sendingMessage = `Sending ${functionName} instruction...`
|
||||||
const sentMessage = `${functionName} instruction sent`
|
const sentMessage = `${functionName} instruction sent`
|
||||||
const successMessage = `${functionName} instruction success`
|
const successMessage = `${functionName} instruction success`
|
||||||
return await sendTransaction({
|
return await sendTransaction({
|
||||||
|
|
|
@ -10,8 +10,6 @@ export function notify(newNotification: {
|
||||||
const notifications = useMangoStore.getState().notifications
|
const notifications = useMangoStore.getState().notifications
|
||||||
|
|
||||||
setMangoStore((state) => {
|
setMangoStore((state) => {
|
||||||
console.log('original', state.notifications)
|
|
||||||
|
|
||||||
state.notifications = [
|
state.notifications = [
|
||||||
...notifications,
|
...notifications,
|
||||||
{ type: 'success', ...newNotification },
|
{ type: 'success', ...newNotification },
|
||||||
|
|
Loading…
Reference in New Issue