Skip to content

Commit 1bd6d20

Browse files
committed
use subscan
1 parent 42bd878 commit 1bd6d20

15 files changed

Lines changed: 412 additions & 67 deletions

File tree

Lines changed: 161 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,174 @@
11
'use client';
22

3-
import React from 'react';
3+
import React, { useEffect, useMemo, useState } from 'react';
4+
import ReactECharts from 'echarts-for-react';
45
import styles from './HistoricalPricingChart.module.scss';
6+
import { useUnit } from 'effector-react';
7+
import { $network } from '@/api/connection';
8+
import { getTokenSymbol, toUnit } from '@/utils';
9+
import { Network } from '@/types';
10+
import { subscanFetch } from '@/subscan';
11+
12+
type SubscanSaleData = {
13+
sales_cycle: number;
14+
start_price: string;
15+
price: string;
16+
sellout_price: string;
17+
cores_sold: number;
18+
cores_offered: number;
19+
};
20+
21+
type SalePoint = {
22+
cycle: number;
23+
price: number;
24+
startPrice: number;
25+
};
26+
27+
const fetchHistoricalSalePrices = async (network: Network): Promise<SalePoint[]> => {
28+
// Get the latest sale to know the current cycle.
29+
const latest = await subscanFetch<SubscanSaleData>(network, '/api/scan/broker/sale');
30+
if (!latest?.sales_cycle) return [];
31+
32+
const currentCycle = latest.sales_cycle;
33+
const cyclesToFetch = Math.min(currentCycle, 3);
34+
35+
// Fetch the last 3 cycles sequentially to respect rate limits.
36+
const points: SalePoint[] = [];
37+
for (let i = cyclesToFetch - 1; i >= 0; i--) {
38+
const cycle = currentCycle - i;
39+
const data = await subscanFetch<SubscanSaleData>(network, '/api/scan/broker/sale', {
40+
sales_cycle: cycle,
41+
});
42+
if (data && data.sales_cycle > 0) {
43+
points.push({
44+
cycle: data.sales_cycle,
45+
price: parseInt(data.sellout_price || data.price || '0'),
46+
startPrice: parseInt(data.start_price || '0'),
47+
});
48+
}
49+
}
50+
51+
return points;
52+
};
553

654
export default function HistoricalPricingChart() {
55+
const network = useUnit($network);
56+
const [data, setData] = useState<SalePoint[]>([]);
57+
const [loading, setLoading] = useState(true);
58+
const token = getTokenSymbol(network);
59+
60+
useEffect(() => {
61+
if (!network) return;
62+
setLoading(true);
63+
fetchHistoricalSalePrices(network)
64+
.then(setData)
65+
.finally(() => setLoading(false));
66+
}, [network]);
67+
68+
const option = useMemo(() => {
69+
if (!network || !data.length) return null;
70+
71+
const fmt = (raw: number) => toUnit(network, BigInt(raw));
72+
const yVals = [
73+
...data.map((d) => Number(fmt(d.price))),
74+
...data.map((d) => Number(fmt(d.startPrice))),
75+
];
76+
const max = Math.max(...yVals, 0);
77+
const tickCount = 6;
78+
const step = max > 0 ? max / tickCount : 1;
79+
const yMax = Math.max(max, step);
80+
81+
return {
82+
backgroundColor: 'transparent',
83+
grid: { left: 70, right: 40, top: 40, bottom: 50 },
84+
tooltip: {
85+
trigger: 'axis',
86+
formatter: (params: any[]) => {
87+
const p = params[0];
88+
const d = data[p?.dataIndex ?? 0];
89+
const price = Number(toUnit(network, BigInt(d.price))).toLocaleString(undefined, {
90+
maximumFractionDigits: 6,
91+
});
92+
const startP = Number(toUnit(network, BigInt(d.startPrice))).toLocaleString(undefined, {
93+
maximumFractionDigits: 6,
94+
});
95+
return [
96+
`Sale cycle: ${d.cycle}`,
97+
`Sellout price: <b>${price} ${token}</b>`,
98+
`Start price: <b>${startP} ${token}</b>`,
99+
].join('<br/>');
100+
},
101+
},
102+
103+
legend: {
104+
top: 0,
105+
right: 10,
106+
textStyle: { color: '#aaa', fontSize: 12 },
107+
icon: 'roundRect',
108+
},
109+
xAxis: {
110+
type: 'category',
111+
boundaryGap: false,
112+
data: data.map((d) => `Cycle ${d.cycle}`),
113+
axisLine: { lineStyle: { color: '#666' } },
114+
axisLabel: {
115+
color: '#aaa',
116+
fontSize: 10,
117+
hideOverlap: true,
118+
},
119+
axisTick: { alignWithLabel: true },
120+
},
121+
122+
yAxis: {
123+
type: 'value',
124+
min: 0,
125+
max: yMax,
126+
interval: step,
127+
axisLine: { lineStyle: { color: '#666' } },
128+
axisLabel: {
129+
color: '#aaa',
130+
fontSize: 10,
131+
formatter: (val: number) => `${val.toFixed(2)} ${token}`,
132+
},
133+
splitLine: { lineStyle: { type: 'dashed', color: 'rgba(136,136,136,0.25)' } },
134+
},
135+
series: [
136+
{
137+
name: 'Sellout price',
138+
type: 'line',
139+
smooth: false,
140+
showSymbol: true,
141+
symbolSize: 6,
142+
lineStyle: { width: 2, color: '#eab308' },
143+
itemStyle: { color: '#eab308' },
144+
data: data.map((d) => Number(fmt(d.price))),
145+
},
146+
{
147+
name: 'Start price',
148+
type: 'line',
149+
smooth: false,
150+
showSymbol: false,
151+
lineStyle: { width: 2, type: 'dashed', color: '#0cc184' },
152+
itemStyle: { color: '#0cc184' },
153+
data: data.map((d) => Number(fmt(d.startPrice))),
154+
},
155+
],
156+
};
157+
}, [data, network, token]);
158+
7159
return (
8160
<div className={styles.card}>
9161
<div className={styles.header}>
10162
<span className={styles.title}>Last 3 Sale Cycle Prices</span>
11163
</div>
12-
<div className={styles.placeholder}>
13-
Historical pricing data is currently unavailable. This feature requires an indexer service.
14-
</div>
164+
165+
{loading ? (
166+
<div className={styles.placeholder}>Loading data...</div>
167+
) : !data.length || !option ? (
168+
<div className={styles.placeholder}>No data found.</div>
169+
) : (
170+
<ReactECharts option={option} style={{ height: 330, width: '100%' }} />
171+
)}
15172
</div>
16173
);
17174
}

src/components/Home/GeneralAnalytics/BulkSaleSummary/index.tsx

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,18 @@
1-
import { useEffect } from 'react';
21
import { useUnit } from 'effector-react';
32
import styles from './BulkSaleSummary.module.scss';
43

54
import { $network } from '@/api/connection';
6-
import { $latestSaleInfo } from '@/coretime/saleInfo';
7-
import {
8-
$purchaseHistory,
9-
purchaseHistoryRequested,
10-
PurchaseType,
11-
} from '@/coretime/purchaseHistory';
12-
import { toUnitFormatted } from '@/utils';
5+
import { $purchaseHistory, PurchaseType } from '@/coretime/purchaseHistory';
136
import RevenueBox from '../RevenueBox/index';
147
import CurrentAuctionPrice from '../CurrentAuctionPrice';
158
import UserBalance from '../UserBalance';
169
import AuctionPriceOverview from '../AuctionPriceOverview';
1710
import TopBuyerCard from '../TopBuyerCard';
1811

1912
export default function BulkSaleSummary() {
20-
const [network, saleInfo, purchaseHistory] = useUnit([
21-
$network,
22-
$latestSaleInfo,
23-
$purchaseHistory,
24-
]);
25-
26-
useEffect(() => {
27-
if (network && saleInfo) {
28-
purchaseHistoryRequested({ network, saleCycle: saleInfo.saleCycle });
29-
}
30-
}, [network, saleInfo]);
13+
const [network, purchaseHistory] = useUnit([$network, $purchaseHistory]);
14+
15+
// purchaseHistory is fetched globally in _app.tsx.
3116

3217
const bulkRevenue = purchaseHistory
3318
.filter((item) => item.type === PurchaseType.BULK)

src/components/Home/HomeDashboard/CoreComparison/index.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { useEffect, useState } from 'react';
44
import { useUnit } from 'effector-react';
55
import { $latestSaleInfo, fetchSelloutPrice } from '@/coretime/saleInfo';
66
import { $network, $connections } from '@/api/connection';
7-
import { purchaseHistoryRequested } from '@/coretime/purchaseHistory';
87
import { getCorePriceAt, toUnitFormatted } from '@/utils';
98
import styles from './CoreComparison.module.scss';
109
import { getNetworkChainIds, getNetworkMetadata } from '@/network';
@@ -21,8 +20,6 @@ export default function CoreComparison({ view }: Props) {
2120

2221
useEffect(() => {
2322
if (network && saleInfo) {
24-
purchaseHistoryRequested({ network, saleCycle: saleInfo.saleCycle });
25-
2623
(async () => {
2724
const networkChainIds = getNetworkChainIds(network);
2825
if (!networkChainIds) return null;

src/components/Home/HomeDashboard/CorePurchaseCard/index.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import { useEffect, useState } from 'react';
44
import { useUnit } from 'effector-react';
55
import { $latestSaleInfo, fetchCoresSold, getCurrentPhase, SalePhase } from '@/coretime/saleInfo';
6-
import { purchaseHistoryRequested } from '@/coretime/purchaseHistory';
76
import { $connections, $network } from '@/api/connection';
87
import { getCorePriceAt, SOLD_OUT_MESSAGE, toUnitFormatted } from '@/utils';
98
import styles from './CorePurchaseCard.module.scss';
@@ -51,8 +50,6 @@ export default function CorePurchaseCard({ view }: Props) {
5150
let isMounted = true;
5251

5352
(async () => {
54-
purchaseHistoryRequested({ network, saleCycle: saleInfo.saleCycle });
55-
5653
const currentBlockNumber = await relayClient
5754
.getTypedApi(metadata.relayChain)
5855
.query.System.Number.getValue();

src/components/Home/HomeDashboard/CoreRemainingCard/index.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import 'chartjs-adapter-date-fns';
1313
import { useUnit } from 'effector-react';
1414
import { $connections, $network } from '@/api/connection';
1515
import { $latestSaleInfo, fetchCoresSold } from '@/coretime/saleInfo';
16-
import { $purchaseHistory, purchaseHistoryRequested } from '@/coretime/purchaseHistory';
16+
import { $purchaseHistory } from '@/coretime/purchaseHistory';
1717
import { getNetworkChainIds } from '@/network';
1818
import { coretimeChainBlockTime } from '@/utils/index';
1919

@@ -52,7 +52,6 @@ export default function CoreRemainingCard({ view }: Props) {
5252
};
5353

5454
fetchData();
55-
purchaseHistoryRequested({ network, saleCycle: saleInfo.saleCycle });
5655
}, [connections, network, saleInfo]);
5756

5857
const chartData = useMemo(() => {

src/components/Home/HomeDashboard/DashboardHeader/OwnedRegionsModal/index.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { useEffect, useState } from 'react';
44
import { useUnit } from 'effector-react';
55
import { $regions } from '@/coretime/regions';
6-
import { $latestSaleInfo, latestSaleRequested } from '@/coretime/saleInfo';
6+
import { $latestSaleInfo } from '@/coretime/saleInfo';
77
import { $connections, $network } from '@/api/connection';
88
import { $selectedAccount } from '@/wallet';
99
import { bitStringToUint8Array, maskToBin, timesliceToTimestamp } from '@/utils';
@@ -41,9 +41,7 @@ export default function OwnedRegionsModal({ isOpen, onClose }: Props) {
4141
$selectedAccount,
4242
]);
4343

44-
useEffect(() => {
45-
latestSaleRequested(network);
46-
}, [network]);
44+
// latestSaleInfo is fetched globally in _app.tsx.
4745

4846
useEffect(() => {
4947
const loadDates = async () => {

src/components/Home/HomeDashboard/PurchaseHistoryTable/index.tsx

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,7 @@ import { useUnit } from 'effector-react';
55
import { TableComponent } from '../../../elements/TableComponent';
66
import styles from './PurchaseHistory.module.scss';
77
import { $latestSaleInfo } from '@/coretime/saleInfo';
8-
import {
9-
$purchaseHistory,
10-
purchaseHistoryRequested,
11-
PurchaseHistoryItem,
12-
} from '@/coretime/purchaseHistory';
8+
import { $purchaseHistory, PurchaseHistoryItem } from '@/coretime/purchaseHistory';
139
import { $network } from '@/api/connection';
1410
import { toUnitFormatted } from '@/utils';
1511

@@ -49,14 +45,7 @@ export default function PurchaseHistoryTable() {
4945
const purchaseHistory = useUnit($purchaseHistory);
5046
const [tableData, setTableData] = useState<Array<Record<string, TableData>>>([]);
5147

52-
useEffect(() => {
53-
if (network && latestSaleInfo) {
54-
purchaseHistoryRequested({
55-
network,
56-
saleCycle: latestSaleInfo.saleCycle,
57-
});
58-
}
59-
}, [network, latestSaleInfo]);
48+
// purchaseHistory is fetched globally in _app.tsx.
6049

6150
useEffect(() => {
6251
if (!network) return;

src/components/Home/HomeDashboard/UpcomingRenewalsTable/index.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useUnit } from 'effector-react';
66
import { $network, $connections } from '@/api/connection';
77
import { $parachains, parachainsRequested } from '@/parachains';
88
import { $potentialRenewals, potentialRenewalsRequested } from '@/coretime/renewals';
9-
import { $latestSaleInfo, latestSaleRequested } from '@/coretime/saleInfo';
9+
import { $latestSaleInfo } from '@/coretime/saleInfo';
1010
import { chainData } from '@/chaindata';
1111
import { TableComponent } from '@/components/elements/TableComponent';
1212
import { TableData } from '@/types/type';
@@ -24,7 +24,6 @@ const UpcomingRenewalsTable = () => {
2424
useEffect(() => {
2525
parachainsRequested(network);
2626
potentialRenewalsRequested({ network, connections });
27-
latestSaleRequested(network);
2827
}, [network, connections]);
2928

3029
useEffect(() => {

0 commit comments

Comments
 (0)