Add page for listing new markets
This commit is contained in:
parent
ce57e3c1e1
commit
37067f2f6c
|
@ -1,10 +1,10 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Modal, Input, Row, Col, Typography } from 'antd';
|
import { Col, Input, Modal, Row, Typography } from 'antd';
|
||||||
import { notify } from '../utils/notifications';
|
import { notify } from '../utils/notifications';
|
||||||
import { isValidPublicKey } from '../utils/utils';
|
import { isValidPublicKey } from '../utils/utils';
|
||||||
import { PublicKey } from '@solana/web3.js';
|
import { PublicKey } from '@solana/web3.js';
|
||||||
import { Market, MARKETS, TOKEN_MINTS } from '@project-serum/serum';
|
import { Market, MARKETS, TOKEN_MINTS } from '@project-serum/serum';
|
||||||
import { useConnection } from '../utils/connection';
|
import { useAccountInfo, useConnection } from '../utils/connection';
|
||||||
import { LoadingOutlined } from '@ant-design/icons';
|
import { LoadingOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
@ -15,23 +15,27 @@ export default function CustomMarketDialog({
|
||||||
onClose,
|
onClose,
|
||||||
}) {
|
}) {
|
||||||
const connection = useConnection();
|
const connection = useConnection();
|
||||||
const [marketId, setMarketId] = useState(null);
|
|
||||||
const [programId, setProgramId] = useState(
|
|
||||||
MARKETS.find(({ deprecated }) => !deprecated)?.programId?.toBase58(),
|
|
||||||
);
|
|
||||||
|
|
||||||
const [marketLabel, setMarketLabel] = useState(null);
|
const [marketId, setMarketId] = useState('');
|
||||||
const [baseLabel, setBaseLabel] = useState(null);
|
|
||||||
const [quoteLabel, setQuoteLabel] = useState(null);
|
const [marketLabel, setMarketLabel] = useState('');
|
||||||
|
const [baseLabel, setBaseLabel] = useState('');
|
||||||
|
const [quoteLabel, setQuoteLabel] = useState('');
|
||||||
|
|
||||||
const [market, setMarket] = useState(null);
|
const [market, setMarket] = useState(null);
|
||||||
const [loadingMarket, setLoadingMarket] = useState(false);
|
const [loadingMarket, setLoadingMarket] = useState(false);
|
||||||
|
|
||||||
const wellFormedMarketId = isValidPublicKey(marketId);
|
const wellFormedMarketId = isValidPublicKey(marketId);
|
||||||
const wellFormedProgramId = isValidPublicKey(programId);
|
|
||||||
|
const [marketAccountInfo] = useAccountInfo(
|
||||||
|
wellFormedMarketId ? new PublicKey(marketId) : null,
|
||||||
|
);
|
||||||
|
const programId = marketAccountInfo
|
||||||
|
? marketAccountInfo.owner.toBase58()
|
||||||
|
: MARKETS.find(({ deprecated }) => !deprecated).programId.toBase58();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!wellFormedMarketId || !wellFormedProgramId) {
|
if (!wellFormedMarketId || !programId) {
|
||||||
resetLabels();
|
resetLabels();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -79,12 +83,12 @@ export default function CustomMarketDialog({
|
||||||
const canSubmit =
|
const canSubmit =
|
||||||
!loadingMarket &&
|
!loadingMarket &&
|
||||||
!!market &&
|
!!market &&
|
||||||
|
market.publicKey.toBase58() === marketId &&
|
||||||
marketId &&
|
marketId &&
|
||||||
programId &&
|
programId &&
|
||||||
marketLabel &&
|
marketLabel &&
|
||||||
(knownBaseCurrency || baseLabel) &&
|
(knownBaseCurrency || baseLabel) &&
|
||||||
(knownQuoteCurrency || quoteLabel) &&
|
(knownQuoteCurrency || quoteLabel) &&
|
||||||
wellFormedProgramId &&
|
|
||||||
wellFormedMarketId;
|
wellFormedMarketId;
|
||||||
|
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
|
@ -115,7 +119,6 @@ export default function CustomMarketDialog({
|
||||||
resetLabels();
|
resetLabels();
|
||||||
setMarket(null);
|
setMarket(null);
|
||||||
setMarketId(null);
|
setMarketId(null);
|
||||||
setProgramId(null);
|
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -129,13 +132,11 @@ export default function CustomMarketDialog({
|
||||||
okButtonProps={{ disabled: !canSubmit }}
|
okButtonProps={{ disabled: !canSubmit }}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
{wellFormedMarketId && wellFormedProgramId ? (
|
{wellFormedMarketId ? (
|
||||||
<>
|
<>
|
||||||
{!market && (
|
{!market && !loadingMarket && (
|
||||||
<Row style={{ marginBottom: 8 }}>
|
<Row style={{ marginBottom: 8 }}>
|
||||||
<Text type="danger">
|
<Text type="danger">Not a valid market</Text>
|
||||||
Not a valid market and program ID combination
|
|
||||||
</Text>
|
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
{market && knownMarket && (
|
{market && knownMarket && (
|
||||||
|
@ -161,11 +162,6 @@ export default function CustomMarketDialog({
|
||||||
<Text type="danger">Invalid market ID</Text>
|
<Text type="danger">Invalid market ID</Text>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
{marketId && !wellFormedProgramId && (
|
|
||||||
<Row style={{ marginBottom: 8 }}>
|
|
||||||
<Text type="danger">Invalid program ID</Text>
|
|
||||||
</Row>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Row style={{ marginBottom: 8 }}>
|
<Row style={{ marginBottom: 8 }}>
|
||||||
|
@ -179,20 +175,6 @@ export default function CustomMarketDialog({
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
{programId && !isValidPublicKey(programId) && (
|
|
||||||
<Row style={{ marginBottom: 8 }}>
|
|
||||||
<Text type="danger">Invalid program ID</Text>
|
|
||||||
</Row>
|
|
||||||
)}
|
|
||||||
<Row style={{ marginBottom: 8 }}>
|
|
||||||
<Col span={24}>
|
|
||||||
<Input
|
|
||||||
placeholder="Program Id"
|
|
||||||
value={programId}
|
|
||||||
onChange={(e) => setProgramId(e.target.value)}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row style={{ marginBottom: 8, marginTop: 8 }}>
|
<Row style={{ marginBottom: 8, marginTop: 8 }}>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Input
|
<Input
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { InfoCircleOutlined, UserOutlined } from '@ant-design/icons';
|
import { InfoCircleOutlined, UserOutlined } from '@ant-design/icons';
|
||||||
import { Button, Menu, Popover, Select } from 'antd';
|
import { Button, Menu, Popover, Select } from 'antd';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useHistory, useLocation } from 'react-router-dom';
|
import { useHistory, useLocation } from 'react-router-dom';
|
||||||
import logo from '../assets/logo.svg';
|
import logo from '../assets/logo.svg';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
@ -29,7 +29,6 @@ const LogoWrapper = styled.div`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default function TopBar() {
|
export default function TopBar() {
|
||||||
const [current, setCurrent] = useState('/');
|
|
||||||
const { connected, wallet, providerUrl, setProvider } = useWallet();
|
const { connected, wallet, providerUrl, setProvider } = useWallet();
|
||||||
const { endpoint, setEndpoint } = useConnectionConfig();
|
const { endpoint, setEndpoint } = useConnectionConfig();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
@ -44,24 +43,16 @@ export default function TopBar() {
|
||||||
[history],
|
[history],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (location.pathname.includes('/orders')) {
|
|
||||||
setCurrent('/orders');
|
|
||||||
} else if (location.pathname.includes('/balances')) {
|
|
||||||
setCurrent('/balances');
|
|
||||||
}
|
|
||||||
}, [location]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<LogoWrapper>
|
<LogoWrapper>
|
||||||
<img src={logo} alt="" />
|
<img src={logo} alt="" onClick={() => history.push('/')} />
|
||||||
{'SERUM'}
|
{'SERUM'}
|
||||||
</LogoWrapper>
|
</LogoWrapper>
|
||||||
<Menu
|
<Menu
|
||||||
mode="horizontal"
|
mode="horizontal"
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
selectedKeys={[current]}
|
selectedKeys={[location.pathname]}
|
||||||
style={{
|
style={{
|
||||||
borderBottom: 'none',
|
borderBottom: 'none',
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Button, Form, Input, Typography } from 'antd';
|
||||||
|
import { notify } from '../utils/notifications';
|
||||||
|
import { isValidPublicKey } from '../utils/utils';
|
||||||
|
import { PublicKey } from '@solana/web3.js';
|
||||||
|
import { MARKETS, TokenInstructions } from '@project-serum/serum';
|
||||||
|
import { useAccountInfo, useConnection } from '../utils/connection';
|
||||||
|
import FloatingElement from '../components/layout/FloatingElement';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { parseTokenMintData } from '../utils/tokens';
|
||||||
|
import { useWallet } from '../utils/wallet';
|
||||||
|
import { listMarket } from '../utils/send';
|
||||||
|
|
||||||
|
const { Text, Title } = Typography;
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
max-width: 800px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-top: 24px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function ListNewMarketPage() {
|
||||||
|
const connection = useConnection();
|
||||||
|
const { wallet, connected } = useWallet();
|
||||||
|
const [baseMintInput, baseMintInfo] = useMintInput(
|
||||||
|
'baseMint',
|
||||||
|
'Base Mint Address',
|
||||||
|
);
|
||||||
|
const [quoteMintInput, quoteMintInfo] = useMintInput(
|
||||||
|
'quoteMint',
|
||||||
|
'Quote Mint Address',
|
||||||
|
);
|
||||||
|
const [lotSize, setLotSize] = useState('1');
|
||||||
|
const [tickSize, setTickSize] = useState('0.01');
|
||||||
|
const dexProgramId = MARKETS.find(({ deprecated }) => !deprecated).programId;
|
||||||
|
const [submitting, setSubmitting] = useState(false);
|
||||||
|
|
||||||
|
const [listedMarket, setListedMarket] = useState(null);
|
||||||
|
|
||||||
|
let baseLotSize;
|
||||||
|
let quoteLotSize;
|
||||||
|
if (baseMintInfo && parseFloat(lotSize) > 0) {
|
||||||
|
baseLotSize = Math.round(10 ** baseMintInfo.decimals * parseFloat(lotSize));
|
||||||
|
if (quoteMintInfo && parseFloat(tickSize) > 0) {
|
||||||
|
quoteLotSize = Math.round(
|
||||||
|
parseFloat(lotSize) *
|
||||||
|
10 ** quoteMintInfo.decimals *
|
||||||
|
parseFloat(tickSize),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const canSubmit =
|
||||||
|
connected &&
|
||||||
|
!!baseMintInfo &&
|
||||||
|
!!quoteMintInfo &&
|
||||||
|
!!baseLotSize &&
|
||||||
|
!!quoteLotSize;
|
||||||
|
|
||||||
|
async function onSubmit() {
|
||||||
|
if (!canSubmit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSubmitting(true);
|
||||||
|
try {
|
||||||
|
const marketAddress = await listMarket({
|
||||||
|
connection,
|
||||||
|
wallet,
|
||||||
|
baseMint: baseMintInfo.address,
|
||||||
|
quoteMint: quoteMintInfo.address,
|
||||||
|
baseLotSize,
|
||||||
|
quoteLotSize,
|
||||||
|
dexProgramId,
|
||||||
|
});
|
||||||
|
setListedMarket(marketAddress);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
notify({
|
||||||
|
message: 'Error listing new market: ' + e.message,
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setSubmitting(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<FloatingElement>
|
||||||
|
<Title level={4}>List New Market</Title>
|
||||||
|
<Form
|
||||||
|
labelCol={{ span: 8 }}
|
||||||
|
wrapperCol={{ span: 16 }}
|
||||||
|
onFinish={onSubmit}
|
||||||
|
>
|
||||||
|
{baseMintInput}
|
||||||
|
{quoteMintInput}
|
||||||
|
<Form.Item
|
||||||
|
label="Min Order Size (Lot Size)"
|
||||||
|
name="lotSize"
|
||||||
|
initialValue="1"
|
||||||
|
validateStatus={
|
||||||
|
baseMintInfo && quoteMintInfo
|
||||||
|
? baseLotSize
|
||||||
|
? 'success'
|
||||||
|
: 'error'
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
hasFeedback={baseMintInfo && quoteMintInfo}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={lotSize}
|
||||||
|
onChange={(e) => setLotSize(e.target.value.trim())}
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="any"
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="Tick Size (Price Increment)"
|
||||||
|
name="tickSize"
|
||||||
|
initialValue="0.01"
|
||||||
|
validateStatus={
|
||||||
|
baseMintInfo && quoteMintInfo
|
||||||
|
? quoteLotSize
|
||||||
|
? 'success'
|
||||||
|
: 'error'
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
hasFeedback={baseMintInfo && quoteMintInfo}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={tickSize}
|
||||||
|
onChange={(e) => setTickSize(e.target.value.trim())}
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="any"
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item wrapperCol={{ offset: 8, span: 16 }}>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
htmlType="submit"
|
||||||
|
disabled={!canSubmit}
|
||||||
|
loading={submitting}
|
||||||
|
>
|
||||||
|
{connected ? 'Submit' : 'Not connected to wallet'}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</FloatingElement>
|
||||||
|
{listedMarket ? (
|
||||||
|
<FloatingElement>
|
||||||
|
<Text>New market address: {listedMarket.toBase58()}</Text>
|
||||||
|
</FloatingElement>
|
||||||
|
) : null}
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function useMintInput(name, label) {
|
||||||
|
const [address, setAddress] = useState('');
|
||||||
|
const [accountInfo, loaded] = useAccountInfo(
|
||||||
|
isValidPublicKey(address) ? new PublicKey(address) : null,
|
||||||
|
);
|
||||||
|
|
||||||
|
let validateStatus = null;
|
||||||
|
let hasFeedback = false;
|
||||||
|
let help = null;
|
||||||
|
let mintInfo = null;
|
||||||
|
if (address) {
|
||||||
|
hasFeedback = true;
|
||||||
|
if (accountInfo) {
|
||||||
|
if (
|
||||||
|
accountInfo.owner.equals(TokenInstructions.TOKEN_PROGRAM_ID) &&
|
||||||
|
accountInfo.data.length === 82
|
||||||
|
) {
|
||||||
|
let parsed = parseTokenMintData(accountInfo.data);
|
||||||
|
if (parsed.initialized) {
|
||||||
|
validateStatus = 'success';
|
||||||
|
mintInfo = {
|
||||||
|
address: new PublicKey(address),
|
||||||
|
decimals: parsed.decimals,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
validateStatus = 'error';
|
||||||
|
help = 'Invalid SPL mint';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
validateStatus = 'error';
|
||||||
|
help = 'Invalid SPL mint address';
|
||||||
|
}
|
||||||
|
} else if (isValidPublicKey(address) && !loaded) {
|
||||||
|
validateStatus = 'loading';
|
||||||
|
} else {
|
||||||
|
validateStatus = 'error';
|
||||||
|
help = 'Invalid Solana address';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const input = (
|
||||||
|
<Form.Item
|
||||||
|
label={label}
|
||||||
|
name={name}
|
||||||
|
validateStatus={validateStatus}
|
||||||
|
hasFeedback={hasFeedback}
|
||||||
|
help={help}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={address}
|
||||||
|
onChange={(e) => setAddress(e.target.value.trim())}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
|
||||||
|
return [input, mintInfo];
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import OpenOrdersPage from './pages/OpenOrdersPage';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import BalancesPage from './pages/BalancesPage';
|
import BalancesPage from './pages/BalancesPage';
|
||||||
import BasicLayout from './components/BasicLayout';
|
import BasicLayout from './components/BasicLayout';
|
||||||
|
import ListNewMarketPage from './pages/ListNewMarketPage';
|
||||||
|
|
||||||
export function Routes() {
|
export function Routes() {
|
||||||
return (
|
return (
|
||||||
|
@ -11,6 +12,11 @@ export function Routes() {
|
||||||
<Route exact path="/" component={TradePageContents} />
|
<Route exact path="/" component={TradePageContents} />
|
||||||
<Route exact path="/orders" component={OpenOrdersPageContents} />
|
<Route exact path="/orders" component={OpenOrdersPageContents} />
|
||||||
<Route exact path="/balances" component={BalancesPageContents} />
|
<Route exact path="/balances" component={BalancesPageContents} />
|
||||||
|
<Route exact path="/list-new-market">
|
||||||
|
<BasicLayout>
|
||||||
|
<ListNewMarketPage />
|
||||||
|
</BasicLayout>
|
||||||
|
</Route>
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,17 @@ import { notify } from './notifications';
|
||||||
import { getDecimalCount, sleep } from './utils';
|
import { getDecimalCount, sleep } from './utils';
|
||||||
import {
|
import {
|
||||||
Account,
|
Account,
|
||||||
|
PublicKey,
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
Transaction,
|
Transaction,
|
||||||
PublicKey,
|
|
||||||
} from '@solana/web3.js';
|
} from '@solana/web3.js';
|
||||||
import { TOKEN_MINTS, TokenInstructions } from '@project-serum/serum';
|
import { BN } from 'bn.js';
|
||||||
|
import {
|
||||||
|
DexInstructions,
|
||||||
|
Market,
|
||||||
|
TOKEN_MINTS,
|
||||||
|
TokenInstructions,
|
||||||
|
} from '@project-serum/serum';
|
||||||
|
|
||||||
export async function createTokenAccountTransaction({
|
export async function createTokenAccountTransaction({
|
||||||
connection,
|
connection,
|
||||||
|
@ -249,6 +255,145 @@ export async function placeOrder({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function listMarket({
|
||||||
|
connection,
|
||||||
|
wallet,
|
||||||
|
baseMint,
|
||||||
|
quoteMint,
|
||||||
|
baseLotSize,
|
||||||
|
quoteLotSize,
|
||||||
|
dexProgramId,
|
||||||
|
}) {
|
||||||
|
const market = new Account();
|
||||||
|
const requestQueue = new Account();
|
||||||
|
const eventQueue = new Account();
|
||||||
|
const bids = new Account();
|
||||||
|
const asks = new Account();
|
||||||
|
const baseVault = new Account();
|
||||||
|
const quoteVault = new Account();
|
||||||
|
const feeRateBps = 0;
|
||||||
|
const quoteDustThreshold = new BN(100);
|
||||||
|
|
||||||
|
async function getVaultOwnerAndNonce() {
|
||||||
|
const nonce = new BN(0);
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const vaultOwner = await PublicKey.createProgramAddress(
|
||||||
|
[market.publicKey.toBuffer(), nonce.toArrayLike(Buffer, 'le', 8)],
|
||||||
|
dexProgramId,
|
||||||
|
);
|
||||||
|
return [vaultOwner, nonce];
|
||||||
|
} catch (e) {
|
||||||
|
nonce.iaddn(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const [vaultOwner, vaultSignerNonce] = await getVaultOwnerAndNonce();
|
||||||
|
|
||||||
|
const tx1 = new Transaction();
|
||||||
|
tx1.add(
|
||||||
|
SystemProgram.createAccount({
|
||||||
|
fromPubkey: wallet.publicKey,
|
||||||
|
newAccountPubkey: baseVault.publicKey,
|
||||||
|
lamports: await connection.getMinimumBalanceForRentExemption(165),
|
||||||
|
space: 165,
|
||||||
|
programId: TokenInstructions.TOKEN_PROGRAM_ID,
|
||||||
|
}),
|
||||||
|
SystemProgram.createAccount({
|
||||||
|
fromPubkey: wallet.publicKey,
|
||||||
|
newAccountPubkey: quoteVault.publicKey,
|
||||||
|
lamports: await connection.getMinimumBalanceForRentExemption(165),
|
||||||
|
space: 165,
|
||||||
|
programId: TokenInstructions.TOKEN_PROGRAM_ID,
|
||||||
|
}),
|
||||||
|
TokenInstructions.initializeAccount({
|
||||||
|
account: baseVault.publicKey,
|
||||||
|
mint: baseMint,
|
||||||
|
owner: vaultOwner,
|
||||||
|
}),
|
||||||
|
TokenInstructions.initializeAccount({
|
||||||
|
account: quoteVault.publicKey,
|
||||||
|
mint: quoteMint,
|
||||||
|
owner: vaultOwner,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const tx2 = new Transaction();
|
||||||
|
tx2.add(
|
||||||
|
SystemProgram.createAccount({
|
||||||
|
fromPubkey: wallet.publicKey,
|
||||||
|
newAccountPubkey: market.publicKey,
|
||||||
|
lamports: await connection.getMinimumBalanceForRentExemption(
|
||||||
|
Market.getLayout(dexProgramId).span,
|
||||||
|
),
|
||||||
|
space: Market.getLayout(dexProgramId).span,
|
||||||
|
programId: dexProgramId,
|
||||||
|
}),
|
||||||
|
SystemProgram.createAccount({
|
||||||
|
fromPubkey: wallet.publicKey,
|
||||||
|
newAccountPubkey: requestQueue.publicKey,
|
||||||
|
lamports: await connection.getMinimumBalanceForRentExemption(5120 + 12),
|
||||||
|
space: 5120 + 12,
|
||||||
|
programId: dexProgramId,
|
||||||
|
}),
|
||||||
|
SystemProgram.createAccount({
|
||||||
|
fromPubkey: wallet.publicKey,
|
||||||
|
newAccountPubkey: eventQueue.publicKey,
|
||||||
|
lamports: await connection.getMinimumBalanceForRentExemption(262144 + 12),
|
||||||
|
space: 262144 + 12,
|
||||||
|
programId: dexProgramId,
|
||||||
|
}),
|
||||||
|
SystemProgram.createAccount({
|
||||||
|
fromPubkey: wallet.publicKey,
|
||||||
|
newAccountPubkey: bids.publicKey,
|
||||||
|
lamports: await connection.getMinimumBalanceForRentExemption(65536 + 12),
|
||||||
|
space: 65536 + 12,
|
||||||
|
programId: dexProgramId,
|
||||||
|
}),
|
||||||
|
SystemProgram.createAccount({
|
||||||
|
fromPubkey: wallet.publicKey,
|
||||||
|
newAccountPubkey: asks.publicKey,
|
||||||
|
lamports: await connection.getMinimumBalanceForRentExemption(65536 + 12),
|
||||||
|
space: 65536 + 12,
|
||||||
|
programId: dexProgramId,
|
||||||
|
}),
|
||||||
|
DexInstructions.initializeMarket({
|
||||||
|
market: market.publicKey,
|
||||||
|
requestQueue: requestQueue.publicKey,
|
||||||
|
eventQueue: eventQueue.publicKey,
|
||||||
|
bids: bids.publicKey,
|
||||||
|
asks: asks.publicKey,
|
||||||
|
baseVault: baseVault.publicKey,
|
||||||
|
quoteVault: quoteVault.publicKey,
|
||||||
|
baseMint,
|
||||||
|
quoteMint,
|
||||||
|
baseLotSize: new BN(baseLotSize),
|
||||||
|
quoteLotSize: new BN(quoteLotSize),
|
||||||
|
feeRateBps,
|
||||||
|
vaultSignerNonce,
|
||||||
|
quoteDustThreshold,
|
||||||
|
programId: dexProgramId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
sendTransaction({
|
||||||
|
transaction: tx1,
|
||||||
|
wallet,
|
||||||
|
connection,
|
||||||
|
signers: [wallet.publicKey, baseVault, quoteVault],
|
||||||
|
}),
|
||||||
|
sendTransaction({
|
||||||
|
transaction: tx2,
|
||||||
|
wallet,
|
||||||
|
connection,
|
||||||
|
signers: [wallet.publicKey, market, requestQueue, eventQueue, bids, asks],
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return market.publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
const getUnixTs = () => {
|
const getUnixTs = () => {
|
||||||
return new Date().getTime() / 1000;
|
return new Date().getTime() / 1000;
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,6 +10,13 @@ export const ACCOUNT_LAYOUT = BufferLayout.struct([
|
||||||
BufferLayout.blob(93),
|
BufferLayout.blob(93),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
export const MINT_LAYOUT = BufferLayout.struct([
|
||||||
|
BufferLayout.blob(44),
|
||||||
|
BufferLayout.u8('decimals'),
|
||||||
|
BufferLayout.u8('initialized'),
|
||||||
|
BufferLayout.blob(36),
|
||||||
|
]);
|
||||||
|
|
||||||
export function parseTokenAccountData(data) {
|
export function parseTokenAccountData(data) {
|
||||||
let { mint, owner, amount } = ACCOUNT_LAYOUT.decode(data);
|
let { mint, owner, amount } = ACCOUNT_LAYOUT.decode(data);
|
||||||
return {
|
return {
|
||||||
|
@ -19,6 +26,11 @@ export function parseTokenAccountData(data) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function parseTokenMintData(data) {
|
||||||
|
let { decimals, initialized } = MINT_LAYOUT.decode(data);
|
||||||
|
return { decimals, initialized };
|
||||||
|
}
|
||||||
|
|
||||||
export function getOwnedAccountsFilters(publicKey) {
|
export function getOwnedAccountsFilters(publicKey) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue