Skip to content

Commit

Permalink
Merge pull request #78 from clober-dex/fix/cache
Browse files Browse the repository at this point in the history
fix: clean cache for next call
  • Loading branch information
graykode authored Jul 11, 2024
2 parents 8219b2b + 082d513 commit cbba884
Show file tree
Hide file tree
Showing 29 changed files with 1,398 additions and 1,345 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@clober/v2-sdk",
"version": "0.0.55",
"version": "0.0.56",
"description": "🛠 An SDK for building applications on top of Clober V2",
"files": [
"dist"
Expand Down
61 changes: 52 additions & 9 deletions src/apis/chart-logs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CHAIN_IDS } from '../constants/chain'
import { CHART_LOG_INTERVALS, ChartLog } from '../type'
import { ChartLogDto } from '../model/chart-log'
import { cachedSubgraph } from '../constants/subgraph'
import { Subgraph } from '../constants/subgraph'

const CHART_LOG_INTERVAL_TIMESTAMP: {
[key in CHART_LOG_INTERVALS]: number
Expand All @@ -22,7 +22,7 @@ const CHART_LOG_INTERVAL_TIMESTAMP: {

const PAGE_SIZE = 1000

const getChartLogs = async ({
const getChartLogsFromSubgraph = async ({
chainId,
first,
skip,
Expand All @@ -39,11 +39,12 @@ const getChartLogs = async ({
from: number
to: number
}) => {
return cachedSubgraph[chainId]!.get<{
return Subgraph.get<{
data: {
chartLogs: ChartLogDto[]
}
}>(
chainId,
'getChartLogs',
'query getChartLogs($first: Int!, $skip: Int!, $marketCode: String!, $intervalType: String!, $from: BigInt!, $to: BigInt!) { chartLogs( first: $first, skip: $skip, orderBy: timestamp, orderDirection: desc where: { marketCode: $marketCode, intervalType: $intervalType, timestamp_gte: $from, timestamp_lte: $to, }) { timestamp open high low close baseVolume } }',
{
Expand All @@ -57,18 +58,19 @@ const getChartLogs = async ({
)
}

const getLatestChartLog = async ({
const getLatestChartLogFromSubgraph = async ({
chainId,
marketCode,
}: {
chainId: CHAIN_IDS
marketCode: string
}) => {
return cachedSubgraph[chainId]!.get<{
return Subgraph.get<{
data: {
chartLogs: ChartLogDto[]
}
}>(
chainId,
'getLatestChartLog',
'query getLatestChartLog($marketCode: String!) { chartLogs( first: 1, orderBy: timestamp, orderDirection: desc where: { marketCode: $marketCode, }) { timestamp open high low close baseVolume } }',
{
Expand All @@ -83,7 +85,7 @@ export async function fetchLatestChartLog(
): Promise<ChartLog> {
const {
data: { chartLogs },
} = await getLatestChartLog({
} = await getLatestChartLogFromSubgraph({
chainId,
marketCode: marketCode.toLowerCase(),
})
Expand All @@ -106,20 +108,61 @@ export async function fetchLatestChartLog(
}
}

const buildChartCacheKey = (
chainId: CHAIN_IDS,
marketCode: string,
intervalType: CHART_LOG_INTERVALS,
from: number,
to: number,
) => `${chainId}:${marketCode}:${intervalType}:${from}:${to}`
const chartLogsCache = new Map<string, ChartLog[]>()
const getChartLogsFromCache = (
chainId: CHAIN_IDS,
marketCode: string,
intervalType: CHART_LOG_INTERVALS,
from: number,
to: number,
): ChartLog[] | undefined =>
chartLogsCache.get(
buildChartCacheKey(chainId, marketCode, intervalType, from, to),
)
const setChartLogsToCache = (
chainId: CHAIN_IDS,
marketCode: string,
intervalType: CHART_LOG_INTERVALS,
from: number,
to: number,
chartLogs: ChartLog[],
) =>
chartLogsCache.set(
buildChartCacheKey(chainId, marketCode, intervalType, from, to),
chartLogs,
)

export async function fetchChartLogs(
chainId: CHAIN_IDS,
marketCode: string,
intervalType: CHART_LOG_INTERVALS,
from: number,
to: number,
): Promise<ChartLog[]> {
const cachedChartLogs = getChartLogsFromCache(
chainId,
marketCode,
intervalType,
from,
to,
)
if (cachedChartLogs !== undefined) {
return cachedChartLogs
}
const chartLogsBetweenFromAndTo: ChartLog[] = []
let skip = 0
// eslint-disable-next-line no-constant-condition
while (true) {
const {
data: { chartLogs },
} = await getChartLogs({
} = await getChartLogsFromSubgraph({
chainId,
first: PAGE_SIZE,
skip,
Expand Down Expand Up @@ -150,7 +193,7 @@ export async function fetchChartLogs(
)
const {
data: { chartLogs: chartLogsBeforeFrom },
} = await getChartLogs({
} = await getChartLogsFromSubgraph({
chainId,
first: 1,
skip: 0,
Expand Down Expand Up @@ -218,6 +261,6 @@ export async function fetchChartLogs(

timestampForAcc += intervalInNumber
}

setChartLogsToCache(chainId, marketCode, intervalType, from, to, result)
return result
}
32 changes: 19 additions & 13 deletions src/apis/market.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { PublicClient } from 'viem'

import { CHAIN_IDS } from '../constants/chain'
import { Market } from '../model/market'
import { Book } from '../model/book'
Expand All @@ -6,14 +8,13 @@ import { toBookId } from '../utils/book-id'
import { calculateUnitSize } from '../utils/unit-size'
import type { Currency } from '../model/currency'
import { CONTRACT_ADDRESSES } from '../constants/addresses'
import { cachedPublicClients } from '../constants/client'
import { BOOK_VIEWER_ABI } from '../abis/core/book-viewer-abi'
import { fetchIsOpened } from '../utils/open'
import { fetchCurrency } from '../utils/currency'
import { cachedSubgraph } from '../constants/subgraph'
import { Subgraph } from '../constants/subgraph'

const fetchBook = async (chainId: CHAIN_IDS, bookId: string) => {
return cachedSubgraph[chainId]!.get<{
const fetchBookFromSubgraph = async (chainId: CHAIN_IDS, bookId: string) => {
return Subgraph.get<{
data: {
book: {
depths: {
Expand All @@ -24,6 +25,7 @@ const fetchBook = async (chainId: CHAIN_IDS, bookId: string) => {
} | null
}
}>(
chainId,
'getBook',
'query getBook($bookId: ID!) { book(id: $bookId){ depths { tick unitAmount } } }',
{
Expand All @@ -33,22 +35,24 @@ const fetchBook = async (chainId: CHAIN_IDS, bookId: string) => {
}

const getBook = async (
publicClient: PublicClient,
chainId: CHAIN_IDS,
quoteCurrency: Currency,
baseCurrency: Currency,
useSubgraph: boolean,
n: number,
): Promise<Book> => {
const unitSize = await calculateUnitSize(chainId, quoteCurrency)
const unitSize = await calculateUnitSize(publicClient, chainId, quoteCurrency)
const bookId = toBookId(
chainId,
quoteCurrency.address,
baseCurrency.address,
unitSize,
)
if (cachedSubgraph[chainId]) {
if (useSubgraph) {
const {
data: { book },
} = await fetchBook(chainId, bookId.toString())
} = await fetchBookFromSubgraph(chainId, bookId.toString())
new Book({
chainId,
id: bookId,
Expand All @@ -68,13 +72,13 @@ const getBook = async (
}

const [depths, isOpened] = await Promise.all([

Check failure on line 74 in src/apis/market.ts

View workflow job for this annotation

GitHub Actions / NPM Test (18.10)

limit-order.test.ts > make ask order

ContractFunctionExecutionError: The request took too long to respond. URL: http://127.0.0.1:8545/1720677419388 Request body: {"method":"eth_call","params":[{"data":"0x694d8a2900000000000000006631c4f1d45bb65b8eeeb5bcdea09be74f45c3afbc9ba1e4000000000000000000000000000000000000000000000000000000000007ffff0000000000000000000000000000000000000000000000000000000000000064","to":"0xA7603C4c895a533E66c30EA76cC6F6A6A0c5cbFe"},"latest"]} Raw Call Arguments: to: 0xA7603C4c895a533E66c30EA76cC6F6A6A0c5cbFe data: 0x694d8a2900000000000000006631c4f1d45bb65b8eeeb5bcdea09be74f45c3afbc9ba1e4000000000000000000000000000000000000000000000000000000000007ffff0000000000000000000000000000000000000000000000000000000000000064 Contract Call: address: 0xA7603C4c895a533E66c30EA76cC6F6A6A0c5cbFe function: getLiquidity(uint192 id, int24 tick, uint256 n) args: (2505799676027433010421416925405481572661563164234992034276, 524287, 100) Docs: https://viem.sh/docs/contract/readContract Details: The request timed out. Version: [email protected] ❯ getContractError ../node_modules/viem/utils/errors/getContractError.ts:72:10 ❯ readContract ../node_modules/viem/actions/public/readContract.ts:128:11 ❯ getBook ../src/apis/market.ts:74:30 ❯ Module.fetchMarket ../src/apis/market.ts:118:30 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { details: 'The request timed out.', docsPath: '/docs/contract/readContract', metaMessages: [ 'URL: http://127.0.0.1:8545/1720677419388', 'Request body: {"method":"eth_call","params":[{"data":"0x694d8a2900000000000000006631c4f1d45bb65b8eeeb5bcdea09be74f45c3afbc9ba1e4000000000000000000000000000000000000000000000000000000000007ffff0000000000000000000000000000000000000000000000000000000000000064","to":"0xA7603C4c895a533E66c30EA76cC6F6A6A0c5cbFe"},"latest"]}', ' ', 'Raw Call Arguments:', ' to: 0xA7603C4c895a533E66c30EA76cC6F6A6A0c5cbFe\n data: 0x694d8a2900000000000000006631c4f1d45bb65b8eeeb5bcdea09be74f45c3afbc9ba1e4000000000000000000000000000000000000000000000000000000000007ffff0000000000000000000000000000000000000000000000000000000000000064', ' ', 'Contract Call:', ' address: 0xA7603C4c895a533E66c30EA76cC6F6A6A0c5cbFe\n function: getLiquidity(uint192 id, int24 tick, uint256 n)\n args: (2505799676027433010421416925405481572661563164234992034276, 524287, 100)' ], shortMessage: 'The request took too long to respond.', version: '[email protected]', abi: [ { anonymous: false, inputs: [ { indexed: true, internalType: 'address', name: 'previousOwner', type: 'address' }, { indexed: true, internalType: 'address', name: 'newOwner', type: 'address' } ], name: 'OwnershipTransferred', type: 'event' }, { anonymous: false, inputs: [ { indexed: true, internalType: 'address', name: 'previousImplementation', type: 'address' }, { indexed: true, internalType: 'address', name: 'newImplementation', type: 'address' } ], name: 'ProxyImplementationUpdated', type: 'event' }, { stateMutability: 'payable', type: 'fallback' }, { inputs: [], name: 'owner', outputs: [ { internalType: 'address', name: '', type: 'address' } ], stateMutability: 'view', type: 'function' }, { inputs: [ { internalType: 'bytes4', name: 'id', type: 'bytes4' } ], name: 'supportsInterface', outputs: [ { internalType: 'bool', name: '', type: 'bool' } ], stateMutability: 'view', type: 'function' }, { inputs: [ { internalType: 'address', name: 'newOwner', type: 'address' } ], name: 'transferOwnership', outputs: [], stateMutability: 'nonpayable', type: 'function' }, { inputs: [ { internalType: 'address', name: 'newImplementation', type: 'address' } ], name: 'upgradeTo', outputs: [], stateMutability: 'nonpayable', type: 'function' }, { inputs: [ { internalType: 'address', name: 'newImplementation', type: 'address' }, { internalType: 'bytes', name: 'data', type: 'bytes' } ], name: 'upgradeToAndCall', outputs: [], stateMutability: 'payable', type: 'function' }, { stateMutability: 'payable', type: 'receive' }, { inputs: [], name: 'InvalidTick', type: 'error' }, { inputs: [ { internalType: 'uint256', name: 'value', type: 'uint256' } ], name: '
cachedPublicClients[chainId].readContract({
publicClient.readContract({
address: CONTRACT_ADDRESSES[chainId]!.BookViewer,
abi: BOOK_VIEWER_ABI,
functionName: 'getLiquidity',
args: [bookId, Number(2n ** 19n - 1n), BigInt(n)],
}),
fetchIsOpened(chainId, bookId),
fetchIsOpened(publicClient, chainId, bookId),
])

return new Book({
Expand All @@ -92,8 +96,10 @@ const getBook = async (
}

export async function fetchMarket(
publicClient: PublicClient,
chainId: CHAIN_IDS,
tokenAddresses: `0x${string}`[],
useSubgraph: boolean,
n = 100,
): Promise<Market> {
if (tokenAddresses.length !== 2) {
Expand All @@ -106,12 +112,12 @@ export async function fetchMarket(
])

const [quoteCurrency, baseCurrency] = await Promise.all([
fetchCurrency(chainId, quoteTokenAddress),
fetchCurrency(chainId, baseTokenAddress),
fetchCurrency(publicClient, chainId, quoteTokenAddress),
fetchCurrency(publicClient, chainId, baseTokenAddress),
])
const [bidBook, askBook] = await Promise.all([
getBook(chainId, quoteCurrency, baseCurrency, n),
getBook(chainId, baseCurrency, quoteCurrency, n),
getBook(publicClient, chainId, quoteCurrency, baseCurrency, useSubgraph, n),
getBook(publicClient, chainId, baseCurrency, quoteCurrency, useSubgraph, n),
])

return new Market({
Expand Down
42 changes: 27 additions & 15 deletions src/apis/open-order.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { formatUnits, getAddress, isAddressEqual } from 'viem'
import { formatUnits, getAddress, isAddressEqual, PublicClient } from 'viem'

import { CHAIN_IDS } from '../constants/chain'
import { getMarketId } from '../utils/market'
Expand All @@ -10,14 +10,18 @@ import type { OpenOrder, OpenOrderDto } from '../model/open-order'
import { fetchCurrency } from '../utils/currency'
import { applyPercent } from '../utils/bigint'
import { MAKER_DEFAULT_POLICY } from '../constants/fee'
import { cachedSubgraph } from '../constants/subgraph'
import { Subgraph } from '../constants/subgraph'

const getOpenOrder = async (chainId: CHAIN_IDS, orderId: string) => {
return cachedSubgraph[chainId]!.get<{
const getOpenOrderFromSubgraph = async (
chainId: CHAIN_IDS,
orderId: string,
) => {
return Subgraph.get<{
data: {
openOrder: OpenOrderDto | null
}
}>(
chainId,
'getOpenOrder',
'query getOpenOrder($orderId: ID!) { openOrder(id: $orderId) { id user book { id base { id name symbol decimals } quote { id name symbol decimals } unitSize } tick txHash createdAt unitAmount unitFilledAmount unitClaimedAmount unitClaimableAmount orderIndex } }',
{
Expand All @@ -26,12 +30,16 @@ const getOpenOrder = async (chainId: CHAIN_IDS, orderId: string) => {
)
}

const getOpenOrders = async (chainId: CHAIN_IDS, orderIds: string[]) => {
return cachedSubgraph[chainId]!.get<{
const getOpenOrdersFromSubgraph = async (
chainId: CHAIN_IDS,
orderIds: string[],
) => {
return Subgraph.get<{
data: {
openOrders: OpenOrderDto[]
}
}>(
chainId,
'getOpenOrders',
'query getOpenOrders($orderIds: [ID!]!) { openOrders(where: {id_in: $orderIds}) { id user book { id base { id name symbol decimals } quote { id name symbol decimals } unitSize } tick txHash createdAt unitAmount unitFilledAmount unitClaimedAmount unitClaimableAmount orderIndex } }',
{
Expand All @@ -40,15 +48,16 @@ const getOpenOrders = async (chainId: CHAIN_IDS, orderIds: string[]) => {
)
}

const getOpenOrdersByUserAddress = async (
const getOpenOrdersByUserAddressFromSubgraph = async (
chainId: CHAIN_IDS,
userAddress: `0x${string}`,
) => {
return cachedSubgraph[chainId]!.get<{
return Subgraph.get<{
data: {
openOrders: OpenOrderDto[]
}
}>(
chainId,
'getOpenOrdersByUserAddress',
'query getOpenOrdersByUserAddress($userAddress: String!) { openOrders(where: { user: $userAddress }) { id user book { id base { id name symbol decimals } quote { id name symbol decimals } unitSize } tick txHash createdAt unitAmount unitFilledAmount unitClaimedAmount unitClaimableAmount orderIndex } }',
{
Expand All @@ -58,12 +67,13 @@ const getOpenOrdersByUserAddress = async (
}

export async function fetchOpenOrdersByUserAddress(
publicClient: PublicClient,
chainId: CHAIN_IDS,
userAddress: `0x${string}`,
): Promise<OpenOrder[]> {
const {
data: { openOrders },
} = await getOpenOrdersByUserAddress(chainId, userAddress)
} = await getOpenOrdersByUserAddressFromSubgraph(chainId, userAddress)
const currencies = await Promise.all(
openOrders
.map((openOrder) => [
Expand All @@ -75,37 +85,39 @@ export async function fetchOpenOrdersByUserAddress(
(address, index, self) =>
self.findIndex((c) => isAddressEqual(c, address)) === index,
)
.map((address) => fetchCurrency(chainId, address)),
.map((address) => fetchCurrency(publicClient, chainId, address)),
)
return openOrders.map((openOrder) =>
toOpenOrder(chainId, currencies, openOrder),
)
}

export async function fetchOpenOrder(
publicClient: PublicClient,
chainId: CHAIN_IDS,
id: string,
): Promise<OpenOrder> {
const {
data: { openOrder },
} = await getOpenOrder(chainId, id)
} = await getOpenOrderFromSubgraph(chainId, id)
if (!openOrder) {
throw new Error(`Open order not found: ${id}`)
}
const currencies = await Promise.all([
fetchCurrency(chainId, getAddress(openOrder.book.base.id)),
fetchCurrency(chainId, getAddress(openOrder.book.quote.id)),
fetchCurrency(publicClient, chainId, getAddress(openOrder.book.base.id)),
fetchCurrency(publicClient, chainId, getAddress(openOrder.book.quote.id)),
])
return toOpenOrder(chainId, currencies, openOrder)
}

export async function fetchOpenOrders(
publicClient: PublicClient,
chainId: CHAIN_IDS,
ids: string[],
): Promise<OpenOrder[]> {
const {
data: { openOrders },
} = await getOpenOrders(chainId, ids)
} = await getOpenOrdersFromSubgraph(chainId, ids)
const currencies = await Promise.all(
openOrders
.map((openOrder) => [
Expand All @@ -117,7 +129,7 @@ export async function fetchOpenOrders(
(address, index, self) =>
self.findIndex((c) => isAddressEqual(c, address)) === index,
)
.map((address) => fetchCurrency(chainId, address)),
.map((address) => fetchCurrency(publicClient, chainId, address)),
)
return openOrders.map((openOrder) =>
toOpenOrder(chainId, currencies, openOrder),
Expand Down
Loading

0 comments on commit cbba884

Please sign in to comment.