merge main
This commit is contained in:
commit
d0845efbae
91
README.md
91
README.md
|
@ -1,57 +1,78 @@
|
|||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
This repo contains the Next.js app for the Mango v4 user interface.
|
||||
|
||||
## Dependency Management
|
||||
## ⚡️ Quickstart
|
||||
|
||||
When updating dependencies, there are various files that must be kept up-to-date. Newly added, or updated dependencies can introduce unwanted/malicious scripts that can introduce risks for users and/or developers. The `lavamoat allow-scripts` feature allows us to deny by default, but adds some additional steps to the usual workflow.
|
||||
To get started, follow these steps:
|
||||
|
||||
`yarn.lock`:
|
||||
|
||||
- Instead of running `yarn` or `yarn install`, run `yarn setup` to ensure the `yarn.lock` file is in sync and that dependency scripts are run according to the `allowScripts` policy (set in `packages.json`)
|
||||
- If `lavamoat` detects new scripts that are not explicitely allowed/denied, it'll throw and error with details (see below)
|
||||
- Running `yarn setup` will also dedupe the `yarn.lock` file to reduce the dependency tree. Note CI will fail if there are dupes in `yarn.lock`!
|
||||
|
||||
The `allowScripts` configuration in `package.json`:
|
||||
|
||||
- There are two ways to configure script policies:
|
||||
1. Update the allow-scripts section manually by adding the missing package in the `allowScripts` section in `package.json`
|
||||
2. Run `yarn allow-scripts auto` to update the `allowScripts` configuration automatically
|
||||
- Review each new package to determine whether the install script needs to run or not, testing if necessary.
|
||||
- Use `npx can-i-ignore-scripts` to help assessing whether scripts are needed
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
1. **Clone the repo:** Begin by cloning the repository using the command:
|
||||
|
||||
```bash
|
||||
git clone git@github.com:blockworks-foundation/mango-v4-ui.git
|
||||
```
|
||||
|
||||
2. **Install Dependencies:** Move into the directory and install the dependencies:
|
||||
|
||||
```bash
|
||||
cd mango-v4-ui
|
||||
yarn setup
|
||||
```
|
||||
|
||||
3. **Run the app:**
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
4. Browse to http://localhost:3000
|
||||
|
||||
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
|
||||
## ⌨️ Contributor's Guide
|
||||
|
||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
|
||||
### Code quality
|
||||
|
||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
||||
- Avoid duplication
|
||||
- Consider performance (use useMemo and useCallback where appropriate)
|
||||
- Create logical components and give them descriptive names
|
||||
- Destructure objects and arrays
|
||||
- Define constants for event functions unless they are very simple e.g. a single state update
|
||||
- Create hooks for shared logic
|
||||
- Add translation keys in alphabetical order
|
||||
|
||||
## Learn More
|
||||
### Branching
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
Prefix your branches with your Git username and give them concise and descriptive names
|
||||
e.g. username/branch-name
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
### Commits
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
Add commits for each self-contained change and give your commits clear messages that describe the change. Smaller commits that encompass a specific change are preferred over large commits with many changes
|
||||
|
||||
## Deploy on Vercel
|
||||
### PRs
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
All PRs should have a meaningful name and include a description of what the changes are.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
If there are visual changes, include screenshots in the description.
|
||||
|
||||
## Creating Color Themes
|
||||
If the PR is unfinished include a "TODO" section with work not yet completed. If there are known issues/bugs include a section outlining what they are.
|
||||
|
||||
#### Drafts
|
||||
|
||||
Opening draft PRs is a good way for other contributors to know a feature is being worked on. This is most useful for larger/complex features and is not a requirement. When your feature is at a point where you'd like to gather feedback or it's close to completion open a draft PR and share the preview link in the relevant Discord channel
|
||||
|
||||
Prefix "WIP:" to your draft PR name
|
||||
|
||||
### Reviews
|
||||
|
||||
When your changes are finished, who you request review from depends on the type of changes.
|
||||
|
||||
For complex changes e.g. new transactions, large features, lots of client or backend interactions you should at a minimum include @tlrsssss in your review
|
||||
|
||||
For changes that affect visual elements of the app (including text changes), request a review from @saml33 at a minimum
|
||||
|
||||
If you're unsure, request a review from @tlrssss and @saml33
|
||||
|
||||
If your work involves other parts of the stack (backend, client, etc.) request a review from the relevant person in that area
|
||||
|
||||
## 🎨 Creating Color Themes
|
||||
|
||||
1. Copy one of the other color themes in [tailwind.config.js](https://github.com/blockworks-foundation/mango-v4-ui/blob/main/tailwind.config.js) (starting line 25)
|
||||
2. Modify the colors. For the variables bkg-\* and fgd-\* pick a base color for bkg-1 and fgd-1 then adjust the lightness for 2-4. Use this same process to create dark/hover variations for the colors that have these properties. The base color can be anything that works for your theme.
|
||||
|
|
|
@ -37,6 +37,8 @@ import { NFT } from 'types'
|
|||
import { useViewport } from 'hooks/useViewport'
|
||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
import { SIDEBAR_COLLAPSE_KEY } from 'utils/constants'
|
||||
import { createTransferInstruction } from '@solana/spl-token'
|
||||
import { PublicKey, TransactionInstruction } from '@solana/web3.js'
|
||||
|
||||
const SideNav = ({ collapsed }: { collapsed: boolean }) => {
|
||||
const { t } = useTranslation(['common', 'search'])
|
||||
|
@ -46,6 +48,10 @@ const SideNav = ({ collapsed }: { collapsed: boolean }) => {
|
|||
const themeData = mangoStore((s) => s.themeData)
|
||||
const nfts = mangoStore((s) => s.wallet.nfts.data)
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const setPrependedGlobalAdditionalInstructions = mangoStore(
|
||||
(s) => s.actions.setPrependedGlobalAdditionalInstructions,
|
||||
)
|
||||
|
||||
const router = useRouter()
|
||||
const { pathname } = router
|
||||
|
||||
|
@ -88,14 +94,39 @@ const SideNav = ({ collapsed }: { collapsed: boolean }) => {
|
|||
return mangoNfts
|
||||
}, [nfts])
|
||||
|
||||
//mark transactions with used nfts
|
||||
useEffect(() => {
|
||||
let newInstruction: TransactionInstruction[] = []
|
||||
if (mangoNfts.length && theme) {
|
||||
const collectionAddress = CUSTOM_SKINS[theme.toLowerCase()]
|
||||
const usedNft = mangoNfts.find(
|
||||
(nft) => nft.collectionAddress === collectionAddress,
|
||||
)
|
||||
if (usedNft && publicKey && collectionAddress) {
|
||||
newInstruction = [
|
||||
createTransferInstruction(
|
||||
new PublicKey(usedNft.tokenAccount),
|
||||
new PublicKey(usedNft.tokenAccount),
|
||||
publicKey,
|
||||
1,
|
||||
),
|
||||
]
|
||||
}
|
||||
}
|
||||
setPrependedGlobalAdditionalInstructions(newInstruction)
|
||||
}, [mangoNfts, theme, themeData])
|
||||
|
||||
// find sidebar image url from skin nft for theme
|
||||
const sidebarImageUrl = useMemo(() => {
|
||||
if (!theme) return themeData.sideImagePath
|
||||
const collectionAddress = CUSTOM_SKINS[theme.toLowerCase()]
|
||||
if (collectionAddress && mangoNfts.length) {
|
||||
const sidebarImageUrl =
|
||||
mangoNfts.find((nft) => nft.collectionAddress === collectionAddress)
|
||||
?.image || themeData.sideImagePath
|
||||
const attributes = mangoNfts.find(
|
||||
(nft) => nft.collectionAddress === collectionAddress,
|
||||
)?.json?.attributes
|
||||
const sidebarImageUrl = attributes
|
||||
? attributes[0].value || themeData.sideImagePath
|
||||
: ''
|
||||
return sidebarImageUrl
|
||||
}
|
||||
return themeData.sideImagePath
|
||||
|
|
|
@ -468,9 +468,18 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
Number(tierPreset.maintLiabWeight),
|
||||
Number(tierPreset.initLiabWeight),
|
||||
Number(tierPreset.liquidationFee),
|
||||
Number(tierPreset.delayIntervalSeconds),
|
||||
Number(tierPreset.delayGrowthLimit),
|
||||
Number(tierPreset.stableGrowthLimit),
|
||||
Number(tierPreset.minVaultToDepositsRatio),
|
||||
new BN(tierPreset.netBorrowLimitWindowSizeTs),
|
||||
new BN(tierPreset.netBorrowLimitPerWindowQuote),
|
||||
Number(tierPreset.borrowWeightScale),
|
||||
Number(tierPreset.depositWeightScale),
|
||||
Number(tierPreset.reduceOnly),
|
||||
Number(tierPreset.tokenConditionalSwapTakerFeeRate),
|
||||
Number(tierPreset.tokenConditionalSwapMakerFeeRate),
|
||||
Number(tierPreset.flashLoanDepositFeeRate),
|
||||
)
|
||||
.accounts({
|
||||
admin: MANGO_DAO_WALLET,
|
||||
|
@ -502,13 +511,16 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
null,
|
||||
null,
|
||||
null,
|
||||
tierPreset.borrowWeightScale,
|
||||
tierPreset.depositWeightScale,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
)
|
||||
.accounts({
|
||||
oracle: new PublicKey(advForm.oraclePk),
|
||||
|
@ -524,7 +536,9 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
} as AccountMeta,
|
||||
])
|
||||
.instruction()
|
||||
proposalTx.push(editIx)
|
||||
if (!tierPreset.insuranceFound) {
|
||||
proposalTx.push(editIx)
|
||||
}
|
||||
} else {
|
||||
const trustlessIx = await client!.program.methods
|
||||
.tokenRegisterTrustless(Number(advForm.tokenIndex), advForm.name)
|
||||
|
|
|
@ -15,7 +15,7 @@ const CreateAccountModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<div className="flex min-h-[264px] flex-col items-center justify-center">
|
||||
<div className="flex min-h-[400px] flex-col items-center justify-center">
|
||||
<CreateAccountForm customClose={handleClose} />
|
||||
</div>
|
||||
</Modal>
|
||||
|
|
|
@ -122,7 +122,7 @@ const CreateSwitchboardOracleModal = ({
|
|||
minRequiredOracleResults: 3,
|
||||
minRequiredJobResults: 2,
|
||||
minUpdateDelaySeconds: 6,
|
||||
forceReportPeriod: 24 * 60 * 60,
|
||||
forceReportPeriod: 60 * 60,
|
||||
withdrawAuthority: MANGO_DAO_WALLET,
|
||||
authority: payer,
|
||||
crankDataBuffer: crankAccount.dataBuffer?.publicKey,
|
||||
|
|
|
@ -196,6 +196,9 @@ const DashboardSuggestedValues = ({
|
|||
bank.reduceOnly ? 0 : null,
|
||||
null,
|
||||
null,
|
||||
getNullOrVal(fieldsToChange.tokenConditionalSwapTakerFeeRate),
|
||||
getNullOrVal(fieldsToChange.tokenConditionalSwapMakerFeeRate),
|
||||
getNullOrVal(fieldsToChange.loanFeeRate),
|
||||
)
|
||||
.accounts({
|
||||
group: group!.publicKey,
|
||||
|
|
|
@ -102,7 +102,7 @@ const MangoAccountsListModal = ({
|
|||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<div className="inline-block w-full transform overflow-x-hidden">
|
||||
<div className="flex min-h-[364px] flex-col justify-between">
|
||||
<div className="flex min-h-[400px] flex-col justify-between">
|
||||
<div>
|
||||
<h2 className="text-center">{t('accounts')}</h2>
|
||||
{loading ? (
|
||||
|
|
|
@ -14,7 +14,11 @@ import {
|
|||
TrHead,
|
||||
} from '@components/shared/TableElements'
|
||||
import Tooltip from '@components/shared/Tooltip'
|
||||
import { NoSymbolIcon, UsersIcon } from '@heroicons/react/20/solid'
|
||||
import {
|
||||
EyeSlashIcon,
|
||||
NoSymbolIcon,
|
||||
UsersIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
|
@ -32,6 +36,7 @@ import PerpSideBadge from './PerpSideBadge'
|
|||
import TableMarketName from './TableMarketName'
|
||||
import { useSortableData } from 'hooks/useSortableData'
|
||||
import { useCallback } from 'react'
|
||||
import { useHiddenMangoAccounts } from 'hooks/useHiddenMangoAccounts'
|
||||
|
||||
const TradeHistory = () => {
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
|
@ -45,6 +50,7 @@ const TradeHistory = () => {
|
|||
} = useTradeHistory()
|
||||
const { width } = useViewport()
|
||||
const { connected } = useWallet()
|
||||
const { hiddenAccounts } = useHiddenMangoAccounts()
|
||||
const showTableView = width ? width > breakpoints.md : false
|
||||
|
||||
const formattedTableData = useCallback(() => {
|
||||
|
@ -142,6 +148,14 @@ const TradeHistory = () => {
|
|||
{tableData.map((trade, index: number) => {
|
||||
const { side, price, market, size, feeCost, liquidity, value } =
|
||||
trade
|
||||
|
||||
let counterpartyAddress = ''
|
||||
if ('taker' in trade) {
|
||||
counterpartyAddress =
|
||||
trade.liquidity === 'Taker'
|
||||
? trade.maker.toString()
|
||||
: trade.taker.toString()
|
||||
}
|
||||
return (
|
||||
<TrBody
|
||||
key={`${side}${size}${price}${index}`}
|
||||
|
@ -184,29 +198,41 @@ const TradeHistory = () => {
|
|||
<Td className="xl:!pl-0">
|
||||
{'taker' in trade ? (
|
||||
<div className="flex justify-end">
|
||||
<Tooltip
|
||||
content={`View Counterparty ${abbreviateAddress(
|
||||
trade.liquidity === 'Taker'
|
||||
? new PublicKey(trade.maker)
|
||||
: new PublicKey(trade.taker),
|
||||
)}`}
|
||||
delay={0}
|
||||
>
|
||||
<a
|
||||
className=""
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={`/?address=${
|
||||
trade.liquidity === 'Taker'
|
||||
? trade.maker
|
||||
: trade.taker
|
||||
}`}
|
||||
{!hiddenAccounts?.includes(counterpartyAddress) ? (
|
||||
<Tooltip
|
||||
content={t('trade:tooltip-view-counterparty', {
|
||||
pk: abbreviateAddress(
|
||||
trade.liquidity === 'Taker'
|
||||
? new PublicKey(trade.maker)
|
||||
: new PublicKey(trade.taker),
|
||||
),
|
||||
})}
|
||||
delay={0}
|
||||
>
|
||||
<IconButton size="small">
|
||||
<UsersIcon className="h-4 w-4" />
|
||||
<a
|
||||
className=""
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={`/?address=${counterpartyAddress}`}
|
||||
>
|
||||
<IconButton size="small">
|
||||
<UsersIcon className="h-4 w-4" />
|
||||
</IconButton>
|
||||
</a>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip
|
||||
content={t('trade:tooltip-private-counterparty')}
|
||||
>
|
||||
<IconButton
|
||||
className="bg-th-bkg-1"
|
||||
disabled
|
||||
size="small"
|
||||
>
|
||||
<EyeSlashIcon className="h-4 w-4" />
|
||||
</IconButton>
|
||||
</a>
|
||||
</Tooltip>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
</Td>
|
||||
|
@ -220,6 +246,13 @@ const TradeHistory = () => {
|
|||
<div>
|
||||
{combinedTradeHistory.map((trade, index: number) => {
|
||||
const { side, price, market, size, liquidity } = trade
|
||||
let counterpartyAddress = ''
|
||||
if ('taker' in trade) {
|
||||
counterpartyAddress =
|
||||
trade.liquidity === 'Taker'
|
||||
? trade.maker.toString()
|
||||
: trade.taker.toString()
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-between border-b border-th-bkg-3 p-4"
|
||||
|
@ -259,18 +292,41 @@ const TradeHistory = () => {
|
|||
</div>
|
||||
</div>
|
||||
{'taker' in trade ? (
|
||||
<a
|
||||
className=""
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={`/?address=${
|
||||
liquidity === 'Taker' ? trade.maker : trade.taker
|
||||
}`}
|
||||
>
|
||||
<IconButton size="small">
|
||||
<UsersIcon className="h-4 w-4" />
|
||||
</IconButton>
|
||||
</a>
|
||||
!hiddenAccounts?.includes(counterpartyAddress) ? (
|
||||
<Tooltip
|
||||
content={t('trade:tooltip-view-counterparty', {
|
||||
pk: abbreviateAddress(
|
||||
liquidity === 'Taker'
|
||||
? new PublicKey(trade.maker)
|
||||
: new PublicKey(trade.taker),
|
||||
),
|
||||
})}
|
||||
delay={0}
|
||||
>
|
||||
<a
|
||||
className=""
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={`/?address=${counterpartyAddress}`}
|
||||
>
|
||||
<IconButton size="small">
|
||||
<UsersIcon className="h-4 w-4" />
|
||||
</IconButton>
|
||||
</a>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip
|
||||
content={t('trade:tooltip-private-counterparty')}
|
||||
>
|
||||
<IconButton
|
||||
className="bg-th-bkg-1"
|
||||
disabled
|
||||
size="small"
|
||||
>
|
||||
<EyeSlashIcon className="h-4 w-4" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -39,7 +39,7 @@ export const getOracleProvider = (
|
|||
case OracleProvider.Switchboard:
|
||||
return [
|
||||
'Switchboard',
|
||||
`https://switchboard.xyz/explorer/3/${marketOrBase.oracle.toString()}`,
|
||||
`https://app.switchboard.xyz/solana/mainnet-beta/feed/${marketOrBase.oracle.toString()}`,
|
||||
]
|
||||
case OracleProvider.Stub:
|
||||
return ['Stub', '']
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@blockworks-foundation/mango-feeds": "0.1.7",
|
||||
"@blockworks-foundation/mango-v4": "^0.19.15",
|
||||
"@blockworks-foundation/mango-v4-settings": "0.2.6",
|
||||
"@blockworks-foundation/mango-v4": "^0.19.20",
|
||||
"@blockworks-foundation/mango-v4-settings": "0.2.8",
|
||||
"@headlessui/react": "1.6.6",
|
||||
"@heroicons/react": "2.0.10",
|
||||
"@metaplex-foundation/js": "0.19.4",
|
||||
|
|
|
@ -197,6 +197,7 @@
|
|||
"vote": "Vote",
|
||||
"yes": "Yes",
|
||||
"you": "You",
|
||||
"using-ledger": "Using Ledger"
|
||||
"using-ledger": "Using Ledger",
|
||||
"sign-to-in-app-notifications": "Sign to in app notifications"
|
||||
}
|
||||
|
|
@ -106,9 +106,11 @@
|
|||
"tooltip-ioc": "Immediate-Or-Cancel (IOC) orders are guaranteed to be the taker and must be executed immediately. Any portion of the order that can't be filled immediately will be cancelled",
|
||||
"tooltip-insured": "Whether or not {{tokenOrMarket}} losses can be recovered from the insurance fund in the event of bankruptcies.",
|
||||
"tooltip-post": "Post orders are guaranteed to be the maker or they will be canceled",
|
||||
"tooltip-private-counterparty": "Counterparty has Private Account enabled",
|
||||
"tooltip-slippage": "An estimate of the difference between the current price and the price your trade will be executed at",
|
||||
"tooltip-volume-alert": "Volume Alert Settings",
|
||||
"tooltip-stable-price": "Stable price is used in a safety mechanism that limits a user's ability to enter risky positions when the oracle price is changing rapidly",
|
||||
"tooltip-view-counterparty": "View counterparty {{pk}}",
|
||||
"tooltip-volume-alert": "Volume Alert Settings",
|
||||
"total-pnl": "Total PnL",
|
||||
"trade-sounds-tooltip": "Play a sound alert for every new trade",
|
||||
"trades": "Trades",
|
||||
|
|
|
@ -197,6 +197,7 @@
|
|||
"vote": "Vote",
|
||||
"yes": "Yes",
|
||||
"you": "You",
|
||||
"using-ledger": "Using Ledger"
|
||||
"using-ledger": "Using Ledger",
|
||||
"sign-to-in-app-notifications": "Sign to in app notifications"
|
||||
}
|
||||
|
|
@ -106,9 +106,11 @@
|
|||
"tooltip-ioc": "Immediate-Or-Cancel (IOC) orders are guaranteed to be the taker and must be executed immediately. Any portion of the order that can't be filled immediately will be cancelled",
|
||||
"tooltip-insured": "Whether or not {{tokenOrMarket}} losses can be recovered from the insurance fund in the event of bankruptcies.",
|
||||
"tooltip-post": "Post orders are guaranteed to be the maker or they will be canceled",
|
||||
"tooltip-private-counterparty": "Counterparty has Private Account enabled",
|
||||
"tooltip-slippage": "An estimate of the difference between the current price and the price your trade will be executed at",
|
||||
"tooltip-volume-alert": "Volume Alert Settings",
|
||||
"tooltip-stable-price": "Stable price is used in a safety mechanism that limits a user's ability to enter risky positions when the oracle price is changing rapidly",
|
||||
"tooltip-view-counterparty": "View counterparty {{pk}}",
|
||||
"tooltip-volume-alert": "Volume Alert Settings",
|
||||
"total-pnl": "Total PnL",
|
||||
"trade-sounds-tooltip": "Play a sound alert for every new trade",
|
||||
"trades": "Trades",
|
||||
|
@ -119,6 +121,7 @@
|
|||
"tweet-position": "Tweet",
|
||||
"unrealized-pnl": "Unrealized PnL",
|
||||
"unsettled": "Unsettled",
|
||||
"view-counterparty": "View counterparty {{pk}}",
|
||||
"volume-alert": "Volume Alert",
|
||||
"volume-alert-desc": "Play a sound whenever volume exceeds your alert threshold"
|
||||
}
|
|
@ -197,6 +197,7 @@
|
|||
"vote": "Vote",
|
||||
"yes": "Yes",
|
||||
"you": "You",
|
||||
"using-ledger": "Using Ledger"
|
||||
"using-ledger": "Using Ledger",
|
||||
"sign-to-in-app-notifications": "Sign to in app notifications"
|
||||
}
|
||||
|
|
@ -106,9 +106,11 @@
|
|||
"tooltip-ioc": "Immediate-Or-Cancel (IOC) orders are guaranteed to be the taker and must be executed immediately. Any portion of the order that can't be filled immediately will be cancelled",
|
||||
"tooltip-insured": "Whether or not {{tokenOrMarket}} losses can be recovered from the insurance fund in the event of bankruptcies.",
|
||||
"tooltip-post": "Post orders are guaranteed to be the maker or they will be canceled",
|
||||
"tooltip-private-counterparty": "Counterparty has Private Account enabled",
|
||||
"tooltip-slippage": "An estimate of the difference between the current price and the price your trade will be executed at",
|
||||
"tooltip-volume-alert": "Volume Alert Settings",
|
||||
"tooltip-stable-price": "Stable price is used in a safety mechanism that limits a user's ability to enter risky positions when the oracle price is changing rapidly",
|
||||
"tooltip-view-counterparty": "View counterparty {{pk}}",
|
||||
"tooltip-volume-alert": "Volume Alert Settings",
|
||||
"total-pnl": "Total PnL",
|
||||
"trade-sounds-tooltip": "Play a sound alert for every new trade",
|
||||
"trades": "Trades",
|
||||
|
@ -119,6 +121,7 @@
|
|||
"tweet-position": "Tweet",
|
||||
"unrealized-pnl": "Unrealized PnL",
|
||||
"unsettled": "Unsettled",
|
||||
"view-counterparty": "View counterparty {{pk}}",
|
||||
"volume-alert": "Volume Alert",
|
||||
"volume-alert-desc": "Play a sound whenever volume exceeds your alert threshold"
|
||||
}
|
|
@ -196,6 +196,7 @@
|
|||
"vote": "投票",
|
||||
"yes": "是",
|
||||
"you": "你",
|
||||
"using-ledger": "Using Ledger"
|
||||
"using-ledger": "Using Ledger",
|
||||
"sign-to-in-app-notifications": "Sign to in app notifications"
|
||||
}
|
||||
|
|
@ -110,8 +110,10 @@
|
|||
"tooltip-insured": "如果发生破产,{{tokenOrMarket}}损失是否可以从保险基金中归还",
|
||||
"tooltip-ioc": "IOC交易若不吃单就会被取消。任何无法立刻成交的部分将被取消",
|
||||
"tooltip-post": "Post交易若不挂单就会被取消。",
|
||||
"tooltip-private-counterparty": "Counterparty has Private Account enabled",
|
||||
"tooltip-slippage": "当前价格与您的交易将执行的价格之间的差值的估计",
|
||||
"tooltip-stable-price": "稳定价格用于一个安全机制。此机制可以限制用户在预言机价格快速波动时下风险高的订单",
|
||||
"tooltip-view-counterparty": "View counterparty {{pk}}",
|
||||
"tooltip-volume-alert": "交易量警报设定",
|
||||
"total-pnl": "总盈亏",
|
||||
"trade-sounds-tooltip": "为每笔新交易播放警报声音",
|
||||
|
@ -119,6 +121,7 @@
|
|||
"tweet-position": "分享至Twitter",
|
||||
"unrealized-pnl": "未实现盈亏",
|
||||
"unsettled": "未结清",
|
||||
"view-counterparty": "View counterparty {{pk}}",
|
||||
"volume-alert": "交易量警报",
|
||||
"volume-alert-desc": "交易量超过警报设定时播放声音"
|
||||
}
|
|
@ -196,5 +196,7 @@
|
|||
"vote": "投票",
|
||||
"yes": "是",
|
||||
"you": "你",
|
||||
"using-ledger": "Using Ledger",
|
||||
"sign-to-in-app-notifications": "Sign to in app notifications"
|
||||
"using-ledger": "Using Ledger"
|
||||
}
|
||||
|
|
|
@ -106,8 +106,10 @@
|
|||
"tooltip-insured": "如果發生破產,{{tokenOrMarket}}損失是否可以從保險基金中歸還",
|
||||
"tooltip-ioc": "IOC交易若不吃單就會被取消。任何無法立刻成交的部分將被取消",
|
||||
"tooltip-post": "Post交易若不掛單就會被取消。",
|
||||
"tooltip-private-counterparty": "Counterparty has Private Account enabled",
|
||||
"tooltip-slippage": "當前價格與您的交易將執行的價格之間的差值的估計",
|
||||
"tooltip-stable-price": "穩定價格用於一個安全機制。此機制可以限制用戶在預言機價格快速波動時下風險高的訂單",
|
||||
"tooltip-view-counterparty": "View counterparty {{pk}}",
|
||||
"tooltip-volume-alert": "交易量警報設定",
|
||||
"total-pnl": "總盈虧",
|
||||
"trade-sounds-tooltip": "為每筆新交易播放警報聲音",
|
||||
|
@ -119,6 +121,7 @@
|
|||
"tweet-position": "分享至Twitter",
|
||||
"unrealized-pnl": "未實現盈虧",
|
||||
"unsettled": "未結清",
|
||||
"view-counterparty": "View counterparty {{pk}}",
|
||||
"volume-alert": "交易量警報",
|
||||
"volume-alert-desc": "交易量超過警報設定時播放聲音"
|
||||
}
|
|
@ -9,6 +9,7 @@ import {
|
|||
Keypair,
|
||||
PublicKey,
|
||||
RecentPrioritizationFees,
|
||||
TransactionInstruction,
|
||||
} from '@solana/web3.js'
|
||||
import { OpenOrders, Order } from '@project-serum/serum/lib/market'
|
||||
import { Orderbook } from '@project-serum/serum'
|
||||
|
@ -114,10 +115,18 @@ export const emptyWallet = new EmptyWallet(Keypair.generate())
|
|||
|
||||
const initMangoClient = (
|
||||
provider: AnchorProvider,
|
||||
opts = { prioritizationFee: DEFAULT_PRIORITY_FEE },
|
||||
opts: {
|
||||
prioritizationFee: number
|
||||
prependedGlobalAdditionalInstructions: TransactionInstruction[]
|
||||
} = {
|
||||
prioritizationFee: DEFAULT_PRIORITY_FEE,
|
||||
prependedGlobalAdditionalInstructions: [],
|
||||
},
|
||||
): MangoClient => {
|
||||
return MangoClient.connect(provider, CLUSTER, MANGO_V4_ID[CLUSTER], {
|
||||
prioritizationFee: opts.prioritizationFee,
|
||||
prependedGlobalAdditionalInstructions:
|
||||
opts.prependedGlobalAdditionalInstructions,
|
||||
idsSource: 'api',
|
||||
postSendTxCallback: ({ txid }: { txid: string }) => {
|
||||
notify({
|
||||
|
@ -193,6 +202,7 @@ export type MangoStore = {
|
|||
details: ProfileDetails | null
|
||||
loadDetails: boolean
|
||||
}
|
||||
prependedGlobalAdditionalInstructions: TransactionInstruction[]
|
||||
priorityFee: number
|
||||
selectedMarket: {
|
||||
name: string | undefined
|
||||
|
@ -283,6 +293,9 @@ export type MangoStore = {
|
|||
connectMangoClientWithWallet: (wallet: WalletAdapter) => Promise<void>
|
||||
loadMarketFills: () => Promise<void>
|
||||
updateConnection: (url: string) => void
|
||||
setPrependedGlobalAdditionalInstructions: (
|
||||
instructions: TransactionInstruction[],
|
||||
) => void
|
||||
estimatePriorityFee: (feeMultiplier: number) => Promise<void>
|
||||
}
|
||||
}
|
||||
|
@ -358,6 +371,7 @@ const mangoStore = create<MangoStore>()(
|
|||
details: { profile_name: '', trader_category: '', wallet_pk: '' },
|
||||
},
|
||||
priorityFee: DEFAULT_PRIORITY_FEE,
|
||||
prependedGlobalAdditionalInstructions: [],
|
||||
selectedMarket: {
|
||||
name: 'SOL/USDC',
|
||||
current: undefined,
|
||||
|
@ -1016,8 +1030,11 @@ const mangoStore = create<MangoStore>()(
|
|||
)
|
||||
provider.opts.skipPreflight = true
|
||||
const priorityFee = get().priorityFee ?? DEFAULT_PRIORITY_FEE
|
||||
|
||||
const client = initMangoClient(provider, {
|
||||
prioritizationFee: priorityFee,
|
||||
prependedGlobalAdditionalInstructions:
|
||||
get().prependedGlobalAdditionalInstructions,
|
||||
})
|
||||
|
||||
set((s) => {
|
||||
|
@ -1033,6 +1050,25 @@ const mangoStore = create<MangoStore>()(
|
|||
}
|
||||
}
|
||||
},
|
||||
async setPrependedGlobalAdditionalInstructions(
|
||||
instructions: TransactionInstruction[],
|
||||
) {
|
||||
const set = get().set
|
||||
const client = mangoStore.getState().client
|
||||
|
||||
const provider = client.program.provider as AnchorProvider
|
||||
provider.opts.skipPreflight = true
|
||||
|
||||
const newClient = initMangoClient(provider, {
|
||||
prioritizationFee: get().priorityFee,
|
||||
prependedGlobalAdditionalInstructions: instructions,
|
||||
})
|
||||
|
||||
set((s) => {
|
||||
s.client = newClient
|
||||
s.prependedGlobalAdditionalInstructions = instructions
|
||||
})
|
||||
},
|
||||
async fetchProfileDetails(walletPk: string) {
|
||||
const set = get().set
|
||||
set((state) => {
|
||||
|
@ -1128,7 +1164,11 @@ const mangoStore = create<MangoStore>()(
|
|||
options,
|
||||
)
|
||||
newProvider.opts.skipPreflight = true
|
||||
const newClient = initMangoClient(newProvider)
|
||||
const newClient = initMangoClient(newProvider, {
|
||||
prependedGlobalAdditionalInstructions:
|
||||
get().prependedGlobalAdditionalInstructions,
|
||||
prioritizationFee: DEFAULT_PRIORITY_FEE,
|
||||
})
|
||||
set((state) => {
|
||||
state.connection = newConnection
|
||||
state.client = newClient
|
||||
|
@ -1180,6 +1220,8 @@ const mangoStore = create<MangoStore>()(
|
|||
provider.opts.skipPreflight = true
|
||||
const newClient = initMangoClient(provider, {
|
||||
prioritizationFee: feeEstimate,
|
||||
prependedGlobalAdditionalInstructions:
|
||||
get().prependedGlobalAdditionalInstructions,
|
||||
})
|
||||
set((state) => {
|
||||
state.priorityFee = feeEstimate
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
Serum3Market,
|
||||
} from '@blockworks-foundation/mango-v4'
|
||||
import { Modify } from '@blockworks-foundation/mango-v4'
|
||||
import { JsonMetadata } from '@metaplex-foundation/js'
|
||||
import { Event } from '@project-serum/serum/lib/queue'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import { formatTradeHistory } from 'hooks/useTradeHistory'
|
||||
|
@ -305,6 +306,7 @@ export interface NFT {
|
|||
name: string
|
||||
mint: string
|
||||
tokenAccount: string
|
||||
json: JsonMetadata | null
|
||||
}
|
||||
|
||||
export interface PerpStatsItem {
|
||||
|
|
|
@ -49,7 +49,7 @@ export const FAVORITE_SWAPS_KEY = 'favoriteSwaps-0.1'
|
|||
|
||||
export const THEME_KEY = 'theme-0.1'
|
||||
|
||||
export const RPC_PROVIDER_KEY = 'rpcProviderKey-0.8'
|
||||
export const RPC_PROVIDER_KEY = 'rpcProviderKey-0.9'
|
||||
|
||||
export const PRIORITY_FEE_KEY = 'priorityFeeKey-0.2'
|
||||
|
||||
|
|
|
@ -84,5 +84,5 @@ export const nftThemeMeta: NftThemeMeta = {
|
|||
|
||||
export const CUSTOM_SKINS: { [key: string]: string } = {
|
||||
bonk: '6FUYsgvSPiLsMpKZqLWswkw7j4juudZyVopU6RYKLkQ3',
|
||||
pepe: '6FUYsgvSPiLsMpKZqLWswkw7j4juudZyVopU6RYKLkQ3',
|
||||
pepe: 'B4QhXJaSnUBT8aWiPz5GmB9sjXdQDtHXv8x1WsRFHNJx',
|
||||
}
|
||||
|
|
|
@ -93,6 +93,7 @@ const enhanceNFT = (nft: NftWithATA) => {
|
|||
collectionAddress: nft.collection?.address.toBase58(),
|
||||
mint: nft.mint.address.toBase58(),
|
||||
tokenAccount: nft.tokenAccountAddress?.toBase58() || '',
|
||||
json: nft.json,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
20
yarn.lock
20
yarn.lock
|
@ -26,24 +26,24 @@
|
|||
dependencies:
|
||||
ws "^8.13.0"
|
||||
|
||||
"@blockworks-foundation/mango-v4-settings@0.2.6":
|
||||
version "0.2.6"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-v4-settings/-/mango-v4-settings-0.2.6.tgz#725a8cf669e164cd7694d97989472f7852afad68"
|
||||
integrity sha512-RK8O8lbflIN9IgNE1uUkjrtlv/7f0BjIqTwcuLNFos6/e/Q2/AnlXRlD5Y9WnO6xS7mXNsw9kr05xCxeYZzM1Q==
|
||||
"@blockworks-foundation/mango-v4-settings@0.2.8":
|
||||
version "0.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-v4-settings/-/mango-v4-settings-0.2.8.tgz#fa88d6ddb6dcc1dd1cd8f75ab96bc0d41629799d"
|
||||
integrity sha512-20h5+Vky3YZfd3ekcnMgUxp1XaddFgtSNE6SPCy1oOAOjzvDHgZ3AVJd95WTTjTONGTZqqmTbP2ReWIu4ZfP6w==
|
||||
dependencies:
|
||||
bn.js "^5.2.1"
|
||||
eslint-config-prettier "^9.0.0"
|
||||
|
||||
"@blockworks-foundation/mango-v4@^0.19.15":
|
||||
version "0.19.15"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-v4/-/mango-v4-0.19.15.tgz#0e62d9c2f06c103b39abb648a529702048fd2706"
|
||||
integrity sha512-aWtnBgbPJ2F1RZ4dCpu2ans5KQmt7d4en55Dp/n17hWjRUGA4tTPwthE8uoeRqc6ih19ldgDj2oMQAl6XzWQzg==
|
||||
"@blockworks-foundation/mango-v4@^0.19.20":
|
||||
version "0.19.20"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-v4/-/mango-v4-0.19.20.tgz#5932b98bfc94eed1f7cdffdef535afe19c1cce03"
|
||||
integrity sha512-UGPmJ/NPDdIHM7BcYSOOZHDYDse6mefTGI2IK9MRxaqAVpqfFj3Kjk8mM12O+CW1cySaMl3NUAyR/Ul0jGY+jg==
|
||||
dependencies:
|
||||
"@coral-xyz/anchor" "^0.27.0"
|
||||
"@project-serum/serum" "0.13.65"
|
||||
"@pythnetwork/client" "~2.14.0"
|
||||
"@solana/spl-token" "0.3.7"
|
||||
"@solana/web3.js" "^1.73.2"
|
||||
"@solana/web3.js" "^1.78.2"
|
||||
"@switchboard-xyz/sbv2-lite" "^0.1.6"
|
||||
big.js "^6.1.1"
|
||||
binance-api-node "^0.12.0"
|
||||
|
@ -1999,7 +1999,7 @@
|
|||
"@wallet-standard/app" "^1.0.1"
|
||||
"@wallet-standard/base" "^1.0.1"
|
||||
|
||||
"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.22.0", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.44.3", "@solana/web3.js@^1.50.1", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.63.1", "@solana/web3.js@^1.66.2", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.73.2", "@solana/web3.js@^1.78.3":
|
||||
"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.22.0", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.44.3", "@solana/web3.js@^1.50.1", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.63.1", "@solana/web3.js@^1.66.2", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.78.2", "@solana/web3.js@^1.78.3":
|
||||
version "1.78.4"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.78.4.tgz#e8ca9abe4ec2af5fc540c1d272efee24aaffedb3"
|
||||
integrity sha512-up5VG1dK+GPhykmuMIozJZBbVqpm77vbOG6/r5dS7NBGZonwHfTLdBbsYc3rjmaQ4DpCXUa3tUc4RZHRORvZrw==
|
||||
|
|
Loading…
Reference in New Issue