Skip to content

Commit f186f55

Browse files
creed-victorpietro-maximofftiltom
committed
SOv-4462: charts using indexer (#1036)
* 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 <pietro.maxim.official@protonmail.com> Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> Co-authored-by: tiltom <tilnak.tomas@gmail.com>
1 parent d9ff407 commit f186f55

File tree

15 files changed

+979
-155
lines changed

15 files changed

+979
-155
lines changed

.changeset/slow-drinks-invite.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"frontend": patch
3+
---
4+
5+
SOV-4462: D2 charts on Convert page

apps/frontend/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
22

33
# dependencies
4+
/public/charting_library
5+
/public/datafeeds
46
/node_modules
57
/.pnp
68
.pnp.js

apps/frontend/package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"@loadable/component": "5.15.2",
1010
"@sovryn-zero/lib-base": "0.2.1",
1111
"@sovryn-zero/lib-ethers": "0.2.5",
12+
"@sovryn/charting-library": "2.0.0",
1213
"@sovryn/contracts": "*",
1314
"@sovryn/ethers-provider": "*",
1415
"@sovryn/onboard-bitget": "1.0.1",
@@ -58,6 +59,7 @@
5859
"rxjs": "7.5.6",
5960
"sanitize-html": "2.11.0",
6061
"socket.io-client": "4.5.4",
62+
"storage-factory": "^0.2.1",
6163
"utf8": "^3.0.0",
6264
"zustand": "^4.5.1"
6365
},
@@ -92,16 +94,17 @@
9294
},
9395
"scripts": {
9496
"predev": "yarn generate:graphql",
95-
"dev": "craco start",
97+
"dev": "yarn copy-libs && craco start",
9698
"prebuild": "yarn generate:graphql",
97-
"build": "craco build",
99+
"build": "yarn copy-libs && craco build",
98100
"test": "craco test --watchAll=false --passWithNoTests",
99101
"test:staged": "craco test --watchAll=false --passWithNoTests --bail --onlyChanged",
100102
"lint": "eslint -c .eslintrc.js ./",
101103
"generate:graphql": "graphql-codegen",
102104
"generate:graphql:fetch:testnet": "env-cmd -f .env.development.local graphql-codegen -c codegen.fetch.yml",
103105
"generate:graphql:fetch:mainnet": "env-cmd -f .env.staging graphql-codegen -c codegen.fetch.yml",
104-
"find-deadcode": "ts-prune -s .generated.tsx | grep -v '(used in module)'"
106+
"find-deadcode": "ts-prune -s .generated.tsx | grep -v '(used in module)'",
107+
"copy-libs": "node scripts/copyLibs.js"
105108
},
106109
"browserslist": [
107110
"last 5 chrome version",

apps/frontend/scripts/copyLibs.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
const fsp = fs.promises;
4+
5+
async function deleteDir(dir) {
6+
if (fs.existsSync(dir)) {
7+
await fsp.rmdir(dir, { recursive: true });
8+
console.log(`Deleted directory: ${dir}`);
9+
}
10+
}
11+
12+
async function copyDir(src, dest) {
13+
await fsp.mkdir(dest, { recursive: true });
14+
const entries = await fsp.readdir(src, { withFileTypes: true });
15+
16+
for (const entry of entries) {
17+
const srcPath = path.join(src, entry.name);
18+
const destPath = path.join(dest, entry.name);
19+
20+
if (entry.isDirectory()) {
21+
await copyDir(srcPath, destPath);
22+
} else {
23+
await fsp.copyFile(srcPath, destPath);
24+
}
25+
}
26+
}
27+
28+
async function copyLibs() {
29+
const chartingLibrarySrc = path.resolve(
30+
__dirname,
31+
'../../../node_modules/@sovryn/charting-library/public/charting_library',
32+
);
33+
const chartingLibraryDest = path.resolve(
34+
__dirname,
35+
'../public/charting_library',
36+
);
37+
38+
const datafeedsSrc = path.resolve(
39+
__dirname,
40+
'../../../node_modules/@sovryn/charting-library/public/datafeeds',
41+
);
42+
const datafeedsDest = path.resolve(__dirname, '../public/datafeeds');
43+
44+
if (fs.existsSync(chartingLibrarySrc)) {
45+
await deleteDir(chartingLibraryDest);
46+
await copyDir(chartingLibrarySrc, chartingLibraryDest);
47+
console.log('Charting Library copied.');
48+
} else {
49+
console.error('Charting Library source not found.');
50+
}
51+
52+
if (fs.existsSync(datafeedsSrc)) {
53+
await deleteDir(datafeedsDest);
54+
await copyDir(datafeedsSrc, datafeedsDest);
55+
console.log('Datafeeds copied.');
56+
} else {
57+
console.error('Datafeeds source not found.');
58+
}
59+
}
60+
61+
copyLibs();
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { storageFactory } from 'storage-factory';
2+
3+
import { ResolutionString } from '@sovryn/charting-library/src/charting_library';
4+
5+
import { INDEXER_SERVICE } from '../../../constants/infrastructure';
6+
import { Environments } from '../../../types/global';
7+
import { CandleDuration } from './dictionary';
8+
9+
export const REFRESH_RATE = 15 * 1e3;
10+
export const MAXIMUM_CHUNK_SIZE = 1e3;
11+
export const endTimeCache = new Map<string, number>();
12+
export const supportedResolutions = [
13+
'1',
14+
'5',
15+
'10',
16+
'15',
17+
'30',
18+
'60',
19+
'240',
20+
'720',
21+
'1D',
22+
'3D',
23+
'1W',
24+
'1M',
25+
] as ResolutionString[];
26+
27+
export const resolutionMap: { [key: string]: CandleDuration } = {
28+
'1': CandleDuration.M_1,
29+
'5': CandleDuration.M_1,
30+
'10': CandleDuration.M_10,
31+
'15': CandleDuration.M_15,
32+
'30': CandleDuration.M_30,
33+
'60': CandleDuration.H_1,
34+
H: CandleDuration.H_1,
35+
'240': CandleDuration.H_4,
36+
'720': CandleDuration.H_12,
37+
'1440': CandleDuration.D_1,
38+
D: CandleDuration.D_1,
39+
'1D': CandleDuration.D_1,
40+
'3D': CandleDuration.D_3,
41+
W: CandleDuration.W_1,
42+
'1W': CandleDuration.W_1,
43+
M: CandleDuration.D_30,
44+
'1M': CandleDuration.D_30,
45+
};
46+
47+
export const local = storageFactory(() => localStorage);
48+
49+
export const chartStorageKey = 'sovryn.charts';
50+
51+
export const config = {
52+
exchanges: [],
53+
symbols_types: [],
54+
supported_resolutions: supportedResolutions,
55+
supports_time: false,
56+
};
57+
58+
export const SOVRYN_INDEXER_MAINNET = `${
59+
INDEXER_SERVICE[Environments.Mainnet]
60+
}chart`;
61+
62+
export const SOVRYN_INDEXER_TESTNET = `${
63+
INDEXER_SERVICE[Environments.Testnet]
64+
}chart`;
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { useApolloClient } from '@apollo/client';
2+
3+
import React, { FC, useEffect, useLayoutEffect, useRef, useState } from 'react';
4+
5+
import {
6+
ChartingLibraryWidgetOptions,
7+
IChartingLibraryWidget,
8+
ResolutionString,
9+
widget,
10+
} from '@sovryn/charting-library/src/charting_library';
11+
import { noop } from '@sovryn/ui';
12+
13+
import { SeriesStyle, TradingChartProps } from './TradingChart.types';
14+
import Datafeed from './datafeed';
15+
16+
export const TradingChart: FC<TradingChartProps> = ({ pair }) => {
17+
const chartContainerRef =
18+
useRef<HTMLDivElement>() as React.MutableRefObject<HTMLInputElement>;
19+
20+
const [hasCharts, setHasCharts] = useState(false);
21+
const [chart, setChart] = useState<IChartingLibraryWidget | null>(null);
22+
const client = useApolloClient();
23+
24+
useEffect(() => {
25+
try {
26+
const widgetOptions: ChartingLibraryWidgetOptions = {
27+
symbol: pair,
28+
datafeed: Datafeed(client),
29+
interval: '1D' as ResolutionString,
30+
container: chartContainerRef.current,
31+
library_path: '/charting_library/',
32+
load_last_chart: true, //last chart layout (if present)
33+
theme: 'dark',
34+
locale: 'en',
35+
disabled_features: ['header_symbol_search', 'header_compare'],
36+
enabled_features: [
37+
'study_templates',
38+
'side_toolbar_in_fullscreen_mode',
39+
],
40+
charts_storage_url: 'https://saveload.tradingview.com',
41+
charts_storage_api_version: '1.1',
42+
client_id: 'tradingview.com',
43+
user_id: 'public_user_id',
44+
fullscreen: false,
45+
autosize: true,
46+
studies_overrides: {},
47+
};
48+
49+
const myChart = new widget(widgetOptions);
50+
setChart(myChart);
51+
myChart.onChartReady(() => {
52+
setHasCharts(true);
53+
});
54+
55+
return () => {
56+
myChart.remove();
57+
setHasCharts(false);
58+
setChart(null);
59+
};
60+
} catch (e) {
61+
console.error(e);
62+
setHasCharts(false);
63+
}
64+
// eslint-disable-next-line react-hooks/exhaustive-deps
65+
}, [client]);
66+
67+
useLayoutEffect(() => {
68+
if (chart && hasCharts) {
69+
chart.chart().resetData();
70+
71+
chart.chart().setChartType(SeriesStyle.Candles as number);
72+
73+
chart.chart().setSymbol(pair, noop);
74+
}
75+
}, [chart, hasCharts, pair]);
76+
77+
return (
78+
<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">
79+
<div ref={chartContainerRef} className="h-full min-h-96" />
80+
</div>
81+
);
82+
};
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
export type TradingChartProps = {
2+
pair: string;
3+
};
4+
5+
export enum SeriesStyle {
6+
Bars = 0,
7+
Candles = 1,
8+
Line = 2,
9+
Area = 3,
10+
HeikenAshi = 8,
11+
HollowCandles = 9,
12+
Renko = 4,
13+
Kagi = 5,
14+
PointAndFigure = 6,
15+
LineBreak = 7,
16+
}
17+
18+
export type Bar = {
19+
time: number;
20+
low: number;
21+
high: number;
22+
open: number;
23+
close: number;
24+
volume?: number;
25+
};
26+
27+
export type CandleSticksResponse = {
28+
id: string;
29+
open: number;
30+
high: number;
31+
low: number;
32+
close: number;
33+
totalVolume: number;
34+
periodStartUnix: number;
35+
};
36+
37+
export type TimestampChunk = {
38+
from: number;
39+
to: number;
40+
};
41+
42+
export type Candle = {
43+
open: string;
44+
high: string;
45+
low: string;
46+
close: string;
47+
totalVolume: string;
48+
periodStartUnix: string;
49+
};
50+
51+
export type StoredCharts = {
52+
[id: string]: ChartData;
53+
};
54+
55+
export type ChartData = {
56+
id: string;
57+
name: string;
58+
symbol: string;
59+
resolution: string;
60+
content: string;
61+
timestamp: number;
62+
};
63+
64+
export type StoredTemplates = {
65+
[id: string]: TemplateData;
66+
};
67+
68+
export type TemplateData = {
69+
name: string;
70+
content: string;
71+
};
72+
73+
export type SubItem = {
74+
symbolInfo: any;
75+
subscribeUID: string; //e.g. SOV/USDT_10
76+
resolution: string;
77+
lastBar: Bar;
78+
handler: Function;
79+
timer?: number;
80+
};

0 commit comments

Comments
 (0)