fee discount tables and more theming

This commit is contained in:
Tyler Shipe 2021-04-12 23:39:08 -04:00
parent c076413e80
commit 9e67c147bf
20 changed files with 816 additions and 306 deletions

View File

@ -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 (

View File

@ -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>

View File

@ -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)}

View File

@ -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)

View File

@ -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>

View File

@ -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>
) )
} }

View File

@ -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

View File

@ -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}

View File

@ -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>
))} ))}

View File

@ -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 (

View File

@ -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>
) )
) : ( ) : (

View File

@ -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)}

View File

@ -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)

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -21,6 +21,7 @@
"**/*.ts", "**/*.ts",
"**/*.tsx", "**/*.tsx",
"**/*.js", "**/*.js",
"components/AccountSelect.jsx" "components/AccountSelect.jsx",
"components/MangoSrmAccountSelector.jsx"
] ]
} }

View File

@ -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({

View File

@ -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 },