Support transitioning from deprecated markets
This commit is contained in:
parent
1891c159a8
commit
6140b5dfd8
|
@ -6,7 +6,7 @@
|
|||
"dependencies": {
|
||||
"@ant-design/icons": "^4.2.1",
|
||||
"@craco/craco": "^5.6.4",
|
||||
"@project-serum/serum": "^0.12.0",
|
||||
"@project-serum/serum": "^0.12.2",
|
||||
"@project-serum/sol-wallet-adapter": "^0.1.0",
|
||||
"@solana/web3.js": "^0.71.10",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import { Divider, Typography, Button } from 'antd';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
useMarket,
|
||||
useOpenOrders,
|
||||
useBalances,
|
||||
useMarketsList,
|
||||
DEFAULT_MARKET,
|
||||
} from '../utils/markets';
|
||||
import FloatingElement from './layout/FloatingElement';
|
||||
import CheckOutlined from '@ant-design/icons/lib/icons/CheckOutlined';
|
||||
import BalancesTable from './UserInfoTable/BalancesTable';
|
||||
import OpenOrderTable from './UserInfoTable/OpenOrderTable';
|
||||
|
||||
const Title = styled.div`
|
||||
color: rgba(255, 255, 255, 1);
|
||||
`;
|
||||
|
||||
export default function DeprecatedMarketInstructions() {
|
||||
const { marketName, setMarketAddress } = useMarket();
|
||||
const liveMarkets = useMarketsList();
|
||||
const openOrders = useOpenOrders();
|
||||
const balances = useBalances();
|
||||
|
||||
const needToCancelOrders = !openOrders || openOrders.length > 0;
|
||||
const needToSettleFunds =
|
||||
!balances ||
|
||||
balances.some(({ orders, unsettled }) => orders > 0 || unsettled > 0);
|
||||
const liveMarket =
|
||||
liveMarkets.find(({ name }) => name === marketName) || DEFAULT_MARKET;
|
||||
console.log('liveMarkets', liveMarkets);
|
||||
return (
|
||||
<FloatingElement>
|
||||
<Title>Migrate off of deprecated market</Title>
|
||||
<br />
|
||||
<Typography>
|
||||
This {marketName} market is deprecated (inactive).
|
||||
</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
To transition to the new and upgraded {marketName} market, please do the
|
||||
following:
|
||||
</Typography>
|
||||
<br />
|
||||
<Divider>
|
||||
{!needToCancelOrders && <CheckOutlined />} Cancel your orders
|
||||
</Divider>
|
||||
{needToCancelOrders && <OpenOrderTable openOrders={openOrders} />}
|
||||
<Divider>
|
||||
{!needToSettleFunds && <CheckOutlined />} Settle your funds
|
||||
</Divider>
|
||||
{needToSettleFunds && <BalancesTable balances={balances} />}
|
||||
<Divider>Switch to new market</Divider>
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<Button
|
||||
onClick={() =>
|
||||
liveMarket?.address &&
|
||||
setMarketAddress(liveMarket.address.toBase58())
|
||||
}
|
||||
disabled={needToSettleFunds || needToCancelOrders}
|
||||
>
|
||||
Switch to live {liveMarket.name} market
|
||||
</Button>
|
||||
</div>
|
||||
</FloatingElement>
|
||||
);
|
||||
}
|
|
@ -1,13 +1,18 @@
|
|||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Col, Popover, Row, Select } from 'antd';
|
||||
import { Col, Popover, Row, Select, Typography } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
import Orderbook from '../components/Orderbook';
|
||||
import UserInfoTable from '../components/UserInfoTable';
|
||||
import StandaloneBalancesDisplay from '../components/StandaloneBalancesDisplay';
|
||||
import { useMarket, useMarketsList } from '../utils/markets';
|
||||
import {
|
||||
useMarket,
|
||||
useMarketsList,
|
||||
useUnmigratedDeprecatedMarketsList,
|
||||
} from '../utils/markets';
|
||||
import TradeForm from '../components/TradeForm';
|
||||
import TradesTable from '../components/TradesTable';
|
||||
import LinkAddress from '../components/LinkAddress';
|
||||
import DeprecatedMarketInstructions from '../components/DeprecatedMarketInstructions';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
|
||||
const { Option } = Select;
|
||||
|
@ -23,8 +28,9 @@ const Wrapper = styled.div`
|
|||
`;
|
||||
|
||||
export default function TradePage() {
|
||||
const { market, marketName, setMarketAddress } = useMarket();
|
||||
const { marketName, market, deprecated } = useMarket();
|
||||
const markets = useMarketsList();
|
||||
const deprecatedMarkets = useUnmigratedDeprecatedMarketsList();
|
||||
const [dimensions, setDimensions] = useState({
|
||||
height: window.innerHeight,
|
||||
width: window.innerWidth,
|
||||
|
@ -57,14 +63,16 @@ export default function TradePage() {
|
|||
changeOrderRef.current && changeOrderRef.current({ size }),
|
||||
};
|
||||
const getComponent = useCallback(() => {
|
||||
if (width < 1000) {
|
||||
if (deprecated) {
|
||||
return <RenderDeprecatedMarket />;
|
||||
} else if (width < 1000) {
|
||||
return <RenderSmaller {...componentProps} />;
|
||||
} else if (width < 1450) {
|
||||
return <RenderSmall {...componentProps} />;
|
||||
} else {
|
||||
return <RenderNormal {...componentProps} />;
|
||||
}
|
||||
}, [width, componentProps]);
|
||||
}, [width, componentProps, deprecated]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -75,27 +83,7 @@ export default function TradePage() {
|
|||
gutter={16}
|
||||
>
|
||||
<Col>
|
||||
<Select
|
||||
size={'large'}
|
||||
bordered={true}
|
||||
onSelect={setMarketAddress}
|
||||
value={market?.address?.toBase58()}
|
||||
listHeight={400}
|
||||
>
|
||||
{markets.map(({ name, address }, i) => (
|
||||
<Option
|
||||
value={address.toBase58()}
|
||||
key={address}
|
||||
style={{
|
||||
padding: '10px 0',
|
||||
textAlign: 'center',
|
||||
backgroundColor: i % 2 === 0 ? 'rgb(39, 44, 61)' : null,
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
<MarketSelector markets={markets} placeholder={'Select market'} />
|
||||
</Col>
|
||||
{market ? (
|
||||
<Col>
|
||||
|
@ -109,6 +97,22 @@ export default function TradePage() {
|
|||
</Popover>
|
||||
</Col>
|
||||
) : null}
|
||||
{deprecatedMarkets && deprecatedMarkets.length > 0 && (
|
||||
<React.Fragment>
|
||||
<Col>
|
||||
<Typography>
|
||||
You have unsettled funds on deprecated markets! Please go
|
||||
through them to claim your funds.
|
||||
</Typography>
|
||||
</Col>
|
||||
<Col>
|
||||
<MarketSelector
|
||||
markets={deprecatedMarkets}
|
||||
placeholder={'Select deprecated market'}
|
||||
/>
|
||||
</Col>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Row>
|
||||
{getComponent()}
|
||||
</Wrapper>
|
||||
|
@ -116,6 +120,51 @@ export default function TradePage() {
|
|||
);
|
||||
}
|
||||
|
||||
function MarketSelector({ markets, placeholder }) {
|
||||
const { market, setMarketAddress } = useMarket();
|
||||
return (
|
||||
<Select
|
||||
size={'large'}
|
||||
bordered={true}
|
||||
onSelect={setMarketAddress}
|
||||
value={markets
|
||||
.find(
|
||||
(proposedMarket) =>
|
||||
market?.address && proposedMarket.address.equals(market.address),
|
||||
)
|
||||
?.address?.toBase58()}
|
||||
listHeight={400}
|
||||
placeholder={placeholder}
|
||||
>
|
||||
{markets.map(({ address, name, deprecated }, i) => (
|
||||
<Option
|
||||
value={address.toBase58()}
|
||||
key={address}
|
||||
style={{
|
||||
padding: '10px 0',
|
||||
textAlign: 'center',
|
||||
backgroundColor: i % 2 === 0 ? 'rgb(39, 44, 61)' : null,
|
||||
}}
|
||||
>
|
||||
{name} {deprecated ? ' (Deprecated)' : null}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
|
||||
const RenderDeprecatedMarket = () => {
|
||||
return (
|
||||
<>
|
||||
<Row>
|
||||
<Col flex="auto">
|
||||
<DeprecatedMarketInstructions />
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const RenderNormal = ({ onChangeOrderRef, onPrice, onSize }) => {
|
||||
return (
|
||||
<Row
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
TokenInstructions,
|
||||
MARKETS,
|
||||
TOKEN_MINTS,
|
||||
OpenOrders,
|
||||
} from '@project-serum/serum';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
|
@ -16,8 +17,15 @@ import tuple from 'immutable-tuple';
|
|||
import { notify } from './notifications';
|
||||
import { BN } from 'bn.js';
|
||||
|
||||
// Used in debugging, should be false in production
|
||||
const _IGNORE_DEPRECATED = false;
|
||||
|
||||
const USE_MARKETS = _IGNORE_DEPRECATED
|
||||
? MARKETS.map((m) => ({ ...m, deprecated: false }))
|
||||
: MARKETS;
|
||||
|
||||
export function useMarketsList() {
|
||||
return MARKETS;
|
||||
return USE_MARKETS.filter(({ deprecated }) => !deprecated);
|
||||
}
|
||||
|
||||
export function useAllMarkets() {
|
||||
|
@ -28,7 +36,7 @@ export function useAllMarkets() {
|
|||
const getAllMarkets = async () => {
|
||||
const markets = [];
|
||||
let marketInfo;
|
||||
for (marketInfo of MARKETS) {
|
||||
for (marketInfo of USE_MARKETS) {
|
||||
try {
|
||||
const market = await Market.load(
|
||||
connection,
|
||||
|
@ -54,6 +62,73 @@ export function useAllMarkets() {
|
|||
return markets;
|
||||
}
|
||||
|
||||
export function useUnmigratedDeprecatedMarketsList() {
|
||||
const connection = useConnection();
|
||||
const { wallet } = useWallet();
|
||||
|
||||
async function getUnmigratedDeprecatedMarkets() {
|
||||
if (!wallet || !connection || !wallet.publicKey) {
|
||||
return [];
|
||||
}
|
||||
let marketAddresses = [];
|
||||
const deprecatedProgramIds = Array.from(
|
||||
new Set(
|
||||
USE_MARKETS.filter(({ deprecated }) => deprecated).map(
|
||||
({ programId }) => programId,
|
||||
),
|
||||
),
|
||||
);
|
||||
let programId;
|
||||
for (programId of deprecatedProgramIds) {
|
||||
try {
|
||||
const openOrdersAccounts = await OpenOrders.findForOwner(
|
||||
connection,
|
||||
wallet.publicKey,
|
||||
programId,
|
||||
);
|
||||
marketAddresses = marketAddresses.concat(
|
||||
Array.from(
|
||||
new Set(
|
||||
openOrdersAccounts
|
||||
.filter(
|
||||
(openOrders) =>
|
||||
openOrders.baseTokenTotal.toNumber() ||
|
||||
openOrders.quoteTokenTotal.toNumber(),
|
||||
)
|
||||
.map((openOrders) => openOrders.market),
|
||||
),
|
||||
).filter((address) =>
|
||||
USE_MARKETS.some(
|
||||
(market) => market.deprecated && market.address.equals(address),
|
||||
),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(
|
||||
'Error loading deprecated markets',
|
||||
programId?.toBase58(),
|
||||
e.message,
|
||||
);
|
||||
}
|
||||
}
|
||||
return USE_MARKETS.filter((market) =>
|
||||
marketAddresses.some((address) => address.equals(market.address)),
|
||||
);
|
||||
}
|
||||
|
||||
const [markets] = useAsyncData(
|
||||
getUnmigratedDeprecatedMarkets,
|
||||
tuple(
|
||||
'useUnmigratedDeprecatedMarketsList',
|
||||
connection,
|
||||
wallet?.publicKey?.toBase58(),
|
||||
),
|
||||
{ refreshInterval: _SLOW_REFRESH_INTERVAL },
|
||||
);
|
||||
|
||||
return markets;
|
||||
}
|
||||
|
||||
const MarketContext = React.createContext(null);
|
||||
|
||||
// For things that don't really change
|
||||
|
@ -62,16 +137,30 @@ const _SLOW_REFRESH_INTERVAL = 5 * 1000;
|
|||
// For things that change frequently
|
||||
const _FAST_REFRESH_INTERVAL = 1000;
|
||||
|
||||
export const DEFAULT_MARKET = USE_MARKETS.find(
|
||||
({ name }) => name === 'SRM/USDT',
|
||||
);
|
||||
|
||||
export function MarketProvider({ children }) {
|
||||
const [marketAddress, setMarketAddress] = useLocalStorageState(
|
||||
'marketAddress',
|
||||
MARKETS.find(({ name }) => name === 'SRM/USDT')?.address?.toBase58(),
|
||||
DEFAULT_MARKET.address.toBase58(),
|
||||
);
|
||||
|
||||
const connection = useConnection();
|
||||
const marketInfo = MARKETS.find((market) =>
|
||||
const marketInfo = USE_MARKETS.find((market) =>
|
||||
market.address.equals(new PublicKey(marketAddress)),
|
||||
);
|
||||
|
||||
// Replace existing market with a non-deprecated one on first load
|
||||
useEffect(() => {
|
||||
if (marketInfo && marketInfo.deprecated) {
|
||||
console.log('Switching markets from deprecated', marketInfo);
|
||||
setMarketAddress(DEFAULT_MARKET.address.toBase58());
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const [market, setMarket] = useState();
|
||||
useEffect(() => {
|
||||
setMarket(null);
|
||||
|
|
|
@ -1467,10 +1467,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
|
||||
integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
|
||||
|
||||
"@project-serum/serum@^0.12.0":
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.12.0.tgz#27110e4c28a4cdb44a8539f442bbf9c926f27dbe"
|
||||
integrity sha512-QaHZsdhmxfVXMJpCApbFD3gg80sknr/t5LXhmCxdZePf21LLtGl2wug4DYgSMOV73JULiEzU6FpH9qSRAIYtiA==
|
||||
"@project-serum/serum@^0.12.2":
|
||||
version "0.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.12.2.tgz#d3f5e69ad2d62229c91b344583e6841cf1fa34c5"
|
||||
integrity sha512-KZ2Jvh38ys0i0PWcfkbxoJC7AOq2wSrzmYx2vZo8RQB9E2n+a4nhffJ/0uMnANB9T46zCOJ6eeXQHS8id5BThA==
|
||||
dependencies:
|
||||
"@solana/web3.js" "^0.71.10"
|
||||
bn.js "^5.1.2"
|
||||
|
|
Loading…
Reference in New Issue