diff --git a/src/@services/RestakingDashboardService.js b/src/@services/RestakingDashboardService.js
new file mode 100644
index 00000000..0bb26240
--- /dev/null
+++ b/src/@services/RestakingDashboardService.js
@@ -0,0 +1,25 @@
+import BaseService from './BaseService';
+
+export default class RestakingDashboardService extends BaseService {
+ async getPromotedOperators() {
+ const response = await BaseService._get('/rd/promotion/operators');
+
+ if (response.ok) {
+ return await response.json();
+ }
+
+ throw await this._createError(response);
+ }
+
+ async getAVSPromotedOperators(address) {
+ const response = await BaseService._get(
+ `/rd/promotion/avs/${address}/operators`
+ );
+
+ if (response.ok) {
+ return await response.json();
+ }
+
+ throw await this._createError(response);
+ }
+}
diff --git a/src/@services/ServiceContext.jsx b/src/@services/ServiceContext.jsx
index b60c5efc..fc2d3089 100644
--- a/src/@services/ServiceContext.jsx
+++ b/src/@services/ServiceContext.jsx
@@ -3,6 +3,7 @@ import AVSService from './AVSService';
import EigenLayerService from './EigenLayerService';
import LRTService from './LRTService';
import OperatorService from './OperatorService';
+import RestakingDashboardService from './RestakingDashboardService';
export const ServiceContext = createContext();
@@ -21,5 +22,6 @@ const services = {
avsService: new AVSService(),
lrtService: new LRTService(),
operatorService: new OperatorService(),
- eigenlayerService: new EigenLayerService()
+ eigenlayerService: new EigenLayerService(),
+ rdService: new RestakingDashboardService()
};
diff --git a/src/avs/AVSDetailsOperatorsTab.jsx b/src/avs/AVSDetailsOperatorsTab.jsx
index 675dd823..e809bd1b 100644
--- a/src/avs/AVSDetailsOperatorsTab.jsx
+++ b/src/avs/AVSDetailsOperatorsTab.jsx
@@ -105,11 +105,14 @@ export default function AVSDetailsOperatorsTab({
function AVSOperatorsList({ address, avsError, isAVSLoading, tvl }) {
const [searchParams, setSearchParams] = useSearchParams();
const navigate = useNavigate();
+ const { rdService } = useServices();
const [state, dispatch] = useMutativeReducer(reduceState, {
currentRate: 1,
error: undefined,
operators: [],
+ promotedOperators: [],
+ promotedOperatorsRate: 1,
isInputTouched: false,
isTableLoading: true,
totalPages: undefined,
@@ -210,6 +213,20 @@ function AVSOperatorsList({ address, avsError, isAVSLoading, tvl }) {
state.sort
]);
+ useEffect(() => {
+ (async () => {
+ try {
+ const response = await rdService.getAVSPromotedOperators(address);
+ dispatch({
+ promotedOperators: response.results,
+ promotedOperatorsRate: response.rate
+ });
+ } catch (e) {
+ // swallow error because we don't need to show error message
+ }
+ })();
+ }, [address, dispatch, rdService]);
+
const handleSort = useCallback(
e => {
let sort = e.column;
@@ -234,6 +251,48 @@ function AVSOperatorsList({ address, avsError, isAVSLoading, tvl }) {
[dispatch]
);
+ const renderPromotedOperators = useCallback(() => {
+ return state.promotedOperators?.map((operator, i) => (
+
+ navigate(`/operators/${operator.address}`, {
+ state: { operator }
+ })
+ }
+ >
+
+
+
+ ad
+
+
+
+ {operator.metadata?.name || operator.address}
+
+
+
+
+ {tvl === 0
+ ? 'N/A'
+ : `${((operator.strategiesTotal / tvl) * 100).toFixed(2)}%`}
+
+
+
+ {formatUSD(operator.strategiesTotal * state.promotedOperatorsRate)}
+
+
+ {formatETH(operator.strategiesTotal)}
+
+
+
+ ));
+ }, [navigate, state.promotedOperators, state.promotedOperatorsRate, tvl]);
+
return (
@@ -301,15 +360,9 @@ function AVSOperatorsList({ address, avsError, isAVSLoading, tvl }) {
TVL
-
-
- No result found for {truncate(state.search ?? '')}
-
-
- }
- >
+
+ {searchParams.get('page') === '1' && renderPromotedOperators()}
+
{(state.isTableLoading || isAVSLoading) &&
[...new Array(10)].map((_, i) => (
@@ -372,6 +425,22 @@ function AVSOperatorsList({ address, avsError, isAVSLoading, tvl }) {
))}
+ {!state.isTableLoading &&
+ !isAVSLoading &&
+ state.operators.length === 0 && (
+
+
+
+
+ No result found for {truncate(state.search ?? '')}
+
+
+
+
+
+
+ )}
+
{!state.isTableLoading &&
!isAVSLoading &&
state.operators.length > 0 &&
diff --git a/src/operators/OperatorList.jsx b/src/operators/OperatorList.jsx
index 82dcc45b..11d1bfa4 100644
--- a/src/operators/OperatorList.jsx
+++ b/src/operators/OperatorList.jsx
@@ -40,12 +40,14 @@ const columns = [
];
export default function OperatorList() {
- const { operatorService } = useServices();
+ const { operatorService, rdService } = useServices();
const [searchParams, setSearchParams] = useSearchParams();
const navigate = useNavigate();
const abortController = useRef(null);
const [state, dispatch] = useMutativeReducer(reduceState, {
operators: [],
+ promotedOperators: [],
+ promotedOperatorsRate: 1,
isFetchingData: false,
searchTerm: searchParams.get('search'),
error: null,
@@ -100,6 +102,20 @@ export default function OperatorList() {
[operatorService, dispatch]
);
+ useEffect(() => {
+ (async () => {
+ try {
+ const response = await rdService.getPromotedOperators();
+ dispatch({
+ promotedOperators: response.results,
+ promotedOperatorsRate: response.rate
+ });
+ } catch (e) {
+ // swallow error because we don't need to show error message
+ }
+ })();
+ }, [dispatch, rdService]);
+
const handlePageClick = useCallback(
page => {
setSearchParams({ page });
@@ -145,6 +161,46 @@ export default function OperatorList() {
}
}, [dispatch, debouncedSearchTerm]);
+ const renderPromotedOperators = useCallback(() => {
+ return state.promotedOperators?.map((operator, i) => (
+
+ navigate(`/operators/${operator.address}`, {
+ state: { operator }
+ })
+ }
+ >
+
+
+
+ ad
+
+
+
+ {operator.metadata?.name || operator.address}
+
+
+
+
+ {formatNumber(operator.stakerCount)}
+
+
+
+ {formatUSD(operator.strategiesTotal * state.promotedOperatorsRate)}
+
+
+ {formatETH(operator.strategiesTotal)}
+
+
+
+ ));
+ }, [navigate, state.promotedOperators, state.promotedOperatorsRate]);
+
return (
@@ -195,7 +251,11 @@ export default function OperatorList() {
table: state.operators?.length === 0 ? 'h-full' : null,
thead: '[&>tr:last-child]:hidden'
}}
- hideHeader={!state.isFetchingData && state.operators.length == 0}
+ hideHeader={
+ state.promotedOperators.length === 0 &&
+ !state.isFetchingData &&
+ state.operators.length == 0
+ }
layout="fixed"
onSortChange={e => dispatch({ sortDescriptor: e })}
removeWrapper
@@ -212,19 +272,9 @@ export default function OperatorList() {
)}
-
-
- No operator found for "
- {debouncedSearchTerm.length > 42
- ? `${debouncedSearchTerm.substring(0, 42)}...`
- : debouncedSearchTerm}
- "
-
-
- }
- >
+
+ {searchParams.get('page') === '1' && renderPromotedOperators()}
+
{state.isFetchingData
? [...Array(10)].map((_, i) => (
@@ -276,6 +326,24 @@ export default function OperatorList() {
))}
+
+ {!state.isFetchingData && state.operators.length === 0 && (
+
+
+
+
+ No operator found for "
+ {debouncedSearchTerm.length > 42
+ ? `${debouncedSearchTerm.substring(0, 42)}...`
+ : debouncedSearchTerm}
+ "
+
+
+
+
+
+
+ )}
{state.totalPages > 1 && (