Skip to content

Commit

Permalink
SOv-4462: charts using indexer (#1036)
Browse files Browse the repository at this point in the history
* chore: add chart to Convert Page

* chore: update TradingChart component with using version of lib

* Create slow-drinks-invite.md

* fix: wrong import paths to charting library

* chore: update copyLibs

* Use indexer as datafeed

* fix: datafeed

* chore: remove console log

* Delete obsolete code

* fix: add default tokens and bob chain to chart

* fix: chart

---------

Co-authored-by: pietro-maximoff <[email protected]>
Co-authored-by: Pietro <[email protected]>
Co-authored-by: tiltom <[email protected]>
  • Loading branch information
4 people committed Oct 28, 2024
1 parent ae74555 commit d5079da
Show file tree
Hide file tree
Showing 15 changed files with 979 additions and 155 deletions.
5 changes: 5 additions & 0 deletions .changeset/slow-drinks-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"frontend": patch
---

SOV-4462: D2 charts on Convert page
2 changes: 2 additions & 0 deletions apps/frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/public/charting_library
/public/datafeeds
/node_modules
/.pnp
.pnp.js
Expand Down
9 changes: 6 additions & 3 deletions apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"@loadable/component": "5.15.2",
"@sovryn-zero/lib-base": "0.2.1",
"@sovryn-zero/lib-ethers": "0.2.5",
"@sovryn/charting-library": "2.0.0",
"@sovryn/contracts": "*",
"@sovryn/ethers-provider": "*",
"@sovryn/onboard-bitget": "1.0.1",
Expand Down Expand Up @@ -58,6 +59,7 @@
"rxjs": "7.5.6",
"sanitize-html": "2.11.0",
"socket.io-client": "4.5.4",
"storage-factory": "^0.2.1",
"utf8": "^3.0.0",
"zustand": "^4.5.1"
},
Expand Down Expand Up @@ -92,16 +94,17 @@
},
"scripts": {
"predev": "yarn generate:graphql",
"dev": "craco start",
"dev": "yarn copy-libs && craco start",
"prebuild": "yarn generate:graphql",
"build": "craco build",
"build": "yarn copy-libs && craco build",
"test": "craco test --watchAll=false --passWithNoTests",
"test:staged": "craco test --watchAll=false --passWithNoTests --bail --onlyChanged",
"lint": "eslint -c .eslintrc.js ./",
"generate:graphql": "graphql-codegen",
"generate:graphql:fetch:testnet": "env-cmd -f .env.development.local graphql-codegen -c codegen.fetch.yml",
"generate:graphql:fetch:mainnet": "env-cmd -f .env.staging graphql-codegen -c codegen.fetch.yml",
"find-deadcode": "ts-prune -s .generated.tsx | grep -v '(used in module)'"
"find-deadcode": "ts-prune -s .generated.tsx | grep -v '(used in module)'",
"copy-libs": "node scripts/copyLibs.js"
},
"browserslist": [
"last 5 chrome version",
Expand Down
61 changes: 61 additions & 0 deletions apps/frontend/scripts/copyLibs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const fs = require('fs');
const path = require('path');
const fsp = fs.promises;

async function deleteDir(dir) {
if (fs.existsSync(dir)) {
await fsp.rmdir(dir, { recursive: true });
console.log(`Deleted directory: ${dir}`);
}
}

async function copyDir(src, dest) {
await fsp.mkdir(dest, { recursive: true });
const entries = await fsp.readdir(src, { withFileTypes: true });

for (const entry of entries) {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);

if (entry.isDirectory()) {
await copyDir(srcPath, destPath);
} else {
await fsp.copyFile(srcPath, destPath);
}
}
}

async function copyLibs() {
const chartingLibrarySrc = path.resolve(
__dirname,
'../../../node_modules/@sovryn/charting-library/public/charting_library',
);
const chartingLibraryDest = path.resolve(
__dirname,
'../public/charting_library',
);

const datafeedsSrc = path.resolve(
__dirname,
'../../../node_modules/@sovryn/charting-library/public/datafeeds',
);
const datafeedsDest = path.resolve(__dirname, '../public/datafeeds');

if (fs.existsSync(chartingLibrarySrc)) {
await deleteDir(chartingLibraryDest);
await copyDir(chartingLibrarySrc, chartingLibraryDest);
console.log('Charting Library copied.');
} else {
console.error('Charting Library source not found.');
}

if (fs.existsSync(datafeedsSrc)) {
await deleteDir(datafeedsDest);
await copyDir(datafeedsSrc, datafeedsDest);
console.log('Datafeeds copied.');
} else {
console.error('Datafeeds source not found.');
}
}

copyLibs();
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { storageFactory } from 'storage-factory';

import { ResolutionString } from '@sovryn/charting-library/src/charting_library';

import { INDEXER_SERVICE } from '../../../constants/infrastructure';
import { Environments } from '../../../types/global';
import { CandleDuration } from './dictionary';

export const REFRESH_RATE = 15 * 1e3;
export const MAXIMUM_CHUNK_SIZE = 1e3;
export const endTimeCache = new Map<string, number>();
export const supportedResolutions = [
'1',
'5',
'10',
'15',
'30',
'60',
'240',
'720',
'1D',
'3D',
'1W',
'1M',
] as ResolutionString[];

export const resolutionMap: { [key: string]: CandleDuration } = {
'1': CandleDuration.M_1,
'5': CandleDuration.M_1,
'10': CandleDuration.M_10,
'15': CandleDuration.M_15,
'30': CandleDuration.M_30,
'60': CandleDuration.H_1,
H: CandleDuration.H_1,
'240': CandleDuration.H_4,
'720': CandleDuration.H_12,
'1440': CandleDuration.D_1,
D: CandleDuration.D_1,
'1D': CandleDuration.D_1,
'3D': CandleDuration.D_3,
W: CandleDuration.W_1,
'1W': CandleDuration.W_1,
M: CandleDuration.D_30,
'1M': CandleDuration.D_30,
};

export const local = storageFactory(() => localStorage);

export const chartStorageKey = 'sovryn.charts';

export const config = {
exchanges: [],
symbols_types: [],
supported_resolutions: supportedResolutions,
supports_time: false,
};

export const SOVRYN_INDEXER_MAINNET = `${
INDEXER_SERVICE[Environments.Mainnet]
}chart`;

export const SOVRYN_INDEXER_TESTNET = `${
INDEXER_SERVICE[Environments.Testnet]
}chart`;
82 changes: 82 additions & 0 deletions apps/frontend/src/app/2_molecules/TradingChart/TradingChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { useApolloClient } from '@apollo/client';

import React, { FC, useEffect, useLayoutEffect, useRef, useState } from 'react';

import {
ChartingLibraryWidgetOptions,
IChartingLibraryWidget,
ResolutionString,
widget,
} from '@sovryn/charting-library/src/charting_library';
import { noop } from '@sovryn/ui';

import { SeriesStyle, TradingChartProps } from './TradingChart.types';
import Datafeed from './datafeed';

export const TradingChart: FC<TradingChartProps> = ({ pair }) => {
const chartContainerRef =
useRef<HTMLDivElement>() as React.MutableRefObject<HTMLInputElement>;

const [hasCharts, setHasCharts] = useState(false);
const [chart, setChart] = useState<IChartingLibraryWidget | null>(null);
const client = useApolloClient();

useEffect(() => {
try {
const widgetOptions: ChartingLibraryWidgetOptions = {
symbol: pair,
datafeed: Datafeed(client),
interval: '1D' as ResolutionString,
container: chartContainerRef.current,
library_path: '/charting_library/',
load_last_chart: true, //last chart layout (if present)
theme: 'dark',
locale: 'en',
disabled_features: ['header_symbol_search', 'header_compare'],
enabled_features: [
'study_templates',
'side_toolbar_in_fullscreen_mode',
],
charts_storage_url: 'https://saveload.tradingview.com',
charts_storage_api_version: '1.1',
client_id: 'tradingview.com',
user_id: 'public_user_id',
fullscreen: false,
autosize: true,
studies_overrides: {},
};

const myChart = new widget(widgetOptions);
setChart(myChart);
myChart.onChartReady(() => {
setHasCharts(true);
});

return () => {
myChart.remove();
setHasCharts(false);
setChart(null);
};
} catch (e) {
console.error(e);
setHasCharts(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [client]);

useLayoutEffect(() => {
if (chart && hasCharts) {
chart.chart().resetData();

chart.chart().setChartType(SeriesStyle.Candles as number);

chart.chart().setSymbol(pair, noop);
}
}, [chart, hasCharts, pair]);

return (
<div className="lg:mt-12 mt-6 w-full p-0 sm:border sm:border-gray-50 sm:rounded sm:p-6 sm:bg-gray-90">
<div ref={chartContainerRef} className="h-full min-h-96" />
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
export type TradingChartProps = {
pair: string;
};

export enum SeriesStyle {
Bars = 0,
Candles = 1,
Line = 2,
Area = 3,
HeikenAshi = 8,
HollowCandles = 9,
Renko = 4,
Kagi = 5,
PointAndFigure = 6,
LineBreak = 7,
}

export type Bar = {
time: number;
low: number;
high: number;
open: number;
close: number;
volume?: number;
};

export type CandleSticksResponse = {
id: string;
open: number;
high: number;
low: number;
close: number;
totalVolume: number;
periodStartUnix: number;
};

export type TimestampChunk = {
from: number;
to: number;
};

export type Candle = {
open: string;
high: string;
low: string;
close: string;
totalVolume: string;
periodStartUnix: string;
};

export type StoredCharts = {
[id: string]: ChartData;
};

export type ChartData = {
id: string;
name: string;
symbol: string;
resolution: string;
content: string;
timestamp: number;
};

export type StoredTemplates = {
[id: string]: TemplateData;
};

export type TemplateData = {
name: string;
content: string;
};

export type SubItem = {
symbolInfo: any;
subscribeUID: string; //e.g. SOV/USDT_10
resolution: string;
lastBar: Bar;
handler: Function;
timer?: number;
};
Loading

0 comments on commit d5079da

Please sign in to comment.