diff --git a/README.md b/README.md index 12468a37..cfeeb4dc 100644 --- a/README.md +++ b/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. diff --git a/components/SideNav.tsx b/components/SideNav.tsx index c1a7a8d9..1a687568 100644 --- a/components/SideNav.tsx +++ b/components/SideNav.tsx @@ -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 diff --git a/components/governance/ListToken/ListToken.tsx b/components/governance/ListToken/ListToken.tsx index 7d015aa4..4a7e0273 100644 --- a/components/governance/ListToken/ListToken.tsx +++ b/components/governance/ListToken/ListToken.tsx @@ -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) diff --git a/components/modals/CreateAccountModal.tsx b/components/modals/CreateAccountModal.tsx index 2ea38789..088f6d46 100644 --- a/components/modals/CreateAccountModal.tsx +++ b/components/modals/CreateAccountModal.tsx @@ -15,7 +15,7 @@ const CreateAccountModal = ({ isOpen, onClose }: ModalProps) => { return ( -
+
diff --git a/components/modals/CreateSwitchboardOracleModal.tsx b/components/modals/CreateSwitchboardOracleModal.tsx index f63a709e..ef09957b 100644 --- a/components/modals/CreateSwitchboardOracleModal.tsx +++ b/components/modals/CreateSwitchboardOracleModal.tsx @@ -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, diff --git a/components/modals/DashboardSuggestedValuesModal.tsx b/components/modals/DashboardSuggestedValuesModal.tsx index 55d8a407..013d71af 100644 --- a/components/modals/DashboardSuggestedValuesModal.tsx +++ b/components/modals/DashboardSuggestedValuesModal.tsx @@ -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, diff --git a/components/modals/MangoAccountsListModal.tsx b/components/modals/MangoAccountsListModal.tsx index 5eb49bda..002e0446 100644 --- a/components/modals/MangoAccountsListModal.tsx +++ b/components/modals/MangoAccountsListModal.tsx @@ -102,7 +102,7 @@ const MangoAccountsListModal = ({ return (
-
+

{t('accounts')}

{loading ? ( diff --git a/components/trade/TradeHistory.tsx b/components/trade/TradeHistory.tsx index a4268f76..71456a4d 100644 --- a/components/trade/TradeHistory.tsx +++ b/components/trade/TradeHistory.tsx @@ -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 ( { {'taker' in trade ? (
- - - - + + + + + + + ) : ( + + + - - + + )}
) : null} @@ -220,6 +246,13 @@ const TradeHistory = () => {
{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 (
{
{'taker' in trade ? ( - - - - - + !hiddenAccounts?.includes(counterpartyAddress) ? ( + + + + + + + + ) : ( + + + + + + ) ) : null}
diff --git a/hooks/useOracleProvider.ts b/hooks/useOracleProvider.ts index 43065c09..02e380db 100644 --- a/hooks/useOracleProvider.ts +++ b/hooks/useOracleProvider.ts @@ -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', ''] diff --git a/package.json b/package.json index 39823dab..5f6e8bba 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/public/locales/en/common.json b/public/locales/en/common.json index f92c09d2..395118fc 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -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" } \ No newline at end of file diff --git a/public/locales/en/trade.json b/public/locales/en/trade.json index 81199e5e..2af7995a 100644 --- a/public/locales/en/trade.json +++ b/public/locales/en/trade.json @@ -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", diff --git a/public/locales/es/common.json b/public/locales/es/common.json index f92c09d2..395118fc 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -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" } \ No newline at end of file diff --git a/public/locales/es/trade.json b/public/locales/es/trade.json index 3947dc79..3f6c3589 100644 --- a/public/locales/es/trade.json +++ b/public/locales/es/trade.json @@ -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" } \ No newline at end of file diff --git a/public/locales/ru/common.json b/public/locales/ru/common.json index f92c09d2..395118fc 100644 --- a/public/locales/ru/common.json +++ b/public/locales/ru/common.json @@ -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" } \ No newline at end of file diff --git a/public/locales/ru/trade.json b/public/locales/ru/trade.json index 3947dc79..3f6c3589 100644 --- a/public/locales/ru/trade.json +++ b/public/locales/ru/trade.json @@ -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" } \ No newline at end of file diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index 48c30637..263b35e7 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -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" } \ No newline at end of file diff --git a/public/locales/zh/trade.json b/public/locales/zh/trade.json index 59954cd4..ff7474ac 100644 --- a/public/locales/zh/trade.json +++ b/public/locales/zh/trade.json @@ -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": "交易量超过警报设定时播放声音" } \ No newline at end of file diff --git a/public/locales/zh_tw/common.json b/public/locales/zh_tw/common.json index 0a91606a..33b987ba 100644 --- a/public/locales/zh_tw/common.json +++ b/public/locales/zh_tw/common.json @@ -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" } diff --git a/public/locales/zh_tw/trade.json b/public/locales/zh_tw/trade.json index c0c274a6..9940b73e 100644 --- a/public/locales/zh_tw/trade.json +++ b/public/locales/zh_tw/trade.json @@ -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": "交易量超過警報設定時播放聲音" } \ No newline at end of file diff --git a/store/mangoStore.ts b/store/mangoStore.ts index 07bd129e..af1d0de6 100644 --- a/store/mangoStore.ts +++ b/store/mangoStore.ts @@ -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 loadMarketFills: () => Promise updateConnection: (url: string) => void + setPrependedGlobalAdditionalInstructions: ( + instructions: TransactionInstruction[], + ) => void estimatePriorityFee: (feeMultiplier: number) => Promise } } @@ -358,6 +371,7 @@ const mangoStore = create()( 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()( ) 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()( } } }, + 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()( 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()( provider.opts.skipPreflight = true const newClient = initMangoClient(provider, { prioritizationFee: feeEstimate, + prependedGlobalAdditionalInstructions: + get().prependedGlobalAdditionalInstructions, }) set((state) => { state.priorityFee = feeEstimate diff --git a/types/index.ts b/types/index.ts index 7c5a293c..28a62ef7 100644 --- a/types/index.ts +++ b/types/index.ts @@ -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 { diff --git a/utils/constants.ts b/utils/constants.ts index 749a7622..02c867d7 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -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' diff --git a/utils/theme.ts b/utils/theme.ts index f11379a9..103d476f 100644 --- a/utils/theme.ts +++ b/utils/theme.ts @@ -84,5 +84,5 @@ export const nftThemeMeta: NftThemeMeta = { export const CUSTOM_SKINS: { [key: string]: string } = { bonk: '6FUYsgvSPiLsMpKZqLWswkw7j4juudZyVopU6RYKLkQ3', - pepe: '6FUYsgvSPiLsMpKZqLWswkw7j4juudZyVopU6RYKLkQ3', + pepe: 'B4QhXJaSnUBT8aWiPz5GmB9sjXdQDtHXv8x1WsRFHNJx', } diff --git a/utils/tokens.ts b/utils/tokens.ts index 6953fd0f..73f15996 100644 --- a/utils/tokens.ts +++ b/utils/tokens.ts @@ -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, } } diff --git a/yarn.lock b/yarn.lock index 50e318dd..8db16077 100644 --- a/yarn.lock +++ b/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==