Skip to content

Commit

Permalink
Conserve calls to getAccountInfo (#74)
Browse files Browse the repository at this point in the history
* Conserve get markets request

* Add mints to market infos

* A few changes

* Also autosettle active market page

* Fixes
  • Loading branch information
nathanielparke committed Apr 30, 2021
1 parent 1d37d4c commit 0f5f04f
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 166 deletions.
64 changes: 46 additions & 18 deletions src/components/TradeForm.tsx
@@ -1,30 +1,27 @@
import { Button, Input, Radio, Switch, Slider } from 'antd';
import React, { useState, useEffect } from 'react';
import {Button, Input, Radio, Slider, Switch} from 'antd';
import React, {useEffect, useState} from 'react';
import styled from 'styled-components';
import {
useSelectedBaseCurrencyBalances,
useSelectedQuoteCurrencyBalances,
useFeeDiscountKeys,
useLocallyStoredFeeDiscountKey,
useMarket,
useMarkPrice,
useSelectedOpenOrdersAccount,
useSelectedBaseCurrencyAccount,
useSelectedBaseCurrencyBalances,
useSelectedOpenOrdersAccount,
useSelectedQuoteCurrencyAccount,
useFeeDiscountKeys,
useLocallyStoredFeeDiscountKey,
useSelectedQuoteCurrencyBalances,
} from '../utils/markets';
import { useWallet } from '../utils/wallet';
import { notify } from '../utils/notifications';
import {
getDecimalCount,
roundToDecimal,
floorToDecimal,
} from '../utils/utils';
import { useSendConnection } from '../utils/connection';
import {useWallet} from '../utils/wallet';
import {notify} from '../utils/notifications';
import {floorToDecimal, getDecimalCount, roundToDecimal, useLocalStorageState,} from '../utils/utils';
import {useSendConnection} from '../utils/connection';
import FloatingElement from './layout/FloatingElement';
import { getUnixTs, placeOrder } from '../utils/send';
import { SwitchChangeEventHandler } from 'antd/es/switch';
import { refreshCache } from '../utils/fetch-loop';
import {getUnixTs, placeOrder, settleFunds} from '../utils/send';
import {SwitchChangeEventHandler} from 'antd/es/switch';
import {refreshCache} from '../utils/fetch-loop';
import tuple from 'immutable-tuple';
import {useInterval} from "../utils/useInterval";

const SellButton = styled(Button)`
margin: 20px 0px 0px 0px;
Expand Down Expand Up @@ -77,6 +74,10 @@ export default function TradeForm({
const [price, setPrice] = useState<number | undefined>(undefined);
const [submitting, setSubmitting] = useState(false);
const [sizeFraction, setSizeFraction] = useState(0);
const [autoSettleEnabled] = useLocalStorageState(
'autoSettleEnabled',
true,
);

const availableQuote =
openOrdersAccount && market
Expand Down Expand Up @@ -132,6 +133,33 @@ export default function TradeForm({
return () => clearInterval(id);
}, [market, sendConnection, wallet, publicKey]);

useInterval(() => {
const autoSettle = async () => {
if (!wallet || !market || !openOrdersAccount || !baseCurrencyAccount || !quoteCurrencyAccount) {
return;
}
try {
// settle funds into selected token wallets
await settleFunds({
market,
openOrders: openOrdersAccount,
connection: sendConnection,
wallet,
baseCurrencyAccount,
quoteCurrencyAccount
});
} catch (e) {
console.log('Error auto settling funds: ' + e.message);
}
};
(
connected &&
wallet?.autoApprove &&
autoSettleEnabled &&
autoSettle()
);
}, 10000);

const onSetBaseSize = (baseSize: number | undefined) => {
setBaseSize(baseSize);
if (!baseSize) {
Expand Down
6 changes: 6 additions & 0 deletions src/utils/fetch-loop.tsx
Expand Up @@ -261,3 +261,9 @@ export function setCache(
loop.notifyListeners();
}
}

export function getCache(
cacheKey: any
) {
return globalCache.get(cacheKey);
}
133 changes: 45 additions & 88 deletions src/utils/markets.tsx
@@ -1,31 +1,14 @@
import {
decodeEventQueue,
Market,
MARKETS,
OpenOrders,
Orderbook,
TOKEN_MINTS,
TokenInstructions,
} from '@project-serum/serum';
import { PublicKey } from '@solana/web3.js';
import React, { useContext, useEffect, useState } from 'react';
import {
divideBnToNumber,
floorToDecimal,
getTokenMultiplierFromDecimals,
useLocalStorageState,
} from './utils';
import { refreshCache, useAsyncData } from './fetch-loop';
import { useAccountData, useAccountInfo, useConnection } from './connection';
import { useWallet } from './wallet';
import {Market, MARKETS, OpenOrders, Orderbook, TOKEN_MINTS, TokenInstructions,} from '@project-serum/serum';
import {Connection, PublicKey} from '@solana/web3.js';
import React, {useContext, useEffect, useState} from 'react';
import {divideBnToNumber, floorToDecimal, getTokenMultiplierFromDecimals, sleep, useLocalStorageState,} from './utils';
import {getCache, refreshCache, setCache, useAsyncData} from './fetch-loop';
import {useAccountData, useAccountInfo, useConnection} from './connection';
import {useWallet} from './wallet';
import tuple from 'immutable-tuple';
import { notify } from './notifications';
import {notify} from './notifications';
import BN from 'bn.js';
import {
getTokenAccountInfo,
parseTokenAccountData,
useMintInfos,
} from './tokens';
import {getTokenAccountInfo, parseTokenAccountData, useMintInfos,} from './tokens';
import {
Balances,
CustomMarketInfo,
Expand All @@ -36,12 +19,10 @@ import {
OrderWithMarketAndMarketName,
SelectedTokenAccounts,
TokenAccount,
Trade,
} from './types';
import { WRAPPED_SOL_MINT } from '@project-serum/serum/lib/token-instructions';
import { Order } from '@project-serum/serum/lib/market';
import {WRAPPED_SOL_MINT} from '@project-serum/serum/lib/token-instructions';
import {Order} from '@project-serum/serum/lib/market';
import BonfidaApi from './bonfidaConnector';
import { sleep } from './utils';

// Used in debugging, should be false in production
const _IGNORE_DEPRECATED = false;
Expand Down Expand Up @@ -434,13 +415,41 @@ export function useOpenOrdersAccounts(fast = false) {
wallet.publicKey,
);
}
return useAsyncData(
return useAsyncData<OpenOrders[] | null>(
getOpenOrdersAccounts,
tuple('getOpenOrdersAccounts', wallet, market, connected),
{ refreshInterval: fast ? _FAST_REFRESH_INTERVAL : _SLOW_REFRESH_INTERVAL },
);
}

// todo: refresh cache after some time?
export async function getCachedMarket(connection: Connection, address: PublicKey, programId: PublicKey) {
let market;
const cacheKey = tuple('getCachedMarket', 'market', address.toString(), connection);
if (!getCache(cacheKey)) {
market = await Market.load(connection, address, {}, programId)
setCache(cacheKey, market)
} else {
market = getCache(cacheKey);
}
return market;
}

export async function getCachedOpenOrderAccounts(connection: Connection, market: Market, owner: PublicKey) {
let accounts;
const cacheKey = tuple('getCachedOpenOrderAccounts', market.address.toString(), owner.toString(), connection);
if (!getCache(cacheKey)) {
accounts = await market.findOpenOrdersAccountsForOwner(
connection,
owner,
);
setCache(cacheKey, accounts);
} else {
accounts = getCache(cacheKey);
}
return accounts;
}

export function useSelectedOpenOrdersAccount(fast = false) {
const [accounts] = useOpenOrdersAccounts(fast);
if (!accounts) {
Expand Down Expand Up @@ -644,63 +653,6 @@ export function useFills(limit = 100) {
.map((fill) => ({ ...fill, marketName }));
}

// TODO: Update to use websocket
export function useFillsForAllMarkets(limit = 100) {
const { connected, wallet } = useWallet();

const connection = useConnection();
const allMarkets = useAllMarkets();

async function getFillsForAllMarkets() {
let fills: Trade[] = [];
if (!connected) {
return fills;
}

let marketData;
for (marketData of allMarkets) {
const { market, marketName } = marketData;
if (!market || !wallet) {
return fills;
}
const openOrdersAccounts = await market.findOpenOrdersAccountsForOwner(
connection,
wallet.publicKey,
);
const openOrdersAccount = openOrdersAccounts && openOrdersAccounts[0];
if (!openOrdersAccount) {
return fills;
}
const eventQueueData = await connection.getAccountInfo(
market && market._decoded.eventQueue,
);
let data = eventQueueData?.data;
if (!data) {
return fills;
}
const events = decodeEventQueue(data, limit);
const fillsForMarket: Trade[] = events
.filter(
(event) => event.eventFlags.fill && event.nativeQuantityPaid.gtn(0),
)
.map(market.parseFillEvent.bind(market));
const ownFillsForMarket = fillsForMarket
.filter((fill) => fill.openOrders.equals(openOrdersAccount.publicKey))
.map((fill) => ({ ...fill, marketName }));
fills = fills.concat(ownFillsForMarket);
}

console.log(JSON.stringify(fills));
return fills;
}

return useAsyncData(
getFillsForAllMarkets,
tuple('getFillsForAllMarkets', connected, connection, allMarkets, wallet),
{ refreshInterval: _FAST_REFRESH_INTERVAL },
);
}

export function useAllOpenOrdersAccounts() {
const { wallet, connected } = useWallet();
const connection = useConnection();
Expand Down Expand Up @@ -1242,3 +1194,8 @@ export function getExpectedFillPrice(
}
return formattedPrice;
}

export function useCurrentlyAutoSettling(): [boolean, (currentlyAutoSettling: boolean) => void] {
const [currentlyAutoSettling, setCurrentlyAutosettling] = useState<boolean>(false);
return [currentlyAutoSettling, setCurrentlyAutosettling];
}
80 changes: 51 additions & 29 deletions src/utils/preferences.tsx
@@ -1,15 +1,19 @@
import React, { useContext } from 'react';
import { useLocalStorageState } from './utils';
import { useInterval } from './useInterval';
import { useConnection } from './connection';
import { useWallet } from './wallet';
import React, {useContext} from 'react';
import {sleep, useLocalStorageState} from './utils';
import {useInterval} from './useInterval';
import {useConnection} from './connection';
import {useWallet} from './wallet';
import {
useAllMarkets,
useSelectedTokenAccounts,
getCachedMarket,
getCachedOpenOrderAccounts,
getSelectedTokenAccountForMint,
useCurrentlyAutoSettling,
useMarketInfos,
useTokenAccounts,
} from './markets';
import { settleAllFunds } from './send';
import { PreferencesContextValues } from './types';
import {settleFunds} from './send';
import {PreferencesContextValues} from './types';
import {getAssociatedTokenAddress} from "@project-serum/associated-token";

const PreferencesContext = React.createContext<PreferencesContextValues | null>(
null,
Expand All @@ -23,33 +27,51 @@ export function PreferencesProvider({ children }) {

const [tokenAccounts] = useTokenAccounts();
const { connected, wallet } = useWallet();
const [marketList] = useAllMarkets();
const marketInfoList = useMarketInfos();
const [currentlyAutoSettling, setCurrentlyAutoSettling] = useCurrentlyAutoSettling();
const connection = useConnection();
const [selectedTokenAccounts] = useSelectedTokenAccounts();

useInterval(() => {
const autoSettle = async () => {
const markets = (marketList || []).map((m) => m.market);
try {
if (!wallet) {
return;
if (!wallet) {
return;
}
setCurrentlyAutoSettling(true);
for (const marketInfo of marketInfoList) {
try {
console.log(`Autosettling ${marketInfo.name} ${marketInfo.address.toString()}`);
const market = await getCachedMarket(connection, marketInfo.address, marketInfo.programId);
const openOrderAccounts = await getCachedOpenOrderAccounts(connection, market, wallet.publicKey);
// settle funds into selected token wallets
const [baseAssocTokenAddress, quoteAssocTokenAddress] = await Promise.all([
getAssociatedTokenAddress(wallet.publicKey, market.baseMintAddress),
getAssociatedTokenAddress(wallet.publicKey, market.quoteMintAddress)
]);
const baseCurrencyAccount = getSelectedTokenAccountForMint(
tokenAccounts, market.baseMintAddress, baseAssocTokenAddress);
const quoteCurrencyAccount = getSelectedTokenAccountForMint(
tokenAccounts, market.quoteMintAddress, quoteAssocTokenAddress);
const openOrders = openOrderAccounts.find(oo => oo.market.equals(marketInfo.address));
if (baseCurrencyAccount && quoteCurrencyAccount && openOrders) {
await settleFunds({
market, openOrders, connection, wallet, baseCurrencyAccount, quoteCurrencyAccount
});
await sleep(1000);
}
} catch (e) {
console.log('Error auto settling funds: ' + e.message);
}

console.log('Auto settling');
await settleAllFunds({
connection,
wallet,
tokenAccounts: tokenAccounts || [],
markets,
selectedTokenAccounts,
});
} catch (e) {
console.log('Error auto settling funds: ' + e.message);
}
setCurrentlyAutoSettling(false);
};

connected && wallet?.autoApprove && autoSettleEnabled && autoSettle();
}, 10000);
(
connected &&
wallet?.autoApprove &&
autoSettleEnabled &&
!currentlyAutoSettling &&
autoSettle()
);
}, 60000);

return (
<PreferencesContext.Provider
Expand Down

0 comments on commit 0f5f04f

Please sign in to comment.