Skip to content

Commit 708ee7e

Browse files
authored
Merge pull request #425 from skalenetwork/add-validator-management-page
Add validator management page
2 parents a618f3c + b75f2d7 commit 708ee7e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1975
-1801
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "portal",
33
"private": true,
4-
"version": "3.1.0",
4+
"version": "3.2.0",
55
"type": "module",
66
"scripts": {
77
"build:testnet": "NETWORK_NAME=testnet bash build.sh",

packages/core/bun.lockb

-2.87 KB
Binary file not shown.

packages/core/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
},
1212
"author": "SKALE Labs",
1313
"devDependencies": {
14-
"ethers": "*.*.*",
1514
"typescript": "^4.9.5"
1615
}
1716
}

packages/core/src/types/staking/Delegation.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,22 @@ export interface IDelegationInfo {
6969
delegationId: bigint
7070
delegationType: DelegationType
7171
}
72+
73+
export interface IDelegationTotals {
74+
proposed: {
75+
count: number
76+
amount: bigint
77+
}
78+
accepted: {
79+
count: number
80+
amount: bigint
81+
}
82+
delegated: {
83+
count: number
84+
amount: bigint
85+
}
86+
completed: {
87+
count: number
88+
amount: bigint
89+
}
90+
}

src/App.scss

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ body {
7676
width: 100%;
7777
}
7878

79+
.fullH {
80+
height: 100%;
81+
}
82+
7983
.mp__btnConnect {
8084
position: relative;
8185

@@ -449,12 +453,8 @@ body::-webkit-scrollbar {
449453
box-shadow: none !important;
450454
}
451455

452-
.btnerror {
453-
background: #f4433621;
454-
}
455-
456-
.btnwarning {
457-
background: rgb(244 139 54 / 13%);
456+
.btnDisabled {
457+
background: #262626;
458458
}
459459

460460
.btnSmLoading {
@@ -543,6 +543,12 @@ body::-webkit-scrollbar {
543543
}
544544
}
545545

546+
.MuiToggleButton-root {
547+
border-radius: 25px !important;
548+
padding: 4px 10px;
549+
border: none !important;
550+
}
551+
546552
.copyBoard {
547553
margin: 10px 0 !important;
548554
padding: 13pt 15pt !important;
@@ -791,6 +797,22 @@ input[type=number] {
791797
}
792798
}
793799

800+
.chipNotification {
801+
background: #e94e4e;
802+
border-radius: 20px;
803+
width: 20px;
804+
height: 20px;
805+
text-align: center;
806+
display: flex;
807+
align-items: center;
808+
justify-content: center;
809+
810+
p {
811+
color: #000000de !important;
812+
font-weight: 600 !important;
813+
}
814+
}
815+
794816
.chipTrending {
795817
background: linear-gradient(180deg, #e56d36, #D0602D) !important;
796818

@@ -825,12 +847,13 @@ input[type=number] {
825847
}
826848

827849
.chipXs {
828-
border-radius: 20px;
829-
padding: 3px 6px;
850+
border-radius: 15px;
851+
padding: 6px 8px;
852+
text-align: center;
830853

831854
svg {
832-
width: 14px;
833-
height: 14px;
855+
width: 12px;
856+
height: 12px;
834857
}
835858
}
836859

@@ -844,16 +867,6 @@ input[type=number] {
844867
}
845868
}
846869

847-
.chipXs {
848-
border-radius: 15px;
849-
padding: 4px 6px;
850-
851-
svg {
852-
width: 12px;
853-
height: 12px;
854-
}
855-
}
856-
857870
.skChip {
858871
background: linear-gradient(180deg, rgb(52 52 52), rgb(31 31 31));
859872
}
@@ -885,6 +898,11 @@ input[type=number] {
885898
color: #3cda94;
886899
}
887900

901+
.chip_REWARDS {
902+
background: linear-gradient(180deg, #3d390f, #2a230a);
903+
color: #dac83c;
904+
}
905+
888906
.chip_ACCEPTED {
889907
background: linear-gradient(180deg, #233d0f, #0a1b07);
890908
color: #3cda4e;
@@ -1149,7 +1167,6 @@ input[type=number] {
11491167
}
11501168
}
11511169

1152-
11531170
.trustedBadge {
11541171
color: #0095f6;
11551172
}
@@ -1158,12 +1175,6 @@ input[type=number] {
11581175
color: #ffb817;
11591176
}
11601177

1161-
.validatorCard {
1162-
height: 100% !important;
1163-
cursor: pointer;
1164-
}
1165-
1166-
11671178
.pOneLine {
11681179
overflow: hidden;
11691180
white-space: nowrap;
@@ -1426,7 +1437,21 @@ input[type=number] {
14261437
display: none;
14271438
}
14281439

1440+
.opacity0 {
1441+
opacity: 0;
1442+
}
1443+
14291444
.MuiTooltip-tooltip {
14301445
font-size: 0.8rem !important;
14311446
padding: 8px 12px !important;
1447+
}
1448+
1449+
.delegationFlowText {
1450+
border-top: 2px #4a4a4a solid;
1451+
margin: 0 10px;
1452+
padding: 2px 5px 0 5px;
1453+
}
1454+
1455+
.delegationFlowIcon {
1456+
margin-top: -20px;
14321457
}

src/Portal.tsx

Lines changed: 119 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,143 @@
2121
* @copyright SKALE Labs 2023-Present
2222
*/
2323

24+
import { useState, useEffect } from 'react'
25+
import { types } from '@/core'
26+
2427
import Box from '@mui/material/Box'
2528
import CssBaseline from '@mui/material/CssBaseline'
26-
import { useMetaportStore, useWagmiAccount, Debug, cls, cmn } from '@skalenetwork/metaport'
29+
import {
30+
useMetaportStore,
31+
useWagmiAccount,
32+
Debug,
33+
cls,
34+
cmn,
35+
PROXY_ENDPOINTS
36+
} from '@skalenetwork/metaport'
2737

2838
import Header from './Header'
2939
import SkDrawer from './SkDrawer'
3040
import Router from './Router'
3141
import SkBottomNavigation from './SkBottomNavigation'
3242
import ProfileModal from './components/profile/ProfileModal'
3343

44+
import { formatSChains } from './core/chain'
45+
import { STATS_API } from './core/constants'
46+
import { getValidatorDelegations } from './core/delegation/staking'
47+
import { getValidator } from './core/delegation'
48+
import { initContracts } from './core/contracts'
49+
3450
export default function Portal() {
3551
const mpc = useMetaportStore((state) => state.mpc)
52+
53+
const [schains, setSchains] = useState<types.ISChain[]>([])
54+
const [metrics, setMetrics] = useState<types.IMetrics | null>(null)
55+
const [stats, setStats] = useState<types.IStats | null>(null)
56+
const [validator, setValidator] = useState<types.staking.IValidator | null | undefined>(null)
57+
const [validatorDelegations, setValidatorDelegations] = useState<
58+
types.staking.IDelegation[] | null
59+
>(null)
60+
const [customAddress, setCustomAddress] = useState<types.AddressType | undefined>(undefined)
61+
const [sc, setSc] = useState<types.staking.ISkaleContractsMap | null>(null)
62+
const [loadCalled, setLoadCalled] = useState<boolean>(false)
63+
64+
const endpoint = PROXY_ENDPOINTS[mpc.config.skaleNetwork]
65+
const statsApi = STATS_API[mpc.config.skaleNetwork]
66+
3667
const { address } = useWagmiAccount()
3768
if (!mpc) return <div></div>
69+
70+
useEffect(() => {
71+
initSkaleContracts()
72+
loadData()
73+
}, [])
74+
75+
useEffect(() => {
76+
loadValidator()
77+
}, [address, customAddress, sc])
78+
79+
async function initSkaleContracts() {
80+
setLoadCalled(true)
81+
if (loadCalled) return
82+
setSc(await initContracts(mpc))
83+
}
84+
85+
async function loadChains() {
86+
try {
87+
const response = await fetch(`https://${endpoint}/files/chains.json`)
88+
const chainsJson = await response.json()
89+
setSchains(formatSChains(chainsJson))
90+
} catch (e) {
91+
console.log('Failed to load chains')
92+
console.error(e)
93+
}
94+
}
95+
96+
async function loadMetrics() {
97+
try {
98+
const response = await fetch(`https://${endpoint}/files/metrics.json`)
99+
const metricsJson = await response.json()
100+
setMetrics(metricsJson)
101+
} catch (e) {
102+
console.log('Failed to load metrics')
103+
console.error(e)
104+
}
105+
}
106+
107+
async function loadStats() {
108+
if (statsApi === null) return
109+
try {
110+
const response = await fetch(statsApi)
111+
const statsResp = await response.json()
112+
setStats(statsResp.payload)
113+
} catch (e) {
114+
console.log('Failed to load stats')
115+
console.error(e)
116+
}
117+
}
118+
119+
async function loadValidator() {
120+
const addr = customAddress ?? address
121+
if (!sc || !addr) {
122+
setValidator(null)
123+
setValidatorDelegations(null)
124+
return
125+
}
126+
const validatorData = await getValidator(sc.validatorService, addr)
127+
setValidator(validatorData)
128+
if (validatorData && validatorData.id) {
129+
setValidatorDelegations(await getValidatorDelegations(sc, validatorData.id))
130+
} else {
131+
setValidator(undefined)
132+
setValidatorDelegations(null)
133+
}
134+
}
135+
136+
async function loadData() {
137+
loadChains()
138+
loadMetrics()
139+
loadStats()
140+
loadValidator()
141+
}
142+
38143
return (
39144
<Box sx={{ display: 'flex' }} className="AppWrap">
40145
<CssBaseline />
41146
<Header address={address} mpc={mpc} />
42-
<SkDrawer />
147+
<SkDrawer validatorDelegations={validatorDelegations} />
43148
<div className={cls(cmn.fullWidth)} id="appContentScroll">
44-
<Router />
149+
<Router
150+
loadData={loadData}
151+
schains={schains}
152+
metrics={metrics}
153+
stats={stats}
154+
validator={validator}
155+
validatorDelegations={validatorDelegations}
156+
customAddress={customAddress}
157+
setCustomAddress={setCustomAddress}
158+
sc={sc}
159+
loadValidator={loadValidator}
160+
/>
45161
<ProfileModal />
46162
<div className={cls(cmn.mtop20, cmn.fullWidth)}>
47163
<Debug />

0 commit comments

Comments
 (0)