diff --git a/packages/apps/human-app/frontend/src/main.tsx b/packages/apps/human-app/frontend/src/main.tsx
index bf9aaa190f..ccf792a2b6 100644
--- a/packages/apps/human-app/frontend/src/main.tsx
+++ b/packages/apps/human-app/frontend/src/main.tsx
@@ -23,7 +23,6 @@ import { HomePageStateProvider } from '@/shared/contexts/homepage-state';
import { NotificationProvider } from '@/shared/providers/notifications-provider';
import { ModalProvider } from './shared/contexts/modal-context';
import { GlobalModal } from './shared/components/ui/modal/global-modal';
-import { UiConfigProvider } from './shared/providers/ui-config-provider';
const root = document.getElementById('root');
if (!root) throw Error('root element is undefined');
@@ -40,30 +39,25 @@ createRoot(root).render(
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/apps/human-app/frontend/src/modules/auth/context/auth-context.tsx b/packages/apps/human-app/frontend/src/modules/auth/context/auth-context.tsx
index 13b9001fed..2b2478b5f4 100644
--- a/packages/apps/human-app/frontend/src/modules/auth/context/auth-context.tsx
+++ b/packages/apps/human-app/frontend/src/modules/auth/context/auth-context.tsx
@@ -11,7 +11,6 @@ const userDataSchema = z.object({
user_id: z.number(),
reputation_network: z.string(),
exp: z.number(),
- is_stake_eligible: z.boolean(),
});
export type UserData = z.infer;
diff --git a/packages/apps/human-app/frontend/src/modules/worker/hooks/use-exchange-api-keys.ts b/packages/apps/human-app/frontend/src/modules/worker/hooks/use-exchange-api-keys.ts
deleted file mode 100644
index c958791ca7..0000000000
--- a/packages/apps/human-app/frontend/src/modules/worker/hooks/use-exchange-api-keys.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
-import {
- deleteExchangeApiKeys,
- enrollExchangeApiKeys,
- getExchangeApiKeys,
- getSupportedExchanges,
-} from '../services/exchangeApiKeys.service';
-
-function useGetSupportedExchanges() {
- return useQuery({
- queryKey: ['supported-exchanges'],
- queryFn: () => getSupportedExchanges(),
- });
-}
-
-function useGetExchangeApiKeys() {
- return useQuery({
- queryKey: ['exchange-api-keys'],
- queryFn: () => getExchangeApiKeys(),
- });
-}
-
-function useEnrollExchangeApiKeys() {
- const queryClient = useQueryClient();
-
- return useMutation({
- mutationKey: ['enroll-exchange-api-keys'],
- mutationFn: (data: {
- exchange: string;
- apiKey: string;
- secretKey: string;
- }) => enrollExchangeApiKeys(data),
- onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: ['exchange-api-keys'] });
- queryClient.invalidateQueries({ queryKey: ['staking-summary'] });
- },
- });
-}
-
-function useDeleteExchangeApiKeys() {
- const queryClient = useQueryClient();
-
- return useMutation({
- mutationKey: ['delete-exchange-api-keys'],
- mutationFn: () => deleteExchangeApiKeys(),
- onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: ['exchange-api-keys'] });
- queryClient.invalidateQueries({ queryKey: ['staking-summary'] });
- },
- });
-}
-
-export {
- useGetSupportedExchanges,
- useDeleteExchangeApiKeys,
- useGetExchangeApiKeys,
- useEnrollExchangeApiKeys,
-};
diff --git a/packages/apps/human-app/frontend/src/modules/worker/hooks/use-staking.ts b/packages/apps/human-app/frontend/src/modules/worker/hooks/use-staking.ts
deleted file mode 100644
index 1556c072d6..0000000000
--- a/packages/apps/human-app/frontend/src/modules/worker/hooks/use-staking.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { useQuery } from '@tanstack/react-query';
-import { getStakingSummary } from '../services/staking.service';
-
-function useGetStakingSummary() {
- return useQuery({
- queryKey: ['staking-summary'],
- queryFn: () => getStakingSummary(),
- });
-}
-
-export { useGetStakingSummary };
diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs/available-jobs/hooks/use-available-jobs-filter-modal.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs/available-jobs/hooks/use-available-jobs-filter-modal.tsx
index 6ce55c5c48..f90e919961 100644
--- a/packages/apps/human-app/frontend/src/modules/worker/jobs/available-jobs/hooks/use-available-jobs-filter-modal.tsx
+++ b/packages/apps/human-app/frontend/src/modules/worker/jobs/available-jobs/hooks/use-available-jobs-filter-modal.tsx
@@ -1,17 +1,17 @@
import { useModal } from '@/shared/contexts/modal-context';
-import { useUiConfig } from '@/shared/providers/ui-config-provider';
+import { useGetUiConfig } from '@/shared/hooks';
import { AvailableJobsFilterModal } from '../available-jobs-filter-modal';
export function useAvailableJobsFilterModal() {
const { openModal, closeModal } = useModal();
- const { uiConfig } = useUiConfig();
+ const { data: uiConfigData } = useGetUiConfig();
return {
openModal: () => {
openModal({
content: (
),
diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs/jobs.page.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs/jobs.page.tsx
index 21364ccedf..5e65b627ae 100644
--- a/packages/apps/human-app/frontend/src/modules/worker/jobs/jobs.page.tsx
+++ b/packages/apps/human-app/frontend/src/modules/worker/jobs/jobs.page.tsx
@@ -7,7 +7,7 @@ import { useIsMobile } from '@/shared/hooks/use-is-mobile';
import { useColorMode } from '@/shared/contexts/color-mode';
import { NoRecords } from '@/shared/components/ui/no-records';
import { PageCardLoader } from '@/shared/components/ui/page-card';
-import { useUiConfig } from '@/shared/providers/ui-config-provider';
+import { useGetUiConfig } from '@/shared/hooks';
import { useGetOracles } from '../hooks';
import { useGetOraclesNotifications } from '../hooks/use-get-oracles-notifications';
import { TabPanel } from './components';
@@ -30,15 +30,19 @@ export function JobsPage() {
error,
} = useGetOracles();
- const { uiConfig, isUiConfigLoading, isUiConfigError } = useUiConfig();
+ const {
+ data: uiConfigData,
+ isPending: isPendingUiConfig,
+ isError: isErrorUiConfig,
+ } = useGetUiConfig();
const { address: oracle_address } = useParams<{ address: string }>();
const { t } = useTranslation();
const [activeTab, setActiveTab] = useState(0);
const isMobile = useIsMobile();
- const isError = isErrorGetOracles || isUiConfigError;
- const isPending = isPendingGetOracles || isUiConfigLoading;
+ const isError = isErrorGetOracles || isErrorUiConfig;
+ const isPending = isPendingGetOracles || isPendingUiConfig;
const { onError } = useGetOraclesNotifications();
const handleTabChange = (_event: React.SyntheticEvent, newValue: number) => {
@@ -115,7 +119,7 @@ export function JobsPage() {
) : (
)}
@@ -124,7 +128,7 @@ export function JobsPage() {
) : (
)}
diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs/my-jobs/hooks/use-my-jobs-filter-modal.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs/my-jobs/hooks/use-my-jobs-filter-modal.tsx
index a41ca0c491..40a0c9c1be 100644
--- a/packages/apps/human-app/frontend/src/modules/worker/jobs/my-jobs/hooks/use-my-jobs-filter-modal.tsx
+++ b/packages/apps/human-app/frontend/src/modules/worker/jobs/my-jobs/hooks/use-my-jobs-filter-modal.tsx
@@ -1,17 +1,17 @@
import { useModal } from '@/shared/contexts/modal-context';
+import { useGetUiConfig } from '@/shared/hooks';
import { MyJobsFilterModal } from '../components/mobile/my-jobs-filter-modal';
-import { useUiConfig } from '@/shared/providers/ui-config-provider';
export function useMyJobFilterModal() {
const { openModal, closeModal } = useModal();
- const { uiConfig } = useUiConfig();
+ const { data: uiConfigData } = useGetUiConfig();
return {
openModal: () => {
openModal({
content: (
),
diff --git a/packages/apps/human-app/frontend/src/modules/worker/profile/components/add-api-key-modal.tsx b/packages/apps/human-app/frontend/src/modules/worker/profile/components/add-api-key-modal.tsx
deleted file mode 100644
index f3b1ee6954..0000000000
--- a/packages/apps/human-app/frontend/src/modules/worker/profile/components/add-api-key-modal.tsx
+++ /dev/null
@@ -1,252 +0,0 @@
-import { zodResolver } from '@hookform/resolvers/zod';
-import {
- Autocomplete,
- Box,
- FormControl,
- FormHelperText,
- Stack,
- TextField,
- Typography,
-} from '@mui/material';
-import { Controller, useForm } from 'react-hook-form';
-import { useTranslation } from 'react-i18next';
-import { z } from 'zod';
-import { Button } from '@/shared/components/ui/button';
-import { useIsMobile } from '@/shared/hooks';
-import {
- useEnrollExchangeApiKeys,
- useGetSupportedExchanges,
-} from '../../hooks/use-exchange-api-keys';
-import { ModalError, ModalLoading, ModalSuccess } from './modal-states';
-import { useEffect } from 'react';
-
-interface AddApiKeyModalProps {
- onClose: () => void;
- disableClose: (disable: boolean) => void;
-}
-
-export function AddApiKeyModal({ onClose, disableClose }: AddApiKeyModalProps) {
- const { t } = useTranslation();
- const {
- mutate: enrollExchangeApiKey,
- reset: resetMutation,
- error,
- isSuccess,
- isPending,
- isError,
- isIdle,
- } = useEnrollExchangeApiKeys();
- const isMobile = useIsMobile();
- const { data: supportedExchanges } = useGetSupportedExchanges();
-
- useEffect(() => {
- disableClose(isPending);
- }, [isPending, disableClose]);
-
- const {
- control,
- handleSubmit,
- formState: { errors },
- reset,
- } = useForm({
- defaultValues: {
- exchange: '',
- apiKey: '',
- secretKey: '',
- },
- resolver: zodResolver(
- z.object({
- exchange: z.string().min(1, t('validation.required')),
- apiKey: z.string().trim().min(1, t('validation.required')),
- secretKey: z.string().trim().min(1, t('validation.required')),
- })
- ),
- });
-
- useEffect(() => {
- return () => {
- reset();
- resetMutation();
- };
- }, [reset, resetMutation]);
-
- const onSubmit = (data: {
- exchange: string;
- apiKey: string;
- secretKey: string;
- }) => {
- enrollExchangeApiKey(data);
- };
-
- return (
-
- );
-}
diff --git a/packages/apps/human-app/frontend/src/modules/worker/profile/components/api-key-data.tsx b/packages/apps/human-app/frontend/src/modules/worker/profile/components/api-key-data.tsx
deleted file mode 100644
index a6fa175b58..0000000000
--- a/packages/apps/human-app/frontend/src/modules/worker/profile/components/api-key-data.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-import { IconButton, Stack, Typography } from '@mui/material';
-import { useTranslation } from 'react-i18next';
-import { Chip } from '@/shared/components/ui/chip';
-import { CustomTextField, CustomTextFieldDark } from './custom-text-field';
-import { useColorMode } from '@/shared/contexts/color-mode';
-import { DeleteIcon, EditIcon } from '@/shared/components/ui/icons';
-import {
- useDeleteApiKeyModal,
- useEditApiKeyModal,
-} from '../hooks/use-api-key-modals';
-import { useGetExchangeApiKeys } from '../../hooks/use-exchange-api-keys';
-
-export function ApiKeyData({
- stakingExchangeError,
-}: {
- stakingExchangeError?: string;
-}) {
- const { isDarkMode } = useColorMode();
- const { t } = useTranslation();
- const { openModal: openEditApiKeyModal } = useEditApiKeyModal();
- const { openModal: openDeleteApiKeyModal } = useDeleteApiKeyModal();
- const { data: exchangeApiKeyData, isError: isExchangeApiKeyError } =
- useGetExchangeApiKeys();
-
- const textField = isDarkMode ? (
-
- ) : (
-
- );
-
- return (
-
-
-
- {t('worker.profile.apiKeyData.apiKey')}
-
-
-
-
- {textField}
- {exchangeApiKeyData?.exchange_name && (
-
-
- openEditApiKeyModal(exchangeApiKeyData.exchange_name)
- }
- >
-
-
-
-
-
-
- )}
-
-
- );
-}
diff --git a/packages/apps/human-app/frontend/src/modules/worker/profile/components/delete-api-key-modal.tsx b/packages/apps/human-app/frontend/src/modules/worker/profile/components/delete-api-key-modal.tsx
deleted file mode 100644
index 6a75d44423..0000000000
--- a/packages/apps/human-app/frontend/src/modules/worker/profile/components/delete-api-key-modal.tsx
+++ /dev/null
@@ -1,118 +0,0 @@
-import { Stack, Typography } from '@mui/material';
-import { useTranslation } from 'react-i18next';
-import { Button } from '@/shared/components/ui/button';
-import { useIsMobile } from '@/shared/hooks';
-import { useDeleteExchangeApiKeys } from '../../hooks/use-exchange-api-keys';
-import { useEffect } from 'react';
-import { ModalError, ModalSuccess, ModalLoading } from './modal-states';
-
-interface DeleteApiKeyModalProps {
- onClose: () => void;
- disableClose: (disable: boolean) => void;
-}
-
-export function DeleteApiKeyModal({
- onClose,
- disableClose,
-}: DeleteApiKeyModalProps) {
- const { t } = useTranslation();
- const {
- mutate: deleteExchangeApiKey,
- reset: resetMutation,
- isSuccess,
- isError,
- isPending,
- isIdle,
- } = useDeleteExchangeApiKeys();
- const isMobile = useIsMobile();
-
- const handleDeleteExchangeApiKey = () => {
- deleteExchangeApiKey();
- };
-
- useEffect(() => {
- disableClose(isPending);
- }, [isPending, disableClose]);
-
- useEffect(() => {
- return () => {
- resetMutation();
- };
- }, [resetMutation]);
-
- return (
-
-
- {t('worker.profile.apiKeyData.deleteApiKey')}
-
- {isPending && }
- {isIdle && (
- <>
-
- {t('worker.profile.apiKeyData.deleteApiKeyConfirmation')}
-
-
- {t('worker.profile.apiKeyData.deleteApiKeyDescription')}
-
- >
- )}
- {isSuccess && (
-
-
- {t('worker.profile.apiKeyData.deleteKeySuccess')}
-
-
- )}
- {isError && (
-
- )}
- {isIdle && (
-
-
-
-
- )}
- {(isPending || isSuccess) && (
-
- )}
- {isError && (
-
- )}
-
- );
-}
diff --git a/packages/apps/human-app/frontend/src/modules/worker/profile/components/edit-api-key-modal.tsx b/packages/apps/human-app/frontend/src/modules/worker/profile/components/edit-api-key-modal.tsx
deleted file mode 100644
index 9141eb9b02..0000000000
--- a/packages/apps/human-app/frontend/src/modules/worker/profile/components/edit-api-key-modal.tsx
+++ /dev/null
@@ -1,280 +0,0 @@
-import { zodResolver } from '@hookform/resolvers/zod';
-import {
- Autocomplete,
- Box,
- FormControl,
- FormHelperText,
- Stack,
- TextField,
- Typography,
-} from '@mui/material';
-import { Controller, useForm } from 'react-hook-form';
-import { useTranslation } from 'react-i18next';
-import { z } from 'zod';
-import { Button } from '@/shared/components/ui/button';
-import { useIsMobile } from '@/shared/hooks';
-import { useEffect } from 'react';
-import {
- useEnrollExchangeApiKeys,
- useGetSupportedExchanges,
-} from '../../hooks/use-exchange-api-keys';
-import { ModalError, ModalLoading, ModalSuccess } from './modal-states';
-
-interface EditApiKeyModalProps {
- exchangeName: string;
- onClose: () => void;
- disableClose: (disable: boolean) => void;
-}
-
-export function EditApiKeyModal({
- onClose,
- exchangeName,
- disableClose,
-}: EditApiKeyModalProps) {
- const { t } = useTranslation();
- const isMobile = useIsMobile();
- const {
- mutate: postExchangeApiKey,
- reset: resetMutation,
- error,
- isSuccess,
- isError,
- isPending,
- isIdle,
- } = useEnrollExchangeApiKeys();
- const { data: supportedExchanges } = useGetSupportedExchanges();
-
- const {
- control,
- handleSubmit,
- formState: { errors },
- reset,
- } = useForm({
- defaultValues: {
- exchange: '',
- apiKey: '',
- secretKey: '',
- },
- resolver: zodResolver(
- z.object({
- exchange: z.string().min(1, t('validation.required')),
- apiKey: z.string().trim().min(1, t('validation.required')),
- secretKey: z.string().trim().min(1, t('validation.required')),
- })
- ),
- });
-
- useEffect(() => {
- if (exchangeName && supportedExchanges) {
- reset({
- exchange: exchangeName,
- apiKey: '',
- secretKey: '',
- });
- }
- }, [exchangeName, reset, supportedExchanges]);
-
- useEffect(() => {
- disableClose(isPending);
- }, [isPending, disableClose]);
-
- useEffect(() => {
- return () => {
- reset();
- resetMutation();
- };
- }, [reset, resetMutation]);
-
- const onSubmit = (data: {
- exchange: string;
- apiKey: string;
- secretKey: string;
- }) => {
- postExchangeApiKey(data);
- };
-
- return (
-
- );
-}
diff --git a/packages/apps/human-app/frontend/src/modules/worker/profile/components/index.ts b/packages/apps/human-app/frontend/src/modules/worker/profile/components/index.ts
index 7df62f04b8..7b2eadb361 100644
--- a/packages/apps/human-app/frontend/src/modules/worker/profile/components/index.ts
+++ b/packages/apps/human-app/frontend/src/modules/worker/profile/components/index.ts
@@ -2,9 +2,3 @@ export * from './wallet-connect-done';
export * from './profile-data';
export * from './identity-verification-control';
export * from './wallet-connection-control';
-export * from './staking-info';
-export * from './api-key-data';
-export * from './custom-text-field';
-export * from './add-api-key-modal';
-export * from './edit-api-key-modal';
-export * from './delete-api-key-modal';
diff --git a/packages/apps/human-app/frontend/src/modules/worker/profile/components/staking-info.tsx b/packages/apps/human-app/frontend/src/modules/worker/profile/components/staking-info.tsx
deleted file mode 100644
index 889d26aaae..0000000000
--- a/packages/apps/human-app/frontend/src/modules/worker/profile/components/staking-info.tsx
+++ /dev/null
@@ -1,225 +0,0 @@
-import {
- Button,
- IconButton,
- Link,
- Skeleton,
- Stack,
- Typography,
-} from '@mui/material';
-import { useTranslation } from 'react-i18next';
-import { Chip } from '@/shared/components/ui/chip';
-import { env } from '@/shared/env';
-import { useAddApiKeyModal } from '../hooks/use-api-key-modals';
-import { ApiKeyData } from './';
-import { useGetStakingSummary } from '../../hooks/use-staking';
-import { RefreshIcon } from '@/shared/components/ui/icons';
-import { useGetExchangeApiKeys } from '../../hooks/use-exchange-api-keys';
-import { useAuthenticatedUser } from '@/modules/auth/hooks/use-authenticated-user';
-import { useEffect, useRef, useState } from 'react';
-import { useAccessTokenRefresh } from '@/api/hooks/use-access-token-refresh';
-import { colorPalette } from '@/shared/styles/color-palette';
-import { useGetUiConfig } from '@/shared/hooks/use-get-ui-config';
-import {
- TopNotificationType,
- useNotification,
-} from '@/shared/hooks/use-notification';
-
-export function StakingInfo() {
- const [isPromptExpanded, setIsPromptExpanded] = useState(false);
- const tokenRefreshLock = useRef(false);
-
- const { user, updateUserData } = useAuthenticatedUser();
- const { refreshAccessTokenAsync } = useAccessTokenRefresh();
- const { t } = useTranslation();
- const { showNotification } = useNotification();
-
- const { openModal: openAddApiKeyModal } = useAddApiKeyModal();
- const { data: exchangeApiKeyData, isLoading: isExchangeApiKeyLoading } =
- useGetExchangeApiKeys();
- const {
- data: stakingSummary,
- isLoading,
- isError,
- refetch,
- isRefetching,
- } = useGetStakingSummary();
- const { data: uiConfig, isLoading: isUiConfigLoading } = useGetUiConfig();
-
- const isConnectButtonDisabled =
- !!exchangeApiKeyData?.exchange_name || isExchangeApiKeyLoading;
-
- const stakedAmount =
- Number(stakingSummary?.on_chain_stake || 0) +
- Number(stakingSummary?.exchange_stake || 0);
-
- const isStaked =
- isLoading || isError || isUiConfigLoading
- ? false
- : stakedAmount >= Number(uiConfig?.minThreshold || '0');
-
- useEffect(() => {
- const stakingSummaryError =
- stakingSummary?.on_chain_error || stakingSummary?.exchange_error;
- if (stakingSummaryError && !isLoading && !isRefetching) {
- showNotification({
- type: TopNotificationType.WARNING,
- message: stakingSummaryError,
- });
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [
- isLoading,
- isRefetching,
- stakingSummary?.on_chain_error,
- stakingSummary?.exchange_error,
- ]);
-
- useEffect(() => {
- if (isRefetching || isLoading) return;
-
- if (isStaked !== user.is_stake_eligible) {
- if (!tokenRefreshLock.current) {
- tokenRefreshLock.current = true;
- updateUserData({ is_stake_eligible: isStaked });
- void refreshAccessTokenAsync({ authType: 'web2' });
- }
- } else {
- tokenRefreshLock.current = false;
- }
- }, [
- isStaked,
- user.is_stake_eligible,
- refreshAccessTokenAsync,
- isRefetching,
- isLoading,
- updateUserData,
- ]);
-
- const handleRefreshStakingInfo = () => {
- if (isRefetching || isLoading) return;
- tokenRefreshLock.current = false;
- refetch();
- };
-
- return (
-
-
-
- {t('worker.profile.stakingInfo.stakeHmt')}
-
- {isLoading || isRefetching ? (
-
- ) : (
-
- )}
-
-
- {isPromptExpanded
- ? t('worker.profile.stakingInfo.prompt', {
- amount: uiConfig?.minThreshold,
- })
- : t('worker.profile.stakingInfo.promptShort')}{' '}
- {isPromptExpanded && (
- <>
-
-
- {t('worker.profile.stakingInfo.howToCreateApiKeys')}
-
-
- >
- )}
-
-
-
-
- {t('worker.profile.stakingInfo.stakedAmount')}
-
- svg > path': {
- fill:
- isRefetching || isLoading
- ? colorPalette.button.disabled
- : 'primary.main',
- },
- }}
- onClick={handleRefreshStakingInfo}
- >
-
-
-
- {isLoading || isRefetching ? (
-
- ) : (
-
- {stakedAmount} HMT
-
- )}
-
-
-
-
-
-
- );
-}
diff --git a/packages/apps/human-app/frontend/src/modules/worker/profile/hooks/use-api-key-modals.tsx b/packages/apps/human-app/frontend/src/modules/worker/profile/hooks/use-api-key-modals.tsx
deleted file mode 100644
index 5d7db03586..0000000000
--- a/packages/apps/human-app/frontend/src/modules/worker/profile/hooks/use-api-key-modals.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { useModal } from '@/shared/contexts/modal-context';
-import {
- AddApiKeyModal,
- EditApiKeyModal,
- DeleteApiKeyModal,
-} from '../components';
-
-export function useAddApiKeyModal() {
- const { openModal, closeModal, setDisableClose } = useModal();
-
- return {
- openModal: () =>
- openModal({
- content: (
-
- ),
- }),
- };
-}
-
-export function useEditApiKeyModal() {
- const { openModal, closeModal, setDisableClose } = useModal();
-
- return {
- openModal: (exchangeName: string) =>
- openModal({
- content: (
-
- ),
- }),
- };
-}
-
-export function useDeleteApiKeyModal() {
- const { openModal, closeModal, setDisableClose } = useModal();
-
- return {
- openModal: () =>
- openModal({
- content: (
-
- ),
- }),
- };
-}
diff --git a/packages/apps/human-app/frontend/src/modules/worker/profile/views/profile.page.tsx b/packages/apps/human-app/frontend/src/modules/worker/profile/views/profile.page.tsx
index 3fa42e64b9..d79116a913 100644
--- a/packages/apps/human-app/frontend/src/modules/worker/profile/views/profile.page.tsx
+++ b/packages/apps/human-app/frontend/src/modules/worker/profile/views/profile.page.tsx
@@ -12,10 +12,7 @@ import {
ProfileData,
IdentityVerificationControl,
WalletConnectionControl,
- StakingInfo,
} from '../components';
-import { PageCardLoader } from '@/shared/components/ui/page-card/page-card-loader';
-import { useUiConfig } from '@/shared/providers/ui-config-provider';
export function WorkerProfilePage() {
const { user } = useAuthenticatedUser();
@@ -23,7 +20,6 @@ export function WorkerProfilePage() {
const { isConnected, initializing, web3ProviderMutation } =
useWalletConnect();
const { showNotification } = useNotification();
- const { uiConfig, isUiConfigLoading } = useUiConfig();
useEffect(() => {
if (initializing) return;
@@ -50,10 +46,6 @@ export function WorkerProfilePage() {
showNotification,
]);
- if (isUiConfigLoading) {
- return ;
- }
-
return (
- {!!user.wallet_address && uiConfig?.stakingEligibilityEnabled && (
-
- )}
);
diff --git a/packages/apps/human-app/frontend/src/modules/worker/providers/require-stake.tsx b/packages/apps/human-app/frontend/src/modules/worker/providers/require-stake.tsx
deleted file mode 100644
index 83802a5467..0000000000
--- a/packages/apps/human-app/frontend/src/modules/worker/providers/require-stake.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import type { ReactNode } from 'react';
-import { useAuthenticatedUser } from '@/modules/auth/hooks/use-authenticated-user';
-import { routerPaths } from '@/router/router-paths';
-import { protectedRoutes } from '@/router/routes';
-import { useUiConfig } from '@/shared/providers/ui-config-provider';
-import { Navigate, useLocation, matchPath } from 'react-router-dom';
-
-const stakeProtectedPaths = protectedRoutes
- .map((route) => route.routerProps.path)
- .filter((path) => path !== routerPaths.worker.profile);
-
-export function RequireStake({ children }: Readonly<{ children: ReactNode }>) {
- const { user } = useAuthenticatedUser();
- const location = useLocation();
- const { uiConfig } = useUiConfig();
-
- const isStakeProtectedRoute = stakeProtectedPaths.some(
- (path) => path && matchPath(path, location.pathname)
- );
-
- if (
- uiConfig?.stakingEligibilityEnabled &&
- !user?.is_stake_eligible &&
- isStakeProtectedRoute
- ) {
- return ;
- }
-
- return children;
-}
diff --git a/packages/apps/human-app/frontend/src/modules/worker/services/exchangeApiKeys.service.ts b/packages/apps/human-app/frontend/src/modules/worker/services/exchangeApiKeys.service.ts
deleted file mode 100644
index 41db9404e2..0000000000
--- a/packages/apps/human-app/frontend/src/modules/worker/services/exchangeApiKeys.service.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { authorizedHumanAppApiClient } from '@/api';
-
-interface ExchangeApiKey {
- api_key: string;
- exchange_name: string;
-}
-
-interface Exchange {
- name: string;
- display_name: string;
-}
-
-async function getSupportedExchanges(): Promise {
- const response = await authorizedHumanAppApiClient.get(
- '/exchange-api-keys/supported-exchanges'
- );
- return response || [];
-}
-
-async function getExchangeApiKeys(): Promise {
- const response =
- await authorizedHumanAppApiClient.get('/exchange-api-keys');
- return response || null;
-}
-
-async function enrollExchangeApiKeys(data: {
- exchange: string;
- apiKey: string;
- secretKey: string;
-}): Promise {
- const { exchange, ...body } = data;
- await authorizedHumanAppApiClient.post(`/exchange-api-keys/${exchange}`, {
- body,
- });
-}
-
-async function deleteExchangeApiKeys(): Promise {
- await authorizedHumanAppApiClient.delete('/exchange-api-keys');
-}
-
-export {
- enrollExchangeApiKeys,
- getExchangeApiKeys,
- deleteExchangeApiKeys,
- getSupportedExchanges,
-};
diff --git a/packages/apps/human-app/frontend/src/modules/worker/services/staking.service.ts b/packages/apps/human-app/frontend/src/modules/worker/services/staking.service.ts
deleted file mode 100644
index d4a38a65b3..0000000000
--- a/packages/apps/human-app/frontend/src/modules/worker/services/staking.service.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { authorizedHumanAppApiClient } from '@/api';
-
-interface StakeSummary {
- exchange_stake: string;
- exchange_error?: string;
- on_chain_stake: string;
- on_chain_error?: string;
-}
-
-async function getStakingSummary(): Promise {
- const response =
- await authorizedHumanAppApiClient.get('/staking/summary');
- return response || null;
-}
-
-export { getStakingSummary };
diff --git a/packages/apps/human-app/frontend/src/router/components/drawer-menu-items/drawer-menu-items-worker.tsx b/packages/apps/human-app/frontend/src/router/components/drawer-menu-items/drawer-menu-items-worker.tsx
index 5c39359e16..427bba124b 100644
--- a/packages/apps/human-app/frontend/src/router/components/drawer-menu-items/drawer-menu-items-worker.tsx
+++ b/packages/apps/human-app/frontend/src/router/components/drawer-menu-items/drawer-menu-items-worker.tsx
@@ -9,21 +9,14 @@ import type { UserData } from '@/modules/auth/context/auth-context';
import { routerPaths } from '@/router/router-paths';
import { KycStatus } from '@/modules/worker/profile/types';
import { type MenuItem } from '../layout/protected';
-import { UiConfig } from '@/shared/services/ui-config.service';
-export const workerDrawerTopMenuItems = (
- user: UserData | null,
- uiConfig: UiConfig | undefined
-): MenuItem[] => {
+export const workerDrawerTopMenuItems = (user: UserData | null): MenuItem[] => {
return [
{
label: t('components.DrawerNavigation.jobs'),
icon: ,
link: routerPaths.worker.jobsDiscovery,
- disabled:
- !user?.wallet_address ||
- (uiConfig?.stakingEligibilityEnabled && !user?.is_stake_eligible) ||
- user.kyc_status !== KycStatus.APPROVED,
+ disabled: !user?.wallet_address || user.kyc_status !== KycStatus.APPROVED,
},
];
};
diff --git a/packages/apps/human-app/frontend/src/router/router.tsx b/packages/apps/human-app/frontend/src/router/router.tsx
index 0d27ce4c46..b22c8052d8 100644
--- a/packages/apps/human-app/frontend/src/router/router.tsx
+++ b/packages/apps/human-app/frontend/src/router/router.tsx
@@ -20,12 +20,9 @@ import {
workerDrawerBottomMenuItems,
workerDrawerTopMenuItems,
} from './components';
-import { RequireStake } from '@/modules/worker/providers/require-stake';
-import { useUiConfig } from '@/shared/providers/ui-config-provider';
export function Router() {
const { user } = useAuth();
- const { uiConfig } = useUiConfig();
const handleSignOut = () => {
browserAuthProvider.signOut({
@@ -60,24 +57,22 @@ export function Router() {
-
- (
-
- )}
- renderHCaptchaStatisticsDrawer={(isOpen) => (
-
- )}
- renderGovernanceBanner
- />
-
+ (
+
+ )}
+ renderHCaptchaStatisticsDrawer={(isOpen) => (
+
+ )}
+ renderGovernanceBanner
+ />
}
key={routerProps.path}
diff --git a/packages/apps/human-app/frontend/src/shared/i18n/en.json b/packages/apps/human-app/frontend/src/shared/i18n/en.json
index d5619012e9..127b9c1d4e 100644
--- a/packages/apps/human-app/frontend/src/shared/i18n/en.json
+++ b/packages/apps/human-app/frontend/src/shared/i18n/en.json
@@ -226,48 +226,6 @@
"review": "Under Review",
"expired": "Expired",
"abandoned": "Abandoned"
- },
- "stakingStatusValues": {
- "staked": "Staked",
- "error": "Error",
- "notStaked": "Not Staked"
- },
- "stakingInfo": {
- "promptShort": "Before you can start earning on the HUMAN App, you'll need to stake HMT.",
- "prompt": "Before you can start earning on the HUMAN App, you’ll need to stake HMT. You can stake directly on our Staking Dashboard, or connect your API key having minimum balance of {{amount}} HMT in your exchange account to stake automatically via API KEY.",
- "stakeHmt": "Stake HMT",
- "stakingInfoValue": "Staking Info Value",
- "stakedAmount": "Staked amount",
- "readMore": "Read more",
- "readLess": "Read less",
- "howToCreateApiKeys": "How to create API keys?"
- },
- "apiKeyData": {
- "apiKey": "API KEY",
- "exchange": "Exchange",
- "apiSecret": "API Secret",
- "apiKeyConnected": "Connected",
- "apiKeyNotConnected": "Not connected",
- "cancel": "Cancel",
- "error": "Error",
- "close": "Close",
- "edit": "Edit",
- "tryAgain": "Try Again",
- "connectYourApiKey": "Connect your API KEY",
- "connectApiKey": "Connect API KEY",
- "connectSuccess": "You have successfully connected your API key",
- "connectError": "Failed to connect API key.",
- "editSuccess": "You have successfully edited your API key",
- "editError": "Failed to edit API key.",
- "deleteKeySuccess": "You have successfully deleted your API key",
- "deleteKeyError": "Failed to delete API key.",
- "modalDescription": "To stake HMT using your exchange balance, connect your exchange account by adding your API key and secret. Your credentials are encrypted and securely stored.",
- "modalFooterAgreement": "By connecting your API KEY, you agree to HUMAN Protocol Terms of Service and consent to its Privacy Policy.",
- "deleteApiKey": "Delete API KEY",
- "deleteApiKeyConfirmation": "Are you sure you want to delete your API key?",
- "deleteApiKeyDescription": "You can only solve jobs in the HUMAN App if you have staked HMT.",
- "editApiKey": "Edit API KEY",
- "saveChanges": "Save Changes"
}
},
"oraclesTable": {
diff --git a/packages/apps/human-app/frontend/src/shared/providers/ui-config-provider.tsx b/packages/apps/human-app/frontend/src/shared/providers/ui-config-provider.tsx
deleted file mode 100644
index 5f23cd7d49..0000000000
--- a/packages/apps/human-app/frontend/src/shared/providers/ui-config-provider.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import React, { createContext, useContext, useMemo } from 'react';
-import { useGetUiConfig } from '../hooks/use-get-ui-config';
-import { UiConfig } from '../services/ui-config.service';
-
-interface UiConfigContextType {
- uiConfig: UiConfig | undefined;
- isUiConfigLoading: boolean;
- isUiConfigError: boolean;
- error: Error | null;
-}
-
-const UiConfigContext = createContext(
- undefined
-);
-
-export function UiConfigProvider({
- children,
-}: Readonly<{ children: React.ReactNode }>) {
- const { data, isLoading, error, isError } = useGetUiConfig();
-
- const contextValue = useMemo(
- () => ({
- uiConfig: data,
- isUiConfigLoading: isLoading,
- isUiConfigError: isError,
- error,
- }),
- [data, isLoading, isError, error]
- );
-
- return (
-
- {children}
-
- );
-}
-
-export const useUiConfig = () => {
- const context = useContext(UiConfigContext);
- if (!context) {
- throw new Error('useUiConfig must be used within a UiConfigProvider');
- }
- return context;
-};
diff --git a/packages/apps/human-app/frontend/src/shared/services/ui-config.service.ts b/packages/apps/human-app/frontend/src/shared/services/ui-config.service.ts
index 5103cc92c3..0fadf37eaf 100644
--- a/packages/apps/human-app/frontend/src/shared/services/ui-config.service.ts
+++ b/packages/apps/human-app/frontend/src/shared/services/ui-config.service.ts
@@ -7,8 +7,6 @@ const apiPaths = {
const uiConfigSchema = z.object({
chainIdsEnabled: z.array(z.number()),
- stakingEligibilityEnabled: z.boolean().prefault(false),
- minThreshold: z.string(),
});
export type UiConfig = z.infer;
diff --git a/packages/apps/human-app/server/src/app.module.ts b/packages/apps/human-app/server/src/app.module.ts
index 6de9bbd087..529154b0ed 100644
--- a/packages/apps/human-app/server/src/app.module.ts
+++ b/packages/apps/human-app/server/src/app.module.ts
@@ -58,10 +58,6 @@ import { OperatorController } from './modules/user-operator/operator.controller'
import { OperatorModule } from './modules/user-operator/operator.module';
import { WorkerController } from './modules/user-worker/worker.controller';
import { WorkerModule } from './modules/user-worker/worker.module';
-import { ExchangeApiKeysModule } from './modules/exchange-api-keys/exchange-api-keys.module';
-import { ExchangeApiKeysController } from './modules/exchange-api-keys/exchange-api-keys.controller';
-import { StakingController } from './modules/staking/staking.controller';
-import { StakingModule } from './modules/staking/staking.module';
const JOI_BOOLEAN_STRING_SCHEMA = Joi.string().valid('true', 'false');
@@ -151,8 +147,6 @@ const JOI_BOOLEAN_STRING_SCHEMA = Joi.string().valid('true', 'false');
NDAModule,
AbuseModule,
GovernanceModule,
- ExchangeApiKeysModule,
- StakingModule,
],
controllers: [
AppController,
@@ -168,8 +162,6 @@ const JOI_BOOLEAN_STRING_SCHEMA = Joi.string().valid('true', 'false');
NDAController,
AbuseController,
GovernanceController,
- ExchangeApiKeysController,
- StakingController,
],
exports: [HttpModule],
providers: [
diff --git a/packages/apps/human-app/server/src/common/config/gateway-config.service.ts b/packages/apps/human-app/server/src/common/config/gateway-config.service.ts
index 0dbbb0c2cb..7f7c548fb2 100644
--- a/packages/apps/human-app/server/src/common/config/gateway-config.service.ts
+++ b/packages/apps/human-app/server/src/common/config/gateway-config.service.ts
@@ -141,36 +141,6 @@ export class GatewayConfigService {
method: HttpMethod.GET,
headers: this.JSON_HEADER,
},
- [ReputationOracleEndpoints.EXCHANGE_API_KEYS_ENROLL]: {
- endpoint: '/exchange-api-keys',
- method: HttpMethod.POST,
- headers: this.JSON_HEADER,
- },
- [ReputationOracleEndpoints.EXCHANGE_API_KEYS_DELETE]: {
- endpoint: '/exchange-api-keys',
- method: HttpMethod.DELETE,
- headers: this.JSON_HEADER,
- },
- [ReputationOracleEndpoints.EXCHANGE_API_KEYS_RETRIEVE]: {
- endpoint: '/exchange-api-keys',
- method: HttpMethod.GET,
- headers: this.JSON_HEADER,
- },
- [ReputationOracleEndpoints.EXCHANGE_API_KEYS_SUPPORTED_EXCHANGES]: {
- endpoint: '/exchange-api-keys/supported',
- method: HttpMethod.GET,
- headers: this.JSON_HEADER,
- },
- [ReputationOracleEndpoints.STAKE_SUMMARY]: {
- endpoint: '/staking/summary',
- method: HttpMethod.GET,
- headers: this.JSON_HEADER,
- },
- [ReputationOracleEndpoints.STAKE_CONFIG]: {
- endpoint: '/staking/config',
- method: HttpMethod.GET,
- headers: this.JSON_HEADER,
- },
} as Record,
},
[ExternalApiName.HCAPTCHA_LABELING_STATS]: {
diff --git a/packages/apps/human-app/server/src/common/enums/http-method.ts b/packages/apps/human-app/server/src/common/enums/http-method.ts
index 4dfcef38b9..5a3bbf6de1 100644
--- a/packages/apps/human-app/server/src/common/enums/http-method.ts
+++ b/packages/apps/human-app/server/src/common/enums/http-method.ts
@@ -1,5 +1,4 @@
export enum HttpMethod {
GET = 'GET',
POST = 'POST',
- DELETE = 'DELETE',
}
diff --git a/packages/apps/human-app/server/src/common/enums/reputation-oracle-endpoints.ts b/packages/apps/human-app/server/src/common/enums/reputation-oracle-endpoints.ts
index 63cc216389..bedd90d15d 100644
--- a/packages/apps/human-app/server/src/common/enums/reputation-oracle-endpoints.ts
+++ b/packages/apps/human-app/server/src/common/enums/reputation-oracle-endpoints.ts
@@ -22,12 +22,6 @@ export enum ReputationOracleEndpoints {
SIGN_NDA = 'sign_nda',
REPORT_ABUSE = 'report_abuse',
GET_ABUSE_REPORTS = 'get_abuse_reports',
- EXCHANGE_API_KEYS_ENROLL = 'exchange_api_keys_enroll',
- EXCHANGE_API_KEYS_DELETE = 'exchange_api_keys_delete',
- EXCHANGE_API_KEYS_RETRIEVE = 'exchange_api_keys_retrieve',
- EXCHANGE_API_KEYS_SUPPORTED_EXCHANGES = 'exchange_api_keys_supported_exchanges',
- STAKE_SUMMARY = 'stake_summary',
- STAKE_CONFIG = 'stake_config',
}
export enum HCaptchaLabelingStatsEndpoints {
USER_STATS = 'user_stats',
diff --git a/packages/apps/human-app/server/src/common/guards/strategy/jwt.http.ts b/packages/apps/human-app/server/src/common/guards/strategy/jwt.http.ts
index 5d79c29c28..1a06cd60ee 100644
--- a/packages/apps/human-app/server/src/common/guards/strategy/jwt.http.ts
+++ b/packages/apps/human-app/server/src/common/guards/strategy/jwt.http.ts
@@ -44,7 +44,6 @@ export class JwtHttpStrategy extends PassportStrategy(Strategy, 'jwt-http') {
status: string;
wallet_address: string;
reputation_network: string;
- is_stake_eligible?: boolean;
qualifications?: string[];
site_key?: string;
email?: string;
@@ -59,7 +58,6 @@ export class JwtHttpStrategy extends PassportStrategy(Strategy, 'jwt-http') {
wallet_address: payload.wallet_address,
status: payload.status,
reputation_network: payload.reputation_network,
- is_stake_eligible: payload.is_stake_eligible,
qualifications: payload.qualifications,
site_key: payload.site_key,
email: payload.email,
diff --git a/packages/apps/human-app/server/src/common/utils/jwt-token.model.ts b/packages/apps/human-app/server/src/common/utils/jwt-token.model.ts
index a0175c642d..85da60b3c1 100644
--- a/packages/apps/human-app/server/src/common/utils/jwt-token.model.ts
+++ b/packages/apps/human-app/server/src/common/utils/jwt-token.model.ts
@@ -10,8 +10,6 @@ export class JwtUserData {
@AutoMap()
reputation_network: string;
@AutoMap()
- is_stake_eligible?: boolean;
- @AutoMap()
email?: string;
@AutoMap()
qualifications?: string[];
diff --git a/packages/apps/human-app/server/src/integrations/reputation-oracle/reputation-oracle.gateway.ts b/packages/apps/human-app/server/src/integrations/reputation-oracle/reputation-oracle.gateway.ts
index 618833409a..03094bb53f 100644
--- a/packages/apps/human-app/server/src/integrations/reputation-oracle/reputation-oracle.gateway.ts
+++ b/packages/apps/human-app/server/src/integrations/reputation-oracle/reputation-oracle.gateway.ts
@@ -95,17 +95,6 @@ import {
ReportAbuseParams,
ReportedAbuseResponse,
} from '../../modules/abuse/model/abuse.model';
-import { HttpMethod } from '../../common/enums/http-method';
-import {
- EnrollExchangeApiKeysCommand,
- EnrollExchangeApiKeysData,
- RetrieveExchangeApiKeysResponse,
- SupportedExchangeResponse,
-} from '../../modules/exchange-api-keys/model/exchange-api-keys.model';
-import {
- StakeConfigResponse,
- StakeSummaryResponse,
-} from '../../modules/staking/model/staking.model';
@Injectable()
export class ReputationOracleGateway {
@@ -148,76 +137,6 @@ export class ReputationOracleGateway {
return response.data as T;
}
- async enrollExchangeApiKeys(
- command: EnrollExchangeApiKeysCommand,
- ): Promise<{ id: number }> {
- const enrollExchangeApiKeysData = this.mapper.map(
- command,
- EnrollExchangeApiKeysCommand,
- EnrollExchangeApiKeysData,
- );
- const options = this.getEndpointOptions(
- ReputationOracleEndpoints.EXCHANGE_API_KEYS_ENROLL,
- enrollExchangeApiKeysData,
- command.token,
- );
- options.url = `${options.url}/${command.exchangeName}`;
- return this.handleRequestToReputationOracle<{ id: number }>(options);
- }
-
- async deleteExchangeApiKeys(token: string) {
- const options = this.getEndpointOptions(
- ReputationOracleEndpoints.EXCHANGE_API_KEYS_DELETE,
- undefined,
- token,
- );
- options.method = HttpMethod.DELETE;
- return this.handleRequestToReputationOracle(options);
- }
-
- async retrieveExchangeApiKeys(
- token: string,
- ): Promise {
- const options = this.getEndpointOptions(
- ReputationOracleEndpoints.EXCHANGE_API_KEYS_RETRIEVE,
- undefined,
- token,
- );
- return this.handleRequestToReputationOracle(
- options,
- );
- }
-
- async getStakeSummary(token: string): Promise {
- const options = this.getEndpointOptions(
- ReputationOracleEndpoints.STAKE_SUMMARY,
- undefined,
- token,
- );
- return this.handleRequestToReputationOracle(options);
- }
-
- async getStakeConfig(): Promise {
- const options = this.getEndpointOptions(
- ReputationOracleEndpoints.STAKE_CONFIG,
- undefined,
- );
- return this.handleRequestToReputationOracle(options);
- }
-
- async supportedExchanges(
- token: string,
- ): Promise {
- const options = this.getEndpointOptions(
- ReputationOracleEndpoints.EXCHANGE_API_KEYS_SUPPORTED_EXCHANGES,
- undefined,
- token,
- );
- return this.handleRequestToReputationOracle(
- options,
- );
- }
-
async sendWorkerSignup(command: SignupWorkerCommand): Promise {
const signupWorkerData = this.mapper.map(
command,
diff --git a/packages/apps/human-app/server/src/integrations/reputation-oracle/reputation-oracle.mapper.profile.ts b/packages/apps/human-app/server/src/integrations/reputation-oracle/reputation-oracle.mapper.profile.ts
index 3488762b01..1c147e2833 100644
--- a/packages/apps/human-app/server/src/integrations/reputation-oracle/reputation-oracle.mapper.profile.ts
+++ b/packages/apps/human-app/server/src/integrations/reputation-oracle/reputation-oracle.mapper.profile.ts
@@ -62,10 +62,6 @@ import {
ReportAbuseData,
ReportAbuseParams,
} from '../../modules/abuse/model/abuse.model';
-import {
- EnrollExchangeApiKeysCommand,
- EnrollExchangeApiKeysData,
-} from '../../modules/exchange-api-keys/model/exchange-api-keys.model';
@Injectable()
export class ReputationOracleProfile extends AutomapperProfile {
@@ -170,15 +166,6 @@ export class ReputationOracleProfile extends AutomapperProfile {
destination: new SnakeCaseNamingConvention(),
}),
);
- createMap(
- mapper,
- EnrollExchangeApiKeysCommand,
- EnrollExchangeApiKeysData,
- namingConventions({
- source: new CamelCaseNamingConvention(),
- destination: new SnakeCaseNamingConvention(),
- }),
- );
};
}
}
diff --git a/packages/apps/human-app/server/src/main.ts b/packages/apps/human-app/server/src/main.ts
index d2cdacde04..330c60d253 100644
--- a/packages/apps/human-app/server/src/main.ts
+++ b/packages/apps/human-app/server/src/main.ts
@@ -19,7 +19,7 @@ async function bootstrap() {
if (envConfigService.isCorsEnabled) {
app.enableCors({
origin: envConfigService.corsEnabledOrigin,
- methods: ['GET', 'POST', 'OPTIONS', 'PUT', 'DELETE'],
+ methods: ['GET', 'POST', 'OPTIONS', 'PUT'],
allowedHeaders: envConfigService.corsAllowedHeaders,
});
}
diff --git a/packages/apps/human-app/server/src/modules/exchange-api-keys/exchange-api-keys.controller.ts b/packages/apps/human-app/server/src/modules/exchange-api-keys/exchange-api-keys.controller.ts
deleted file mode 100644
index efb605dfa9..0000000000
--- a/packages/apps/human-app/server/src/modules/exchange-api-keys/exchange-api-keys.controller.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import {
- Body,
- Controller,
- Delete,
- Get,
- HttpCode,
- Param,
- Post,
- Request,
-} from '@nestjs/common';
-import {
- ApiBearerAuth,
- ApiBody,
- ApiOperation,
- ApiResponse,
- ApiTags,
-} from '@nestjs/swagger';
-import { RequestWithUser } from '../../common/interfaces/jwt';
-import { ExchangeApiKeysService } from '../../modules/exchange-api-keys/exchange-api-keys.service';
-import { InjectMapper } from '@automapper/nestjs';
-import { Mapper } from '@automapper/core';
-import {
- EnrollExchangeApiKeysCommand,
- EnrollExchangeApiKeysDto,
- RetrieveExchangeApiKeysResponse,
- SupportedExchangeResponse,
-} from './model/exchange-api-keys.model';
-
-@ApiTags('Exchange-Api-Keys')
-@ApiBearerAuth()
-@Controller('/exchange-api-keys')
-export class ExchangeApiKeysController {
- constructor(
- private readonly service: ExchangeApiKeysService,
- @InjectMapper() private readonly mapper: Mapper,
- ) {}
-
- @ApiOperation({ summary: 'Enroll API keys for exchange' })
- @ApiBody({ type: EnrollExchangeApiKeysDto })
- @ApiResponse({ status: 200, description: 'Exchange API keys enrolled' })
- @HttpCode(200)
- @Post('/:exchange_name')
- async enroll(
- @Param('exchange_name') exchangeName: string,
- @Body() dto: EnrollExchangeApiKeysDto,
- @Request() req: RequestWithUser,
- ): Promise<{ id: number }> {
- const command = this.mapper.map(
- dto,
- EnrollExchangeApiKeysDto,
- EnrollExchangeApiKeysCommand,
- );
- command.token = req.token;
- command.exchangeName = exchangeName;
- return this.service.enroll(command);
- }
-
- @ApiOperation({ summary: 'Delete API keys for exchange' })
- @ApiResponse({ status: 204, description: 'Exchange API keys deleted' })
- @HttpCode(204)
- @Delete('/')
- async delete(@Request() req: RequestWithUser): Promise {
- await this.service.delete(req.token);
- }
-
- @ApiOperation({
- summary: 'Retrieve API keys for exchange',
- })
- @Get('/')
- async retrieve(
- @Request() req: RequestWithUser,
- ): Promise {
- return this.service.retrieve(req.token);
- }
-
- @ApiOperation({ summary: 'Get supported exchanges' })
- @Get('/supported-exchanges')
- async getSupportedExchanges(
- @Request() req: RequestWithUser,
- ): Promise {
- return this.service.getSupportedExchanges(req.token);
- }
-}
diff --git a/packages/apps/human-app/server/src/modules/exchange-api-keys/exchange-api-keys.mapper.profile.ts b/packages/apps/human-app/server/src/modules/exchange-api-keys/exchange-api-keys.mapper.profile.ts
deleted file mode 100644
index 5f65c8f475..0000000000
--- a/packages/apps/human-app/server/src/modules/exchange-api-keys/exchange-api-keys.mapper.profile.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Mapper, createMap } from '@automapper/core';
-import { AutomapperProfile, InjectMapper } from '@automapper/nestjs';
-import { Injectable } from '@nestjs/common';
-import {
- EnrollExchangeApiKeysCommand,
- EnrollExchangeApiKeysDto,
-} from './model/exchange-api-keys.model';
-
-@Injectable()
-export class ExchangeApiKeysProfile extends AutomapperProfile {
- constructor(@InjectMapper() mapper: Mapper) {
- super(mapper);
- }
-
- override get profile() {
- return (mapper: Mapper) => {
- createMap(mapper, EnrollExchangeApiKeysDto, EnrollExchangeApiKeysCommand);
- };
- }
-}
diff --git a/packages/apps/human-app/server/src/modules/exchange-api-keys/exchange-api-keys.module.ts b/packages/apps/human-app/server/src/modules/exchange-api-keys/exchange-api-keys.module.ts
deleted file mode 100644
index 3b6d627810..0000000000
--- a/packages/apps/human-app/server/src/modules/exchange-api-keys/exchange-api-keys.module.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Module } from '@nestjs/common';
-import { ExchangeApiKeysController } from '../../modules/exchange-api-keys/exchange-api-keys.controller';
-import { ExchangeApiKeysService } from '../../modules/exchange-api-keys/exchange-api-keys.service';
-import { ReputationOracleModule } from '../../integrations/reputation-oracle/reputation-oracle.module';
-import { ExchangeApiKeysProfile } from './exchange-api-keys.mapper.profile';
-
-@Module({
- imports: [ReputationOracleModule],
- controllers: [ExchangeApiKeysController],
- providers: [ExchangeApiKeysService, ExchangeApiKeysProfile],
- exports: [ExchangeApiKeysService],
-})
-export class ExchangeApiKeysModule {}
diff --git a/packages/apps/human-app/server/src/modules/exchange-api-keys/exchange-api-keys.service.ts b/packages/apps/human-app/server/src/modules/exchange-api-keys/exchange-api-keys.service.ts
deleted file mode 100644
index 712335441b..0000000000
--- a/packages/apps/human-app/server/src/modules/exchange-api-keys/exchange-api-keys.service.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Injectable } from '@nestjs/common';
-import { ReputationOracleGateway } from '../../integrations/reputation-oracle/reputation-oracle.gateway';
-import {
- EnrollExchangeApiKeysCommand,
- RetrieveExchangeApiKeysResponse,
- SupportedExchangeResponse,
-} from './model/exchange-api-keys.model';
-
-@Injectable()
-export class ExchangeApiKeysService {
- constructor(private readonly reputationOracle: ReputationOracleGateway) {}
-
- enroll(command: EnrollExchangeApiKeysCommand): Promise<{ id: number }> {
- return this.reputationOracle.enrollExchangeApiKeys(command);
- }
-
- delete(token: string): Promise {
- return this.reputationOracle.deleteExchangeApiKeys(token);
- }
-
- retrieve(token: string): Promise {
- return this.reputationOracle.retrieveExchangeApiKeys(token);
- }
-
- getSupportedExchanges(token: string): Promise {
- return this.reputationOracle.supportedExchanges(token);
- }
-}
diff --git a/packages/apps/human-app/server/src/modules/exchange-api-keys/model/exchange-api-keys.model.ts b/packages/apps/human-app/server/src/modules/exchange-api-keys/model/exchange-api-keys.model.ts
deleted file mode 100644
index f07c049e26..0000000000
--- a/packages/apps/human-app/server/src/modules/exchange-api-keys/model/exchange-api-keys.model.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { AutoMap } from '@automapper/classes';
-import { ApiProperty } from '@nestjs/swagger';
-import { IsString } from 'class-validator';
-
-export class EnrollExchangeApiKeysDto {
- @AutoMap()
- @IsString()
- @ApiProperty()
- apiKey: string;
-
- @AutoMap()
- @IsString()
- @ApiProperty()
- secretKey: string;
-}
-
-export class EnrollExchangeApiKeysCommand {
- @AutoMap()
- apiKey: string;
- @AutoMap()
- secretKey: string;
- token: string;
- exchangeName: string;
-}
-
-export class EnrollExchangeApiKeysData {
- @AutoMap()
- apiKey: string;
- @AutoMap()
- secretKey: string;
-}
-
-export class RetrieveExchangeApiKeysResponse {
- apiKey: string;
- exchangeName: string;
-}
-
-export class SupportedExchangeResponse {
- @ApiProperty()
- name: string;
-
- @ApiProperty()
- displayName: string;
-}
diff --git a/packages/apps/human-app/server/src/modules/exchange-api-keys/spec/exchange-api-keys.controller.spec.ts b/packages/apps/human-app/server/src/modules/exchange-api-keys/spec/exchange-api-keys.controller.spec.ts
deleted file mode 100644
index e298642428..0000000000
--- a/packages/apps/human-app/server/src/modules/exchange-api-keys/spec/exchange-api-keys.controller.spec.ts
+++ /dev/null
@@ -1,96 +0,0 @@
-import { classes } from '@automapper/classes';
-import { AutomapperModule } from '@automapper/nestjs';
-import { Test, TestingModule } from '@nestjs/testing';
-import { RequestWithUser } from '../../../common/interfaces/jwt';
-import { ExchangeApiKeysController } from '../exchange-api-keys.controller';
-import { ExchangeApiKeysService } from '../exchange-api-keys.service';
-import {
- enrollExchangeApiKeysCommandFixture,
- enrollExchangeApiKeysDtoFixture,
- enrollExchangeApiKeysResponseFixture,
- EXCHANGE_NAME,
- retrieveExchangeApiKeysResponseFixture,
- TOKEN,
-} from './exchange-api-keys.fixtures';
-import { exchangeApiKeysServiceMock } from './exchange-api-keys.service.mock';
-import { ExchangeApiKeysProfile } from '../exchange-api-keys.mapper.profile';
-
-describe('ExchangeApiKeysController', () => {
- let controller: ExchangeApiKeysController;
- let service: ExchangeApiKeysService;
-
- beforeEach(async () => {
- const module: TestingModule = await Test.createTestingModule({
- controllers: [ExchangeApiKeysController],
- imports: [
- AutomapperModule.forRoot({
- strategyInitializer: classes(),
- }),
- ],
- providers: [
- ExchangeApiKeysService,
- ExchangeApiKeysProfile,
- {
- provide: ExchangeApiKeysService,
- useValue: exchangeApiKeysServiceMock,
- },
- ],
- })
- .overrideProvider(ExchangeApiKeysService)
- .useValue(exchangeApiKeysServiceMock)
- .compile();
-
- controller = module.get(
- ExchangeApiKeysController,
- );
- service = module.get(ExchangeApiKeysService);
- });
-
- it('should be defined', () => {
- expect(controller).toBeDefined();
- });
-
- describe('enroll', () => {
- it('should call service.enroll with mapped command and return id', async () => {
- const req: RequestWithUser = { token: TOKEN } as RequestWithUser;
- const result = await controller.enroll(
- EXCHANGE_NAME,
- enrollExchangeApiKeysDtoFixture,
- req,
- );
- expect(service.enroll).toHaveBeenCalledWith(
- enrollExchangeApiKeysCommandFixture,
- );
- expect(result).toEqual(enrollExchangeApiKeysResponseFixture);
- });
- });
-
- describe('delete', () => {
- it('should call service.delete with token', async () => {
- const req: RequestWithUser = { token: TOKEN } as RequestWithUser;
- const result = await controller.delete(req);
- expect(service.delete).toHaveBeenCalledWith(TOKEN);
- expect(result).toEqual(undefined);
- });
- });
-
- describe('retrieve', () => {
- it('should call service.retrieve with token and return response', async () => {
- const req: RequestWithUser = { token: TOKEN } as RequestWithUser;
- const result = await controller.retrieve(req);
- expect(service.retrieve).toHaveBeenCalledWith(TOKEN);
- expect(result).toEqual(retrieveExchangeApiKeysResponseFixture);
- });
- });
-
- describe('getSupportedExchanges', () => {
- it('should call service.getSupportedExchanges with token and return response', async () => {
- const req: RequestWithUser = { token: TOKEN } as RequestWithUser;
- const result = await controller.getSupportedExchanges(req);
- expect(service.getSupportedExchanges).toHaveBeenCalledWith(TOKEN);
- expect(result).toEqual(
- exchangeApiKeysServiceMock.getSupportedExchanges(TOKEN),
- );
- });
- });
-});
diff --git a/packages/apps/human-app/server/src/modules/exchange-api-keys/spec/exchange-api-keys.fixtures.ts b/packages/apps/human-app/server/src/modules/exchange-api-keys/spec/exchange-api-keys.fixtures.ts
deleted file mode 100644
index 8cfda1ab77..0000000000
--- a/packages/apps/human-app/server/src/modules/exchange-api-keys/spec/exchange-api-keys.fixtures.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import {
- EnrollExchangeApiKeysCommand,
- EnrollExchangeApiKeysDto,
- RetrieveExchangeApiKeysResponse,
-} from '../model/exchange-api-keys.model';
-
-export const EXCHANGE_NAME = 'mexc';
-export const TOKEN = 'test_user_token';
-export const API_KEY = 'test_api_key';
-export const API_SECRET = 'test_api_secret';
-export const ID = 123;
-
-export const enrollExchangeApiKeysDtoFixture: EnrollExchangeApiKeysDto = {
- apiKey: API_KEY,
- secretKey: API_SECRET,
-};
-
-export const enrollExchangeApiKeysCommandFixture: EnrollExchangeApiKeysCommand =
- {
- apiKey: API_KEY,
- secretKey: API_SECRET,
- token: TOKEN,
- exchangeName: EXCHANGE_NAME,
- };
-
-export const enrollExchangeApiKeysResponseFixture = {
- id: ID,
-};
-
-export const retrieveExchangeApiKeysResponseFixture: RetrieveExchangeApiKeysResponse =
- {
- apiKey: API_KEY,
- exchangeName: EXCHANGE_NAME,
- };
-
-export const supportedExchangesResponseFixture = [
- {
- name: 'mexc',
- displayName: 'MEXC Global',
- },
- {
- name: 'gate',
- displayName: 'Gate',
- },
-];
diff --git a/packages/apps/human-app/server/src/modules/exchange-api-keys/spec/exchange-api-keys.service.mock.ts b/packages/apps/human-app/server/src/modules/exchange-api-keys/spec/exchange-api-keys.service.mock.ts
deleted file mode 100644
index 357e2142d0..0000000000
--- a/packages/apps/human-app/server/src/modules/exchange-api-keys/spec/exchange-api-keys.service.mock.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import {
- enrollExchangeApiKeysResponseFixture,
- retrieveExchangeApiKeysResponseFixture,
- supportedExchangesResponseFixture,
-} from './exchange-api-keys.fixtures';
-
-export const exchangeApiKeysServiceMock = {
- enroll: jest.fn().mockReturnValue(enrollExchangeApiKeysResponseFixture),
- delete: jest.fn().mockResolvedValue(undefined),
- retrieve: jest.fn().mockReturnValue(retrieveExchangeApiKeysResponseFixture),
- getSupportedExchanges: jest
- .fn()
- .mockReturnValue(supportedExchangesResponseFixture),
-};
diff --git a/packages/apps/human-app/server/src/modules/exchange-api-keys/spec/exchange-api-keys.service.spec.ts b/packages/apps/human-app/server/src/modules/exchange-api-keys/spec/exchange-api-keys.service.spec.ts
deleted file mode 100644
index f29868a395..0000000000
--- a/packages/apps/human-app/server/src/modules/exchange-api-keys/spec/exchange-api-keys.service.spec.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import { Test, TestingModule } from '@nestjs/testing';
-import { ReputationOracleGateway } from '../../../integrations/reputation-oracle/reputation-oracle.gateway';
-import { ExchangeApiKeysService } from '../exchange-api-keys.service';
-import {
- enrollExchangeApiKeysCommandFixture,
- enrollExchangeApiKeysResponseFixture,
- retrieveExchangeApiKeysResponseFixture,
- supportedExchangesResponseFixture,
- TOKEN,
-} from './exchange-api-keys.fixtures';
-
-describe('ExchangeApiKeysService', () => {
- let service: ExchangeApiKeysService;
- let reputationOracleMock: Partial;
-
- beforeEach(async () => {
- reputationOracleMock = {
- enrollExchangeApiKeys: jest.fn(),
- deleteExchangeApiKeys: jest.fn(),
- retrieveExchangeApiKeys: jest.fn(),
- getStakeSummary: jest.fn(),
- supportedExchanges: jest.fn(),
- };
-
- const module: TestingModule = await Test.createTestingModule({
- providers: [
- ExchangeApiKeysService,
- { provide: ReputationOracleGateway, useValue: reputationOracleMock },
- ],
- }).compile();
-
- service = module.get(ExchangeApiKeysService);
- });
-
- it('should be defined', () => {
- expect(service).toBeDefined();
- });
-
- describe('enroll', () => {
- it('should enroll exchange API keys and return id', async () => {
- (
- reputationOracleMock.enrollExchangeApiKeys as jest.Mock
- ).mockResolvedValue(enrollExchangeApiKeysResponseFixture);
- const result = await service.enroll(enrollExchangeApiKeysCommandFixture);
- expect(reputationOracleMock.enrollExchangeApiKeys).toHaveBeenCalledWith(
- enrollExchangeApiKeysCommandFixture,
- );
- expect(result).toEqual(enrollExchangeApiKeysResponseFixture);
- });
- });
-
- describe('delete', () => {
- it('should delete exchange API keys', async () => {
- (
- reputationOracleMock.deleteExchangeApiKeys as jest.Mock
- ).mockResolvedValue(undefined);
- await service.delete(TOKEN);
- expect(reputationOracleMock.deleteExchangeApiKeys).toHaveBeenCalledWith(
- TOKEN,
- );
- });
- });
-
- describe('retrieve', () => {
- it('should retrieve exchange API keys', async () => {
- (
- reputationOracleMock.retrieveExchangeApiKeys as jest.Mock
- ).mockResolvedValue(retrieveExchangeApiKeysResponseFixture);
- const result = await service.retrieve(TOKEN);
- expect(reputationOracleMock.retrieveExchangeApiKeys).toHaveBeenCalledWith(
- TOKEN,
- );
- expect(result).toEqual(retrieveExchangeApiKeysResponseFixture);
- });
- });
-
- describe('getSupportedExchanges', () => {
- it('should retrieve supported exchanges', async () => {
- (reputationOracleMock.supportedExchanges as jest.Mock).mockResolvedValue(
- supportedExchangesResponseFixture,
- );
- const result = await service.getSupportedExchanges(TOKEN);
- expect(reputationOracleMock.supportedExchanges).toHaveBeenCalledWith(
- TOKEN,
- );
- expect(result).toEqual(supportedExchangesResponseFixture);
- });
- });
-});
diff --git a/packages/apps/human-app/server/src/modules/job-assignment/job-assignment.controller.ts b/packages/apps/human-app/server/src/modules/job-assignment/job-assignment.controller.ts
index 79e3a48663..56f6661b91 100644
--- a/packages/apps/human-app/server/src/modules/job-assignment/job-assignment.controller.ts
+++ b/packages/apps/human-app/server/src/modules/job-assignment/job-assignment.controller.ts
@@ -8,7 +8,6 @@ import {
Post,
Query,
Request,
- ForbiddenException,
} from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { RequestWithUser } from '../../common/interfaces/jwt';
@@ -43,11 +42,6 @@ export class JobAssignmentController {
@Body() jobAssignmentDto: JobAssignmentDto,
@Request() req: RequestWithUser,
): Promise {
- // Require stake eligibility
- if (!req.user?.is_stake_eligible) {
- throw new ForbiddenException('Stake requirement not met');
- }
-
const jobAssignmentCommand = this.mapper.map(
jobAssignmentDto,
JobAssignmentDto,
@@ -65,16 +59,6 @@ export class JobAssignmentController {
@Query() jobsAssignmentParamsDto: JobsFetchParamsDto,
@Request() req: RequestWithUser,
): Promise {
- // Require stake eligibility
- if (!req.user?.is_stake_eligible) {
- return {
- page: 0,
- page_size: 1,
- total_pages: 1,
- total_results: 0,
- results: [],
- };
- }
const jobsAssignmentParamsCommand = this.mapper.map(
jobsAssignmentParamsDto,
JobsFetchParamsDto,
@@ -94,10 +78,6 @@ export class JobAssignmentController {
@Body() dto: ResignJobDto,
@Request() req: RequestWithUser,
) {
- // Require stake eligibility
- if (!req.user?.is_stake_eligible) {
- throw new ForbiddenException('Stake requirement not met');
- }
const command = this.mapper.map(dto, ResignJobDto, ResignJobCommand);
command.token = req.token;
return this.service.resignJob(command);
@@ -112,10 +92,6 @@ export class JobAssignmentController {
@Body() dto: RefreshJobDto,
@Request() req: RequestWithUser,
) {
- // Require stake eligibility
- if (!req.user?.is_stake_eligible) {
- throw new ForbiddenException('Stake requirement not met');
- }
const command = new JobsFetchParamsCommand();
command.oracleAddress = dto.oracle_address;
command.token = req.token;
diff --git a/packages/apps/human-app/server/src/modules/job-assignment/spec/job-assignment.controller.spec.ts b/packages/apps/human-app/server/src/modules/job-assignment/spec/job-assignment.controller.spec.ts
index f810cce1ca..ae69ce4e12 100644
--- a/packages/apps/human-app/server/src/modules/job-assignment/spec/job-assignment.controller.spec.ts
+++ b/packages/apps/human-app/server/src/modules/job-assignment/spec/job-assignment.controller.spec.ts
@@ -26,7 +26,6 @@ import {
TOKEN,
} from './job-assignment.fixtures';
import { jobAssignmentServiceMock } from './job-assignment.service.mock';
-import { ForbiddenException } from '@nestjs/common';
const httpServiceMock = {
request: jest.fn().mockImplementation((options) => {
@@ -78,7 +77,6 @@ describe('JobAssignmentController', () => {
const command: JobAssignmentCommand = jobAssignmentCommandFixture;
await controller.assignJob(dto, {
token: jobAssignmentToken,
- user: { is_stake_eligible: true },
} as RequestWithUser);
expect(jobAssignmentService.processJobAssignment).toHaveBeenCalledWith(
command,
@@ -90,85 +88,32 @@ describe('JobAssignmentController', () => {
const command: JobAssignmentCommand = jobAssignmentCommandFixture;
const result = await controller.assignJob(dto, {
token: jobAssignmentToken,
- user: { is_stake_eligible: true },
} as RequestWithUser);
expect(result).toEqual(
jobAssignmentServiceMock.processJobAssignment(command),
);
});
- it('should throw ForbiddenException if user is not stake eligible in assignJob', async () => {
- const dto: JobAssignmentDto = jobAssignmentDtoFixture;
- await expect(
- controller.assignJob(dto, {
- token: jobAssignmentToken,
- user: { is_stake_eligible: false },
- } as RequestWithUser),
- ).rejects.toThrow(new ForbiddenException('Stake requirement not met'));
- });
-
it('should call service processGetAssignedJobs method with proper fields set', async () => {
const dto: JobsFetchParamsDto = jobsFetchParamsDtoFixture;
const command: JobsFetchParamsCommand = jobsFetchParamsCommandFixture;
await controller.getAssignedJobs(dto, {
token: jobAssignmentToken,
- user: { is_stake_eligible: true },
} as RequestWithUser);
expect(jobAssignmentService.processGetAssignedJobs).toHaveBeenCalledWith(
command,
);
});
- it('should return empty results if user is not stake eligible in getAssignedJobs', async () => {
- const dto: JobsFetchParamsDto = jobsFetchParamsDtoFixture;
- const result = await controller.getAssignedJobs(dto, {
- token: jobAssignmentToken,
- user: { is_stake_eligible: false },
- } as RequestWithUser);
- expect(result).toEqual({
- page: 0,
- page_size: 1,
- total_pages: 1,
- total_results: 0,
- results: [],
- });
- });
-
it('should call service refreshAssigments method with proper fields set', async () => {
const dto: RefreshJobDto = refreshJobDtoFixture;
await controller.refreshAssigments(dto, {
token: jobAssignmentToken,
- user: { is_stake_eligible: true },
} as RequestWithUser);
expect(jobAssignmentService.updateAssignmentsCache).toHaveBeenCalledWith({
oracleAddress: EXCHANGE_ORACLE_ADDRESS,
token: TOKEN,
});
});
-
- it('should throw ForbiddenException if user is not stake eligible in refreshAssigments', async () => {
- const dto: RefreshJobDto = refreshJobDtoFixture;
- await expect(
- controller.refreshAssigments(dto, {
- token: jobAssignmentToken,
- user: { is_stake_eligible: false },
- } as RequestWithUser),
- ).rejects.toThrow(new ForbiddenException('Stake requirement not met'));
- });
- });
-
- describe('resignAssigment', () => {
- it('should throw ForbiddenException if user is not stake eligible in resignAssigment', async () => {
- const dto = { assignment_id: '1' };
- await expect(
- controller.resignAssigment(
- dto as any,
- {
- token: jobAssignmentToken,
- user: { is_stake_eligible: false },
- } as RequestWithUser,
- ),
- ).rejects.toThrow(new ForbiddenException('Stake requirement not met'));
- });
});
});
diff --git a/packages/apps/human-app/server/src/modules/jobs-discovery/jobs-discovery.controller.ts b/packages/apps/human-app/server/src/modules/jobs-discovery/jobs-discovery.controller.ts
index d2908c1c28..50650861b6 100644
--- a/packages/apps/human-app/server/src/modules/jobs-discovery/jobs-discovery.controller.ts
+++ b/packages/apps/human-app/server/src/modules/jobs-discovery/jobs-discovery.controller.ts
@@ -49,17 +49,6 @@ export class JobsDiscoveryController {
);
}
- // Require stake eligibility
- if (!req.user?.is_stake_eligible) {
- return {
- page: 0,
- page_size: 1,
- total_pages: 1,
- total_results: 0,
- results: [],
- };
- }
-
const jobsDiscoveryParamsCommand: JobsDiscoveryParamsCommand =
this.mapper.map(
jobsDiscoveryParamsDto,
diff --git a/packages/apps/human-app/server/src/modules/jobs-discovery/spec/jobs-discovery.controller.spec.ts b/packages/apps/human-app/server/src/modules/jobs-discovery/spec/jobs-discovery.controller.spec.ts
index 9b73a31176..c1780290c0 100644
--- a/packages/apps/human-app/server/src/modules/jobs-discovery/spec/jobs-discovery.controller.spec.ts
+++ b/packages/apps/human-app/server/src/modules/jobs-discovery/spec/jobs-discovery.controller.spec.ts
@@ -14,7 +14,6 @@ import {
dtoFixture,
jobsDiscoveryParamsCommandFixture,
responseFixture,
- jobDiscoveryToken,
} from './jobs-discovery.fixtures';
import { jobsDiscoveryServiceMock } from './jobs-discovery.service.mock';
@@ -74,7 +73,7 @@ describe('JobsDiscoveryController', () => {
const dto = dtoFixture;
const command = jobsDiscoveryParamsCommandFixture;
await controller.getJobs(dto, {
- user: { qualifications: [], is_stake_eligible: true },
+ user: { qualifications: [] },
token: command.token,
} as any);
command.data.qualifications = [];
@@ -93,20 +92,5 @@ describe('JobsDiscoveryController', () => {
);
(configServiceMock as any).jobsDiscoveryFlag = true;
});
-
- it('should return empty results if user is not stake eligible', async () => {
- const dto = dtoFixture;
- const result = await controller.getJobs(dto, {
- user: { qualifications: [], is_stake_eligible: false },
- token: jobDiscoveryToken,
- } as any);
- expect(result).toEqual({
- page: 0,
- page_size: 1,
- total_pages: 1,
- total_results: 0,
- results: [],
- });
- });
});
});
diff --git a/packages/apps/human-app/server/src/modules/staking/model/staking.model.ts b/packages/apps/human-app/server/src/modules/staking/model/staking.model.ts
deleted file mode 100644
index a6b6f9d95b..0000000000
--- a/packages/apps/human-app/server/src/modules/staking/model/staking.model.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
-
-export class StakeSummaryResponse {
- @ApiProperty()
- exchange_stake: number;
-
- @ApiProperty({ name: 'on_chain_stake' })
- on_chain_stake: number;
-
- @ApiPropertyOptional()
- exchange_error?: string | null;
-
- @ApiPropertyOptional()
- on_chain_error?: string | null;
-}
-
-export class StakeConfigResponse {
- min_threshold: number;
- eligibility_enabled: boolean;
-}
diff --git a/packages/apps/human-app/server/src/modules/staking/spec/staking.controller.spec.ts b/packages/apps/human-app/server/src/modules/staking/spec/staking.controller.spec.ts
deleted file mode 100644
index f92224d522..0000000000
--- a/packages/apps/human-app/server/src/modules/staking/spec/staking.controller.spec.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { classes } from '@automapper/classes';
-import { AutomapperModule } from '@automapper/nestjs';
-import { Test, TestingModule } from '@nestjs/testing';
-import { RequestWithUser } from '../../../common/interfaces/jwt';
-import { TOKEN } from './staking.fixtures';
-import { StakingService } from '../staking.service';
-import { StakingController } from '../staking.controller';
-import { stakingServiceMock } from './staking.service.mock';
-
-describe('StakingController', () => {
- let controller: StakingController;
- let service: StakingService;
-
- beforeEach(async () => {
- const module: TestingModule = await Test.createTestingModule({
- controllers: [StakingController],
- imports: [
- AutomapperModule.forRoot({
- strategyInitializer: classes(),
- }),
- ],
- providers: [
- StakingService,
- {
- provide: StakingService,
- useValue: stakingServiceMock,
- },
- ],
- })
- .overrideProvider(StakingService)
- .useValue(stakingServiceMock)
- .compile();
-
- controller = module.get(StakingController);
- service = module.get(StakingService);
- });
-
- it('should be defined', () => {
- expect(controller).toBeDefined();
- });
-
- describe('getStakeSummary', () => {
- it('should call service.getStakeSummary with token and return response', async () => {
- const req: RequestWithUser = { token: TOKEN } as RequestWithUser;
- const result = await controller.getStakeSummary(req);
- expect(service.getStakeSummary).toHaveBeenCalledWith(TOKEN);
- expect(result).toEqual(stakingServiceMock.getStakeSummary(TOKEN));
- });
- });
-});
diff --git a/packages/apps/human-app/server/src/modules/staking/spec/staking.fixtures.ts b/packages/apps/human-app/server/src/modules/staking/spec/staking.fixtures.ts
deleted file mode 100644
index 93699eef96..0000000000
--- a/packages/apps/human-app/server/src/modules/staking/spec/staking.fixtures.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export const TOKEN = 'test_user_token';
-
-export const stakeSummaryResponseFixture = {
- exchange_stake: '1000',
- on_chain_stake: '500',
-};
-
-export const stakeConfigResponseFixture = {
- min_threshold: '1000',
- eligibility_enabled: true,
-};
diff --git a/packages/apps/human-app/server/src/modules/staking/spec/staking.service.mock.ts b/packages/apps/human-app/server/src/modules/staking/spec/staking.service.mock.ts
deleted file mode 100644
index 582f931236..0000000000
--- a/packages/apps/human-app/server/src/modules/staking/spec/staking.service.mock.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import {
- stakeConfigResponseFixture,
- stakeSummaryResponseFixture,
-} from './staking.fixtures';
-
-export const stakingServiceMock = {
- getStakeSummary: jest.fn().mockReturnValue(stakeSummaryResponseFixture),
- getStakeConfig: jest.fn().mockReturnValue(stakeConfigResponseFixture),
-};
diff --git a/packages/apps/human-app/server/src/modules/staking/spec/staking.service.spec.ts b/packages/apps/human-app/server/src/modules/staking/spec/staking.service.spec.ts
deleted file mode 100644
index ad97851205..0000000000
--- a/packages/apps/human-app/server/src/modules/staking/spec/staking.service.spec.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import { Test, TestingModule } from '@nestjs/testing';
-import { ReputationOracleGateway } from '../../../integrations/reputation-oracle/reputation-oracle.gateway';
-import { StakingService } from '../staking.service';
-import {
- stakeConfigResponseFixture,
- stakeSummaryResponseFixture,
- TOKEN,
-} from './staking.fixtures';
-
-describe('StakingService', () => {
- let service: StakingService;
- let reputationOracleMock: Partial;
-
- beforeEach(async () => {
- reputationOracleMock = {
- getStakeSummary: jest.fn(),
- getStakeConfig: jest.fn(),
- };
-
- const module: TestingModule = await Test.createTestingModule({
- providers: [
- StakingService,
- { provide: ReputationOracleGateway, useValue: reputationOracleMock },
- ],
- }).compile();
-
- service = module.get(StakingService);
- });
-
- it('should be defined', () => {
- expect(service).toBeDefined();
- });
-
- describe('getStakeSummary', () => {
- it('should retrieve stake summary', async () => {
- (reputationOracleMock.getStakeSummary as jest.Mock).mockResolvedValue(
- stakeSummaryResponseFixture,
- );
- const result = await service.getStakeSummary(TOKEN);
- expect(reputationOracleMock.getStakeSummary).toHaveBeenCalledWith(TOKEN);
- expect(result).toEqual(stakeSummaryResponseFixture);
- });
- });
-
- describe('getStakeConfig', () => {
- it('should retrieve stake config', async () => {
- (reputationOracleMock.getStakeConfig as jest.Mock).mockResolvedValue(
- stakeConfigResponseFixture,
- );
- const result = await service.getStakeConfig();
- expect(reputationOracleMock.getStakeConfig).toHaveBeenCalledWith();
- expect(result).toEqual(stakeConfigResponseFixture);
- });
- });
-});
diff --git a/packages/apps/human-app/server/src/modules/staking/staking.controller.ts b/packages/apps/human-app/server/src/modules/staking/staking.controller.ts
deleted file mode 100644
index f8bcdae90f..0000000000
--- a/packages/apps/human-app/server/src/modules/staking/staking.controller.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Mapper } from '@automapper/core';
-import { InjectMapper } from '@automapper/nestjs';
-import { Controller, Get, Request } from '@nestjs/common';
-import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
-import { RequestWithUser } from '../../common/interfaces/jwt';
-import { StakingService } from './staking.service';
-import { StakeSummaryResponse } from './model/staking.model';
-
-@ApiTags('Staking')
-@ApiBearerAuth()
-@Controller('/staking')
-export class StakingController {
- constructor(
- private readonly service: StakingService,
- @InjectMapper() private readonly mapper: Mapper,
- ) {}
-
- @ApiOperation({ summary: 'Get exchange API keys stake summary' })
- @Get('/summary')
- async getStakeSummary(
- @Request() req: RequestWithUser,
- ): Promise {
- return this.service.getStakeSummary(req.token);
- }
-}
diff --git a/packages/apps/human-app/server/src/modules/staking/staking.module.ts b/packages/apps/human-app/server/src/modules/staking/staking.module.ts
deleted file mode 100644
index 49bc184eda..0000000000
--- a/packages/apps/human-app/server/src/modules/staking/staking.module.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Module } from '@nestjs/common';
-import { StakingController } from './staking.controller';
-import { StakingService } from './staking.service';
-import { ReputationOracleModule } from '../../integrations/reputation-oracle/reputation-oracle.module';
-
-@Module({
- imports: [ReputationOracleModule],
- controllers: [StakingController],
- providers: [StakingService],
- exports: [StakingService],
-})
-export class StakingModule {}
diff --git a/packages/apps/human-app/server/src/modules/staking/staking.service.ts b/packages/apps/human-app/server/src/modules/staking/staking.service.ts
deleted file mode 100644
index 820c8f9361..0000000000
--- a/packages/apps/human-app/server/src/modules/staking/staking.service.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { Injectable } from '@nestjs/common';
-import { ReputationOracleGateway } from '../../integrations/reputation-oracle/reputation-oracle.gateway';
-import {
- StakeConfigResponse,
- StakeSummaryResponse,
-} from './model/staking.model';
-
-@Injectable()
-export class StakingService {
- constructor(private readonly reputationOracle: ReputationOracleGateway) {}
-
- getStakeSummary(token: string): Promise {
- return this.reputationOracle.getStakeSummary(token);
- }
-
- getStakeConfig(): Promise {
- return this.reputationOracle.getStakeConfig();
- }
-}
diff --git a/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.controller.spec.ts b/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.controller.spec.ts
index c8df57939b..24b68667fc 100644
--- a/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.controller.spec.ts
+++ b/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.controller.spec.ts
@@ -3,20 +3,14 @@ import { ConfigModule } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { EnvironmentConfigService } from '../../common/config/environment-config.service';
-import { StakingService } from '../staking/staking.service';
import { UiConfigurationController } from './ui-configuration.controller';
process.env.CHAIN_IDS_ENABLED = '80002,11155111';
describe('UiConfigurationController', () => {
let controller: UiConfigurationController;
- let stakingServiceMock: Pick, 'getStakeConfig'>;
beforeAll(async () => {
- stakingServiceMock = {
- getStakeConfig: jest.fn(),
- };
-
const module: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
@@ -24,13 +18,7 @@ describe('UiConfigurationController', () => {
isGlobal: true,
}),
],
- providers: [
- EnvironmentConfigService,
- {
- provide: StakingService,
- useValue: stakingServiceMock,
- },
- ],
+ providers: [EnvironmentConfigService],
controllers: [UiConfigurationController],
}).compile();
@@ -40,16 +28,10 @@ describe('UiConfigurationController', () => {
});
it('should return proper config', async () => {
- stakingServiceMock.getStakeConfig.mockResolvedValueOnce({
- eligibility_enabled: true,
- min_threshold: Math.random(),
- });
-
const result = await controller.getConfig();
expect(result.chainIdsEnabled).toEqual([
ChainId.POLYGON_AMOY,
ChainId.SEPOLIA,
]);
- expect(result.stakingEligibilityEnabled).toBe(true);
});
});
diff --git a/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.controller.ts b/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.controller.ts
index 0c4107eb01..0ed982fe27 100644
--- a/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.controller.ts
+++ b/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.controller.ts
@@ -3,7 +3,6 @@ import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
import { EnvironmentConfigService } from '../../common/config/environment-config.service';
import { Public } from '../../common/decorators';
import { UiConfigResponseDto } from './ui-configuration.dto';
-import { StakingService } from '../staking/staking.service';
@Controller()
@Public()
@@ -11,7 +10,6 @@ import { StakingService } from '../staking/staking.service';
export class UiConfigurationController {
constructor(
private readonly environmentConfigService: EnvironmentConfigService,
- private readonly stakingService: StakingService,
) {}
@ApiOperation({ summary: 'Retrieve UI configuration' })
@@ -19,15 +17,11 @@ export class UiConfigurationController {
type: UiConfigResponseDto,
description: 'UI Configuration object',
})
- @Header('Cache-Control', 'public, max-age=600')
+ @Header('Cache-Control', 'public, max-age=3600')
@Get('/ui-config')
public async getConfig(): Promise {
- const stakingRequirementConfig = await this.stakingService.getStakeConfig();
-
return {
chainIdsEnabled: this.environmentConfigService.chainIdsEnabled,
- stakingEligibilityEnabled: stakingRequirementConfig.eligibility_enabled,
- minThreshold: stakingRequirementConfig.min_threshold,
};
}
}
diff --git a/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.dto.ts b/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.dto.ts
index 6633c8cf49..864757461b 100644
--- a/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.dto.ts
+++ b/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.dto.ts
@@ -9,15 +9,4 @@ export class UiConfigResponseDto {
enumName: 'ChainId',
})
chainIdsEnabled: ChainId[];
-
- @ApiProperty({
- description:
- 'Indicated if stake eligibility check enabled on app & oracles',
- })
- stakingEligibilityEnabled: boolean;
-
- @ApiProperty({
- description: 'Minimum staking threshold required for eligibility',
- })
- minThreshold: number;
}
diff --git a/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.module.ts b/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.module.ts
index 2e99816674..d14d2d39bd 100644
--- a/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.module.ts
+++ b/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.module.ts
@@ -1,9 +1,7 @@
import { Module } from '@nestjs/common';
import { UiConfigurationController } from './ui-configuration.controller';
-import { StakingModule } from '../staking/staking.module';
@Module({
- imports: [StakingModule],
controllers: [UiConfigurationController],
})
export class UiConfigurationModule {}
diff --git a/packages/apps/reputation-oracle/server/src/app.module.ts b/packages/apps/reputation-oracle/server/src/app.module.ts
index a57b33c078..a064f1b5c9 100644
--- a/packages/apps/reputation-oracle/server/src/app.module.ts
+++ b/packages/apps/reputation-oracle/server/src/app.module.ts
@@ -14,13 +14,11 @@ import { AbuseModule } from './modules/abuse';
import { AuthModule } from './modules/auth';
import { CronJobModule } from './modules/cron-job';
import { EscrowCompletionModule } from './modules/escrow-completion';
-import { ExchangeApiKeysModule } from './modules/exchange-api-keys';
import { HealthModule } from './modules/health';
import { KycModule } from './modules/kyc';
import { NDAModule } from './modules/nda';
import { QualificationModule } from './modules/qualification';
import { ReputationModule } from './modules/reputation';
-import { StakingModule } from './modules/staking';
import { UserModule } from './modules/user';
import {
IncomingWebhookModule,
@@ -74,8 +72,6 @@ import Environment from './utils/environment';
CronJobModule,
UserModule,
NDAModule,
- ExchangeApiKeysModule,
- StakingModule,
EscrowCompletionModule,
HealthModule,
KycModule,
diff --git a/packages/apps/reputation-oracle/server/src/common/constants/index.ts b/packages/apps/reputation-oracle/server/src/common/constants/index.ts
index 02d82593be..1bacfb11fd 100644
--- a/packages/apps/reputation-oracle/server/src/common/constants/index.ts
+++ b/packages/apps/reputation-oracle/server/src/common/constants/index.ts
@@ -12,20 +12,3 @@ export const RESEND_EMAIL_VERIFICATION_PATH =
export const LOGOUT_PATH = '/auth/logout';
export const BACKOFF_INTERVAL_SECONDS = 120;
-
-export enum SupportedExchange {
- MEXC = 'mexc',
- GATE = 'gate',
-}
-
-export type SupportedExchangeInfo = {
- name: SupportedExchange;
- displayName: string;
-};
-
-export const SUPPORTED_EXCHANGES_INFO: readonly SupportedExchangeInfo[] = [
- { name: SupportedExchange.MEXC, displayName: 'MEXC' },
- { name: SupportedExchange.GATE, displayName: 'Gate' },
-] as const;
-
-export const DEFAULT_TIMEOUT_MS = 5000;
diff --git a/packages/apps/reputation-oracle/server/src/config/config.module.ts b/packages/apps/reputation-oracle/server/src/config/config.module.ts
index 1adb75e47f..a06e59563c 100644
--- a/packages/apps/reputation-oracle/server/src/config/config.module.ts
+++ b/packages/apps/reputation-oracle/server/src/config/config.module.ts
@@ -13,7 +13,6 @@ import { ReputationConfigService } from './reputation-config.service';
import { S3ConfigService } from './s3-config.service';
import { ServerConfigService } from './server-config.service';
import { SlackConfigService } from './slack-config.service';
-import { StakingConfigService } from './staking-config.service';
import { Web3ConfigService } from './web3-config.service';
@Global()
@@ -29,7 +28,6 @@ import { Web3ConfigService } from './web3-config.service';
NDAConfigService,
PGPConfigService,
ReputationConfigService,
- StakingConfigService,
S3ConfigService,
ServerConfigService,
SlackConfigService,
@@ -45,7 +43,6 @@ import { Web3ConfigService } from './web3-config.service';
NDAConfigService,
PGPConfigService,
ReputationConfigService,
- StakingConfigService,
S3ConfigService,
ServerConfigService,
SlackConfigService,
diff --git a/packages/apps/reputation-oracle/server/src/config/env-schema.ts b/packages/apps/reputation-oracle/server/src/config/env-schema.ts
index c2a55a98d8..66eed695c5 100644
--- a/packages/apps/reputation-oracle/server/src/config/env-schema.ts
+++ b/packages/apps/reputation-oracle/server/src/config/env-schema.ts
@@ -82,21 +82,6 @@ export const envValidator = Joi.object({
KYC_BASE_URL: Joi.string().uri({ scheme: ['http', 'https'] }),
// Human App
HUMAN_APP_SECRET_KEY: Joi.string().required(),
- // Staking configuration
- STAKING_ASSET: Joi.string().description(
- 'Asset symbol to check for staking (default HMT)',
- ),
- STAKING_MIN_THRESHOLD: Joi.number()
- .min(0)
- .description('Minimum asset amount to qualify as staked'),
- STAKING_TIMEOUT_MS: Joi.number()
- .integer()
- .min(100)
- .description('HTTP timeout for exchange staking checks in ms'),
- STAKING_ELIGIBILITY_ENABLED: Joi.string()
- .valid('true', 'false')
- .default('false')
- .description('Enable or disable staking eligibility checks'),
// Slack notifications
ABUSE_SLACK_WEBHOOK_URL: Joi.string()
.uri({ scheme: ['http', 'https'] })
diff --git a/packages/apps/reputation-oracle/server/src/config/index.ts b/packages/apps/reputation-oracle/server/src/config/index.ts
index 5d1318cd56..358c303f52 100644
--- a/packages/apps/reputation-oracle/server/src/config/index.ts
+++ b/packages/apps/reputation-oracle/server/src/config/index.ts
@@ -9,7 +9,6 @@ export { KycConfigService } from './kyc-config.service';
export { NDAConfigService } from './nda-config.service';
export { PGPConfigService } from './pgp-config.service';
export { ReputationConfigService } from './reputation-config.service';
-export { StakingConfigService } from './staking-config.service';
export { S3ConfigService } from './s3-config.service';
export { ServerConfigService } from './server-config.service';
export { Web3ConfigService, Web3Network } from './web3-config.service';
diff --git a/packages/apps/reputation-oracle/server/src/config/staking-config.service.ts b/packages/apps/reputation-oracle/server/src/config/staking-config.service.ts
deleted file mode 100644
index 174d3ed5d1..0000000000
--- a/packages/apps/reputation-oracle/server/src/config/staking-config.service.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { Injectable } from '@nestjs/common';
-import { ConfigService } from '@nestjs/config';
-
-@Injectable()
-export class StakingConfigService {
- constructor(private readonly configService: ConfigService) {}
-
- /**
- * Default asset symbol to check for staking eligibility.
- * Default: 'HMT'
- */
- get asset(): string {
- return this.configService.get('STAKING_ASSET', 'HMT');
- }
-
- /**
- * Minimum threshold (asset units) required for staking eligibility.
- * Default: 1000
- */
- get minThreshold(): number {
- return Number(this.configService.get('STAKING_MIN_THRESHOLD')) || 1000;
- }
-
- /**
- * Optional per-exchange HTTP timeout, in milliseconds.
- * Default: 2000
- */
- get timeoutMs(): number {
- return Number(this.configService.get('STAKING_TIMEOUT_MS')) || 2000;
- }
-
- /**
- * Feature flag to enable/disable staking eligibility enforcement.
- * When disabled, eligibility will be treated as true unconditionally.
- * Default: false
- */
- get eligibilityEnabled(): boolean {
- return (
- this.configService.get('STAKING_ELIGIBILITY_ENABLED', 'false') ===
- 'true'
- );
- }
-}
diff --git a/packages/apps/reputation-oracle/server/src/database/database.module.ts b/packages/apps/reputation-oracle/server/src/database/database.module.ts
index c8bb6dc8f3..093c5d7abf 100644
--- a/packages/apps/reputation-oracle/server/src/database/database.module.ts
+++ b/packages/apps/reputation-oracle/server/src/database/database.module.ts
@@ -13,7 +13,6 @@ import { TokenEntity } from '@/modules/auth/token.entity';
import { CronJobEntity } from '@/modules/cron-job/cron-job.entity';
import { EscrowCompletionEntity } from '@/modules/escrow-completion/escrow-completion.entity';
import { EscrowPayoutsBatchEntity } from '@/modules/escrow-completion/escrow-payouts-batch.entity';
-import { ExchangeApiKeyEntity } from '@/modules/exchange-api-keys';
import { KycEntity } from '@/modules/kyc/kyc.entity';
import { QualificationEntity } from '@/modules/qualification/qualification.entity';
import { UserQualificationEntity } from '@/modules/qualification/user-qualification.entity';
@@ -72,7 +71,6 @@ import { TypeOrmLoggerModule, TypeOrmLoggerService } from './typeorm';
OutgoingWebhookEntity,
EscrowCompletionEntity,
EscrowPayoutsBatchEntity,
- ExchangeApiKeyEntity,
ReputationEntity,
TokenEntity,
UserEntity,
diff --git a/packages/apps/reputation-oracle/server/src/database/migrations/1783955400433-dropExchangeApiKeys.ts b/packages/apps/reputation-oracle/server/src/database/migrations/1783955400433-dropExchangeApiKeys.ts
new file mode 100644
index 0000000000..c369ba619d
--- /dev/null
+++ b/packages/apps/reputation-oracle/server/src/database/migrations/1783955400433-dropExchangeApiKeys.ts
@@ -0,0 +1,19 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+
+export class DropExchangeApiKeys1783955400433 implements MigrationInterface {
+ name = 'DropExchangeApiKeys1783955400433';
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ `ALTER TABLE "hmt"."exchange_api_keys" DROP CONSTRAINT "FK_96ee74195b058a1b55afc49f673"`,
+ );
+ await queryRunner.query(
+ `DROP INDEX "hmt"."IDX_96ee74195b058a1b55afc49f67"`,
+ );
+ await queryRunner.query(`DROP TABLE "hmt"."exchange_api_keys"`);
+ }
+
+ public async down(_queryRunner: QueryRunner): Promise {
+ // noop
+ }
+}
diff --git a/packages/apps/reputation-oracle/server/src/modules/auth/auth.module.ts b/packages/apps/reputation-oracle/server/src/modules/auth/auth.module.ts
index bcb7cd27c6..f613211882 100644
--- a/packages/apps/reputation-oracle/server/src/modules/auth/auth.module.ts
+++ b/packages/apps/reputation-oracle/server/src/modules/auth/auth.module.ts
@@ -4,10 +4,8 @@ import { JwtModule } from '@nestjs/jwt';
import { AuthConfigService } from '@/config';
import { HCaptchaModule } from '@/integrations/hcaptcha';
import { EmailModule } from '@/modules/email';
-import { ExchangeModule } from '@/modules/exchange';
-import { ExchangeApiKeysModule } from '@/modules/exchange-api-keys';
-import { StakingModule } from '@/modules/staking';
import { UserModule } from '@/modules/user';
+import { Web3Module } from '@/modules/web3';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
@@ -27,10 +25,8 @@ import { TokenRepository } from './token.repository';
},
}),
}),
+ Web3Module,
HCaptchaModule,
- ExchangeModule,
- ExchangeApiKeysModule,
- StakingModule,
EmailModule,
],
providers: [JwtHttpStrategy, AuthService, TokenRepository],
diff --git a/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.spec.ts b/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.spec.ts
index 3eebd54bb9..0de059e6bc 100644
--- a/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.spec.ts
+++ b/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.spec.ts
@@ -11,12 +11,10 @@ import { SignatureType, UserStatus, UserRole } from '@/common/enums';
import {
AuthConfigService,
NDAConfigService,
- StakingConfigService,
ServerConfigService,
Web3ConfigService,
} from '@/config';
import { EmailAction, EmailService } from '@/modules/email';
-import { StakingService } from '@/modules/staking';
import { SiteKeyRepository } from '@/modules/user';
import { UserEntity, UserRepository, UserService } from '@/modules/user';
import { generateOperator, generateWorkerUser } from '@/modules/user/fixtures';
@@ -43,12 +41,6 @@ const mockAuthConfigService: Omit = {
forgotPasswordExpiresIn: 86400000,
humanAppSecretKey: faker.string.alphanumeric({ length: 42 }),
};
-const mockStakingConfigService: Omit = {
- eligibilityEnabled: true,
- minThreshold: 100,
- asset: 'ETH',
- timeoutMs: 2000,
-};
const mockEmailService = createMock();
@@ -64,7 +56,6 @@ const mockSiteKeyRepository = createMock();
const mockTokenRepository = createMock();
const mockUserRepository = createMock();
const mockUserService = createMock();
-const mockStakingService = createMock();
describe('AuthService', () => {
let service: AuthService;
@@ -94,8 +85,6 @@ describe('AuthService', () => {
{ provide: UserRepository, useValue: mockUserRepository },
{ provide: UserService, useValue: mockUserService },
{ provide: Web3ConfigService, useValue: mockWeb3ConfigService },
- { provide: StakingService, useValue: mockStakingService },
- { provide: StakingConfigService, useValue: mockStakingConfigService },
],
}).compile();
@@ -635,7 +624,6 @@ describe('AuthService', () => {
wallet_address: user.evmAddress,
role: user.role,
kyc_status: user.kyc?.status,
- is_stake_eligible: true,
nda_signed: user.ndaSignedUrl === mockNdaConfigService.latestNdaUrl,
reputation_network: mockWeb3ConfigService.operatorAddress,
qualifications: user.userQualifications
@@ -645,11 +633,6 @@ describe('AuthService', () => {
: [],
};
- const spyOncheckStakeEligible = jest
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- .spyOn(service as any, 'checkStakeEligible')
- .mockImplementation();
- spyOncheckStakeEligible.mockResolvedValueOnce(true);
const spyOnGenerateTokens = jest
.spyOn(service, 'generateTokens')
.mockImplementation();
@@ -666,132 +649,10 @@ describe('AuthService', () => {
expectedJwtPayload,
);
- expect(spyOncheckStakeEligible).toHaveBeenCalledTimes(1);
-
- spyOncheckStakeEligible.mockRestore();
spyOnGenerateTokens.mockRestore();
});
});
- describe('checkStakeEligible', () => {
- it('returns true when feature flag disabled', async () => {
- const user = generateWorkerUser();
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (mockStakingConfigService as any).eligibilityEnabled = false;
-
- const result = await service['checkStakeEligible'](user);
-
- expect(result).toBe(true);
- expect(
- mockStakingService.getExchangeStakedBalance,
- ).not.toHaveBeenCalled();
- expect(mockStakingService.getOnChainStakedBalance).not.toHaveBeenCalled();
- });
-
- it('returns true when exchange balance meets threshold (no on-chain call)', async () => {
- const user = generateWorkerUser();
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (mockStakingConfigService as any).eligibilityEnabled = true;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (mockStakingConfigService as any).minThreshold = 1000;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (mockStakingConfigService as any).asset = 'HMT';
-
- mockStakingService.getExchangeStakedBalance.mockResolvedValueOnce(1500);
-
- const result = await service['checkStakeEligible'](user);
-
- expect(result).toBe(true);
- expect(mockStakingService.getExchangeStakedBalance).toHaveBeenCalledWith(
- user.id,
- );
- expect(mockStakingService.getOnChainStakedBalance).not.toHaveBeenCalled();
- });
-
- it('returns true when exchange balance below threshold but on-chain makes up the difference', async () => {
- const user = generateWorkerUser({
- privateKey: generateEthWallet().privateKey,
- });
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (mockStakingConfigService as any).eligibilityEnabled = true;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (mockStakingConfigService as any).minThreshold = 1000;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (mockStakingConfigService as any).asset = 'HMT';
-
- mockStakingService.getExchangeStakedBalance.mockResolvedValueOnce(400);
- mockStakingService.getOnChainStakedBalance.mockResolvedValueOnce(600);
-
- const result = await service['checkStakeEligible'](user);
-
- expect(result).toBe(true);
- expect(mockStakingService.getExchangeStakedBalance).toHaveBeenCalledWith(
- user.id,
- );
- expect(mockStakingService.getOnChainStakedBalance).toHaveBeenCalledTimes(
- 1,
- );
- expect(mockStakingService.getOnChainStakedBalance).toHaveBeenCalledWith(
- user.evmAddress,
- );
- });
-
- it('returns false when no exchange keys and on-chain stake below threshold', async () => {
- const user = generateWorkerUser({
- privateKey: generateEthWallet().privateKey,
- });
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (mockStakingConfigService as any).eligibilityEnabled = true;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (mockStakingConfigService as any).minThreshold = 1000;
-
- mockStakingService.getExchangeStakedBalance.mockResolvedValueOnce(0);
- mockStakingService.getOnChainStakedBalance.mockResolvedValueOnce(500);
-
- const result = await service['checkStakeEligible'](user);
-
- expect(result).toBe(false);
- expect(mockStakingService.getExchangeStakedBalance).toHaveBeenCalledWith(
- user.id,
- );
- expect(mockStakingService.getOnChainStakedBalance).toHaveBeenCalledTimes(
- 1,
- );
- });
-
- it('continues on exchange error and returns based on on-chain stake', async () => {
- const user = generateWorkerUser({
- privateKey: generateEthWallet().privateKey,
- });
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (mockStakingConfigService as any).eligibilityEnabled = true;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (mockStakingConfigService as any).minThreshold = 1000;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (mockStakingConfigService as any).asset = 'HMT';
-
- mockStakingService.getExchangeStakedBalance.mockRejectedValueOnce(
- new Error('network'),
- );
- mockStakingService.getOnChainStakedBalance.mockResolvedValueOnce(1200);
-
- const result = await service['checkStakeEligible'](user);
-
- expect(result).toBe(true);
- expect(mockStakingService.getExchangeStakedBalance).toHaveBeenCalledWith(
- user.id,
- );
- expect(mockStakingService.getOnChainStakedBalance).toHaveBeenCalledTimes(
- 1,
- );
- });
- });
-
describe('web3Auth', () => {
it('should generate jwt payload for operator', async () => {
const operator = generateOperator();
diff --git a/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.ts b/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.ts
index e32d92f809..09fb00c925 100644
--- a/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.ts
+++ b/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.ts
@@ -7,12 +7,10 @@ import {
AuthConfigService,
NDAConfigService,
ServerConfigService,
- StakingConfigService,
Web3ConfigService,
} from '@/config';
import logger from '@/logger';
import { EmailAction, EmailService } from '@/modules/email';
-import { StakingService } from '@/modules/staking';
import {
OperatorStatus,
SiteKeyRepository,
@@ -58,8 +56,6 @@ export class AuthService {
private readonly userRepository: UserRepository,
private readonly userService: UserService,
private readonly web3ConfigService: Web3ConfigService,
- private readonly stakingService: StakingService,
- private readonly stakingConfigService: StakingConfigService,
) {}
async signup(email: string, password: string): Promise {
@@ -233,8 +229,6 @@ export class AuthService {
hCaptchaSiteKey = hCaptchaSiteKeys[0].siteKey;
}
- const stakeEligible = await this.checkStakeEligible(userEntity);
-
const jwtPayload = {
email: userEntity.email,
status: userEntity.status,
@@ -244,7 +238,6 @@ export class AuthService {
kyc_status: userEntity.kyc?.status,
nda_signed:
userEntity.ndaSignedUrl === this.ndaConfigService.latestNdaUrl,
- is_stake_eligible: stakeEligible,
reputation_network: this.web3ConfigService.operatorAddress,
qualifications: userEntity.userQualifications
? userEntity.userQualifications.map(
@@ -257,44 +250,6 @@ export class AuthService {
return this.generateTokens(userEntity.id, jwtPayload);
}
- private async checkStakeEligible(
- userEntity: Web2UserEntity | UserEntity,
- ): Promise {
- if (!this.stakingConfigService.eligibilityEnabled) return true;
-
- let inspectedStakeAmount = 0;
-
- try {
- const exchangeBalance =
- await this.stakingService.getExchangeStakedBalance(userEntity.id);
- inspectedStakeAmount += exchangeBalance;
- } catch (err) {
- this.logger.warn('Failed to query exchange balance; continuing', {
- userId: userEntity.id,
- error: err,
- });
- }
-
- if (
- inspectedStakeAmount < this.stakingConfigService.minThreshold &&
- userEntity.evmAddress
- ) {
- try {
- const onChainStake = await this.stakingService.getOnChainStakedBalance(
- userEntity.evmAddress,
- );
- inspectedStakeAmount += onChainStake;
- } catch (err) {
- this.logger.warn('Failed to query on-chain stake; continuing', {
- userId: userEntity.id,
- error: err,
- });
- }
- }
-
- return inspectedStakeAmount >= this.stakingConfigService.minThreshold;
- }
-
async web3Auth(userEntity: OperatorUserEntity): Promise {
/**
* NOTE
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-key.entity.ts b/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-key.entity.ts
deleted file mode 100644
index 5a9252614e..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-key.entity.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Column, Entity, Index, ManyToOne } from 'typeorm';
-
-import { DATABASE_SCHEMA_NAME } from '@/common/constants';
-import { BaseEntity } from '@/database';
-import type { UserEntity } from '@/modules/user';
-
-@Entity({ schema: DATABASE_SCHEMA_NAME, name: 'exchange_api_keys' })
-@Index(['userId'], { unique: true })
-export class ExchangeApiKeyEntity extends BaseEntity {
- @Column('varchar', { length: 20 })
- exchangeName: string;
-
- @Column('varchar', { length: 1000 })
- apiKey: string;
-
- @Column('varchar', { length: 10000 })
- secretKey: string;
-
- @ManyToOne('UserEntity', { persistence: false, onDelete: 'CASCADE' })
- user?: UserEntity;
-
- @Column()
- userId: number;
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.controller.ts b/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.controller.ts
deleted file mode 100644
index df34ad7f2c..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.controller.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-import {
- Body,
- Controller,
- Delete,
- ForbiddenException,
- Get,
- HttpCode,
- Param,
- Post,
- Req,
- UseFilters,
-} from '@nestjs/common';
-import {
- ApiBearerAuth,
- ApiBody,
- ApiOperation,
- ApiResponse,
- ApiTags,
-} from '@nestjs/swagger';
-
-import type { RequestWithUser } from '@/common/types';
-import Environment from '@/utils/environment';
-
-import {
- EnrollExchangeApiKeysDto,
- EnrollExchangeApiKeysResponseDto,
- EnrolledApiKeyDto,
- ExchangeNameParamDto,
- SupportedExchangeDto,
-} from './exchange-api-keys.dto';
-import { ExchangeApiKeysControllerErrorsFilter } from './exchange-api-keys.error-filter';
-import { ExchangeApiKeyNotFoundError } from './exchange-api-keys.errors';
-import { ExchangeApiKeysRepository } from './exchange-api-keys.repository';
-import { ExchangeApiKeysService } from './exchange-api-keys.service';
-
-@ApiTags('Exchange API Keys')
-@ApiBearerAuth()
-@UseFilters(ExchangeApiKeysControllerErrorsFilter)
-@Controller('exchange-api-keys')
-export class ExchangeApiKeysController {
- constructor(
- private readonly exchangeApiKeysService: ExchangeApiKeysService,
- private readonly exchangeApiKeysRepository: ExchangeApiKeysRepository,
- ) {}
-
- @ApiOperation({
- summary: 'Retrieve enrolled exchange with api key',
- description: 'Returns the enrolled api key for exchange w/o secret key',
- })
- @ApiResponse({
- status: 200,
- type: EnrolledApiKeyDto,
- })
- @Get('/')
- async retrieveEnrolledApiKeys(
- @Req() request: RequestWithUser,
- ): Promise {
- const userId = request.user.id;
-
- const apiKey = await this.exchangeApiKeysService.retrieve(userId);
- if (!apiKey) {
- return null;
- }
-
- return {
- exchangeName: apiKey.exchangeName,
- apiKey: apiKey.apiKey,
- };
- }
-
- @ApiOperation({
- summary: 'Enroll API keys for exchange',
- description:
- 'Enrolls API keys for provided exchange. If keys already exist for exchange - updates them',
- })
- @ApiResponse({
- status: 200,
- description: 'Exchange API keys enrolled',
- type: EnrollExchangeApiKeysResponseDto,
- })
- @ApiBody({ type: EnrollExchangeApiKeysDto })
- @HttpCode(200)
- @Post('/:exchange_name')
- async enroll(
- @Req() request: RequestWithUser,
- @Param() params: ExchangeNameParamDto,
- @Body() data: EnrollExchangeApiKeysDto,
- ): Promise {
- const key = await this.exchangeApiKeysService.enroll({
- userId: request.user.id,
- exchangeName: params.exchangeName,
- apiKey: data.apiKey,
- secretKey: data.secretKey,
- });
-
- return { id: key.id };
- }
-
- @ApiOperation({
- summary: 'Delete API keys',
- })
- @ApiResponse({
- status: 204,
- description: 'Exchange API keys deleted',
- })
- @HttpCode(204)
- @Delete('/')
- async delete(@Req() request: RequestWithUser): Promise {
- await this.exchangeApiKeysRepository.deleteByUser(request.user.id);
- }
-
- @ApiOperation({
- summary: 'Retreive API keys for exchange',
- description:
- 'This functionality is purely for dev solely and works only in non-production environments',
- })
- @Get('/exchange')
- async retrieve(@Req() request: RequestWithUser): Promise {
- if (!Environment.isDevelopment()) {
- throw new ForbiddenException();
- }
-
- const apiKey = await this.exchangeApiKeysService.retrieve(request.user.id);
- if (!apiKey) {
- throw new ExchangeApiKeyNotFoundError(request.user.id);
- }
- return apiKey;
- }
-
- @ApiOperation({ summary: 'List supported exchanges' })
- @ApiResponse({ status: 200, type: SupportedExchangeDto, isArray: true })
- @Get('/supported')
- async getSupportedExchanges(): Promise {
- return this.exchangeApiKeysService.getSupportedExchanges();
- }
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.dto.ts b/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.dto.ts
deleted file mode 100644
index e9546e80d8..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.dto.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { ApiProperty } from '@nestjs/swagger';
-import { IsEnum, IsNotEmpty, IsString, MaxLength } from 'class-validator';
-
-import { SupportedExchange } from '@/common/constants';
-
-export class EnrollExchangeApiKeysDto {
- @ApiProperty({ name: 'api_key' })
- @IsString()
- @IsNotEmpty()
- @MaxLength(200)
- apiKey: string;
-
- @ApiProperty({ name: 'secret_key' })
- @IsString()
- @IsNotEmpty()
- @MaxLength(5000)
- secretKey: string;
-}
-
-export class ExchangeNameParamDto {
- @ApiProperty({
- name: 'exchange_name',
- enum: SupportedExchange,
- })
- @IsEnum(SupportedExchange)
- exchangeName: SupportedExchange;
-}
-
-export class EnrollExchangeApiKeysResponseDto {
- @ApiProperty()
- id: number;
-}
-
-export class EnrolledApiKeyDto {
- @ApiProperty({ name: 'exchange_name' })
- exchangeName: string;
-
- @ApiProperty({ name: 'api_key' })
- apiKey: string;
-}
-
-export class SupportedExchangeDto {
- @ApiProperty({ name: 'name' })
- name: string;
-
- @ApiProperty({ name: 'display_name' })
- displayName: string;
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.error-filter.ts b/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.error-filter.ts
deleted file mode 100644
index d4d629d673..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.error-filter.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import {
- ExceptionFilter,
- Catch,
- ArgumentsHost,
- HttpStatus,
-} from '@nestjs/common';
-import { Request, Response } from 'express';
-
-import logger from '@/logger';
-import { UserNotFoundError } from '@/modules/user';
-
-import {
- ExchangeApiKeyNotFoundError,
- IncompleteKeySuppliedError,
- KeyAuthorizationError,
-} from './exchange-api-keys.errors';
-import { ExchangeApiClientError } from '../exchange/errors';
-
-@Catch(
- UserNotFoundError,
- IncompleteKeySuppliedError,
- KeyAuthorizationError,
- ExchangeApiKeyNotFoundError,
-)
-export class ExchangeApiKeysControllerErrorsFilter implements ExceptionFilter {
- private readonly logger = logger.child({
- context: ExchangeApiKeysControllerErrorsFilter.name,
- });
-
- catch(exception: Error, host: ArgumentsHost) {
- const ctx = host.switchToHttp();
- const response = ctx.getResponse();
- const request = ctx.getRequest();
- let status = HttpStatus.INTERNAL_SERVER_ERROR;
-
- if (
- exception instanceof UserNotFoundError ||
- exception instanceof IncompleteKeySuppliedError ||
- exception instanceof KeyAuthorizationError
- ) {
- status = HttpStatus.UNPROCESSABLE_ENTITY;
- } else if (exception instanceof ExchangeApiClientError) {
- status = HttpStatus.SERVICE_UNAVAILABLE;
- } else if (exception instanceof ExchangeApiKeyNotFoundError) {
- status = HttpStatus.NOT_FOUND;
- }
-
- return response.status(status).json({
- message: exception.message,
- timestamp: new Date().toISOString(),
- path: request.url,
- });
- }
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.errors.ts b/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.errors.ts
deleted file mode 100644
index b16190ddd2..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.errors.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { BaseError } from '@/common/errors/base';
-
-export class ExchangeApiKeyNotFoundError extends BaseError {
- constructor(readonly userId: number) {
- super('Exchange API key not found');
- }
-}
-
-export class IncompleteKeySuppliedError extends BaseError {
- constructor(readonly exchangeName: string) {
- super('Incomplete credentials supplied for exchange');
- }
-}
-
-export class KeyAuthorizationError extends BaseError {
- constructor(readonly exchangeName: string) {
- super("Provided API key can't be authorized on exchange");
- }
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.module.ts b/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.module.ts
deleted file mode 100644
index 5d7a416588..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.module.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Module } from '@nestjs/common';
-
-import { EncryptionModule } from '@/modules/encryption';
-import { ExchangeModule } from '@/modules/exchange/exchange.module';
-import { UserModule } from '@/modules/user';
-
-import { ExchangeApiKeysController } from './exchange-api-keys.controller';
-import { ExchangeApiKeysRepository } from './exchange-api-keys.repository';
-import { ExchangeApiKeysService } from './exchange-api-keys.service';
-
-@Module({
- imports: [ExchangeModule, EncryptionModule, UserModule],
- providers: [ExchangeApiKeysRepository, ExchangeApiKeysService],
- controllers: [ExchangeApiKeysController],
- exports: [ExchangeApiKeysRepository, ExchangeApiKeysService],
-})
-export class ExchangeApiKeysModule {}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.repository.ts b/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.repository.ts
deleted file mode 100644
index ea0d264915..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.repository.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { Injectable } from '@nestjs/common';
-import { DataSource } from 'typeorm';
-
-import { BaseRepository } from '@/database';
-
-import { ExchangeApiKeyEntity } from './exchange-api-key.entity';
-
-@Injectable()
-export class ExchangeApiKeysRepository extends BaseRepository {
- constructor(dataSource: DataSource) {
- super(ExchangeApiKeyEntity, dataSource);
- }
-
- async findOneByUserId(userId: number): Promise {
- if (!userId) {
- throw new Error('Invalid arguments');
- }
- return this.findOne({
- where: { userId },
- });
- }
-
- async deleteByUser(userId: number): Promise {
- if (!userId) {
- throw new Error('userId is required');
- }
-
- await this.delete({ userId });
- }
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.service.spec.ts b/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.service.spec.ts
deleted file mode 100644
index 082d28dc67..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.service.spec.ts
+++ /dev/null
@@ -1,217 +0,0 @@
-import { createMock } from '@golevelup/ts-jest';
-import { Test, TestingModule } from '@nestjs/testing';
-
-import { SUPPORTED_EXCHANGES_INFO } from '@/common/constants';
-import { EncryptionConfigService } from '@/config/encryption-config.service';
-import { AesEncryptionService } from '@/modules/encryption/aes-encryption.service';
-import { mockEncryptionConfigService } from '@/modules/encryption/fixtures';
-import { ExchangeClientFactory } from '@/modules/exchange/exchange-client.factory';
-
-// eslint-disable-next-line import-x/order
-import { ExchangeApiKeysService } from './exchange-api-keys.service';
-import { UserEntity, UserNotFoundError, UserRepository } from '@/modules/user';
-
-import { ExchangeApiKeyEntity } from './exchange-api-key.entity';
-import { KeyAuthorizationError } from './exchange-api-keys.errors';
-import { ExchangeApiKeysRepository } from './exchange-api-keys.repository';
-import {
- generateExchangeApiKey,
- generateExchangeApiKeysData,
-} from './fixtures';
-import { ExchangeClient } from '../exchange/types';
-
-const mockUserRepository = createMock();
-const mockExchangeApiKeysRepository = createMock();
-const mockExchangeClient = createMock();
-const mockExchangeClientFactory = {
- create: jest.fn().mockReturnValue(mockExchangeClient),
-};
-
-describe('ExchangeApiKeysService', () => {
- let exchangeApiKeysService: ExchangeApiKeysService;
- let aesEncryptionService: AesEncryptionService;
-
- beforeAll(async () => {
- const module: TestingModule = await Test.createTestingModule({
- providers: [
- ExchangeApiKeysService,
- AesEncryptionService,
- { provide: UserRepository, useValue: mockUserRepository },
- {
- provide: ExchangeApiKeysRepository,
- useValue: mockExchangeApiKeysRepository,
- },
- {
- provide: EncryptionConfigService,
- useValue: mockEncryptionConfigService,
- },
- {
- provide: ExchangeClientFactory,
- useValue: mockExchangeClientFactory,
- },
- ],
- }).compile();
-
- exchangeApiKeysService = module.get(
- ExchangeApiKeysService,
- );
- aesEncryptionService =
- module.get(AesEncryptionService);
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- it('should be defined', () => {
- expect(exchangeApiKeysService).toBeDefined();
- });
-
- describe('enroll', () => {
- it.each([
- Object.assign(generateExchangeApiKeysData(), { userId: '' }),
- Object.assign(generateExchangeApiKeysData(), { apiKey: '' }),
- Object.assign(generateExchangeApiKeysData(), { secretKey: '' }),
- ])('should throw if required param is missing [%#]', async (input) => {
- let thrownError;
- try {
- await exchangeApiKeysService.enroll(input);
- } catch (error) {
- thrownError = error;
- }
-
- expect(thrownError.constructor).toBe(Error);
- expect(thrownError.message).toBe('Invalid arguments');
- });
-
- it('should throw if provided keys do not have required access', async () => {
- mockExchangeApiKeysRepository.findOneByUserId.mockResolvedValueOnce(null);
- mockExchangeClient.checkRequiredAccess.mockResolvedValueOnce(false);
-
- const input = generateExchangeApiKeysData();
-
- let thrownError;
- try {
- await exchangeApiKeysService.enroll(input);
- } catch (error) {
- thrownError = error;
- }
-
- expect(thrownError).toBeInstanceOf(KeyAuthorizationError);
- expect(thrownError.exchangeName).toBe(input.exchangeName);
- });
-
- it('should overwrite existing keys if user already has active ones', async () => {
- const input = generateExchangeApiKeysData();
- const existingKey = generateExchangeApiKey();
- mockExchangeApiKeysRepository.findOneByUserId.mockResolvedValueOnce(
- existingKey,
- );
- mockExchangeClient.checkRequiredAccess.mockResolvedValueOnce(true);
- mockUserRepository.findOneById.mockResolvedValueOnce({
- id: input.userId,
- } as UserEntity);
- mockExchangeApiKeysRepository.updateOne.mockImplementation(
- async (entity) => entity,
- );
-
- const updatedEntity = await exchangeApiKeysService.enroll(input);
-
- expect(mockExchangeApiKeysRepository.updateOne).toHaveBeenCalledWith(
- existingKey,
- );
- const [decryptedApiKey, decryptedSecretKey] = await Promise.all([
- aesEncryptionService.decrypt(updatedEntity.apiKey),
- aesEncryptionService.decrypt(updatedEntity.secretKey),
- ]);
- expect(decryptedApiKey.toString()).toBe(input.apiKey);
- expect(decryptedSecretKey.toString()).toBe(input.secretKey);
- });
-
- it('should throw if user not exists', async () => {
- mockExchangeApiKeysRepository.findOneByUserId.mockResolvedValueOnce(null);
- mockExchangeClient.checkRequiredAccess.mockResolvedValueOnce(true);
-
- mockUserRepository.findOneById.mockResolvedValueOnce(null);
-
- const input = generateExchangeApiKeysData();
-
- let thrownError;
- try {
- await exchangeApiKeysService.enroll(input);
- } catch (error) {
- thrownError = error;
- }
-
- expect(thrownError).toBeInstanceOf(UserNotFoundError);
- });
-
- it('should insert encrypted keys if data is valid', async () => {
- mockExchangeApiKeysRepository.findOneByUserId.mockResolvedValueOnce(null);
- mockExchangeClient.checkRequiredAccess.mockResolvedValueOnce(true);
- mockUserRepository.findOneById.mockResolvedValueOnce({
- id: 1,
- } as UserEntity);
-
- const input = generateExchangeApiKeysData();
-
- const entity = await exchangeApiKeysService.enroll(input);
-
- expect(entity.userId).toBe(input.userId);
- expect(entity.exchangeName).toBe(input.exchangeName);
- expect(entity.apiKey).not.toBe(input.apiKey);
- expect(entity.secretKey).not.toBe(input.secretKey);
-
- const [decryptedApiKey, decryptedSecretKey] = await Promise.all([
- aesEncryptionService.decrypt(entity.apiKey),
- aesEncryptionService.decrypt(entity.secretKey),
- ]);
-
- expect(decryptedApiKey.toString()).toBe(input.apiKey);
- expect(decryptedSecretKey.toString()).toBe(input.secretKey);
- });
- });
-
- describe('retrieve', () => {
- it('should return null if key not found for the user', async () => {
- const { userId } = generateExchangeApiKeysData();
- mockExchangeApiKeysRepository.findOneByUserId.mockResolvedValueOnce(null);
-
- const result = await exchangeApiKeysService.retrieve(userId);
- expect(result).toBeNull();
- });
-
- it('should return decrypted keys', async () => {
- const { userId, exchangeName, apiKey, secretKey } =
- generateExchangeApiKeysData();
-
- const [encryptedApiKey, encryptedSecretKey] = await Promise.all([
- aesEncryptionService.encrypt(Buffer.from(apiKey)),
- aesEncryptionService.encrypt(Buffer.from(secretKey)),
- ]);
- mockExchangeApiKeysRepository.findOneByUserId.mockResolvedValueOnce({
- exchangeName,
- apiKey: encryptedApiKey,
- secretKey: encryptedSecretKey,
- } as ExchangeApiKeyEntity);
-
- const result = await exchangeApiKeysService.retrieve(userId);
-
- expect(result).not.toBeNull();
- expect(result!.apiKey).toBe(apiKey);
- expect(result!.secretKey).toBe(secretKey);
- expect(
- mockExchangeApiKeysRepository.findOneByUserId,
- ).toHaveBeenCalledWith(userId);
- });
- });
-
- describe('getSupportedExchanges', () => {
- it('returns a copy of supported exchanges constant', () => {
- const result = exchangeApiKeysService.getSupportedExchanges();
-
- expect(result).toEqual(SUPPORTED_EXCHANGES_INFO);
- expect(result).not.toBe(SUPPORTED_EXCHANGES_INFO);
- });
- });
-});
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.service.ts b/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.service.ts
deleted file mode 100644
index c59b852fdc..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/exchange-api-keys.service.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-import { Injectable } from '@nestjs/common';
-
-import {
- SUPPORTED_EXCHANGES_INFO,
- SupportedExchange,
- type SupportedExchangeInfo,
-} from '@/common/constants';
-import { AesEncryptionService } from '@/modules/encryption/aes-encryption.service';
-import { ExchangeClientFactory } from '@/modules/exchange/exchange-client.factory';
-import { UserNotFoundError, UserRepository } from '@/modules/user';
-
-import { ExchangeApiKeyEntity } from './exchange-api-key.entity';
-import { KeyAuthorizationError } from './exchange-api-keys.errors';
-import { ExchangeApiKeysRepository } from './exchange-api-keys.repository';
-
-@Injectable()
-export class ExchangeApiKeysService {
- constructor(
- private readonly aesEncryptionService: AesEncryptionService,
- private readonly exchangeApiKeysRepository: ExchangeApiKeysRepository,
- private readonly exchangeClientFactory: ExchangeClientFactory,
- private readonly userRepository: UserRepository,
- ) {}
-
- async enroll(input: {
- userId: number;
- exchangeName: SupportedExchange;
- apiKey: string;
- secretKey: string;
- }): Promise {
- const { userId, exchangeName, apiKey, secretKey } = input;
-
- if (!userId || !apiKey || !secretKey) {
- throw new Error('Invalid arguments');
- }
-
- const currentKeys =
- await this.exchangeApiKeysRepository.findOneByUserId(userId);
-
- const client = await this.exchangeClientFactory.create(exchangeName, {
- apiKey,
- secretKey,
- });
- const hasRequiredAccess = await client.checkRequiredAccess();
- if (!hasRequiredAccess) {
- throw new KeyAuthorizationError(exchangeName);
- }
-
- const user = await this.userRepository.findOneById(userId);
- if (!user) {
- throw new UserNotFoundError(userId);
- }
-
- const [encryptedApiKey, encryptedSecretKey] = await Promise.all([
- this.aesEncryptionService.encrypt(Buffer.from(apiKey)),
- this.aesEncryptionService.encrypt(Buffer.from(secretKey)),
- ]);
- if (currentKeys) {
- currentKeys.exchangeName = exchangeName;
- currentKeys.apiKey = encryptedApiKey;
- currentKeys.secretKey = encryptedSecretKey;
-
- return this.exchangeApiKeysRepository.updateOne(currentKeys);
- }
-
- const enrolledKey = new ExchangeApiKeyEntity();
- enrolledKey.userId = userId;
- enrolledKey.exchangeName = exchangeName;
- enrolledKey.apiKey = encryptedApiKey;
- enrolledKey.secretKey = encryptedSecretKey;
- await this.exchangeApiKeysRepository.createUnique(enrolledKey);
-
- return enrolledKey;
- }
-
- async retrieve(userId: number): Promise<{
- exchangeName: string;
- apiKey: string;
- secretKey: string;
- } | null> {
- const entity = await this.exchangeApiKeysRepository.findOneByUserId(userId);
- if (!entity) {
- return null;
- }
-
- const [decryptedApiKey, decryptedSecretKey] = await Promise.all([
- this.aesEncryptionService.decrypt(entity.apiKey),
- this.aesEncryptionService.decrypt(entity.secretKey),
- ]);
-
- return {
- exchangeName: entity.exchangeName,
- apiKey: decryptedApiKey.toString(),
- secretKey: decryptedSecretKey.toString(),
- };
- }
-
- getSupportedExchanges(): SupportedExchangeInfo[] {
- return SUPPORTED_EXCHANGES_INFO.map((exchange) => ({ ...exchange }));
- }
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/fixtures/index.ts b/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/fixtures/index.ts
deleted file mode 100644
index 8597aa47fc..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/fixtures/index.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { faker } from '@faker-js/faker';
-
-import { generateExchangeName } from '@/modules/exchange/fixtures';
-
-import { ExchangeApiKeyEntity } from '../exchange-api-key.entity';
-
-export function generateExchangeApiKeysData() {
- return {
- userId: faker.number.int(),
- exchangeName: generateExchangeName(),
- apiKey: faker.string.sample(),
- secretKey: faker.string.sample(),
- };
-}
-
-export function generateExchangeApiKey(): ExchangeApiKeyEntity {
- const entity = {
- id: faker.number.int(),
- ...generateExchangeApiKeysData(),
- createdAt: faker.date.recent(),
- updatedAt: new Date(),
- };
-
- return entity as ExchangeApiKeyEntity;
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/index.ts b/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/index.ts
deleted file mode 100644
index 2736f37d6b..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange-api-keys/index.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export { ExchangeApiKeysModule } from './exchange-api-keys.module';
-export { ExchangeApiKeyEntity } from './exchange-api-key.entity';
-export { ExchangeApiKeysRepository } from './exchange-api-keys.repository';
-export { ExchangeApiKeysService } from './exchange-api-keys.service';
-export {
- ExchangeApiKeyNotFoundError,
- KeyAuthorizationError,
-} from './exchange-api-keys.errors';
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange/errors.ts b/packages/apps/reputation-oracle/server/src/modules/exchange/errors.ts
deleted file mode 100644
index d529464ffa..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange/errors.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { BaseError } from '@/common/errors/base';
-
-export class ExchangeApiClientError extends BaseError {}
-
-export class ExchangeProviderResponseError extends BaseError {
- constructor(exchange: string, status: number, detail?: string) {
- const exchangeLabel = `${exchange.toUpperCase()} API`;
- const fallback = status ? `status ${status}` : 'an error';
- super(
- detail
- ? `${exchangeLabel} error: ${detail}`
- : `${exchangeLabel} responded with ${fallback}`,
- );
- }
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange/exchange-client.factory.ts b/packages/apps/reputation-oracle/server/src/modules/exchange/exchange-client.factory.ts
deleted file mode 100644
index 035c2b0ef2..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange/exchange-client.factory.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { Injectable } from '@nestjs/common';
-
-import type { SupportedExchange } from '@/common/constants';
-
-import { GateExchangeClient } from './gate-exchange.client';
-import { MexcExchangeClient } from './mexc-exchange.client';
-import type {
- ExchangeClient,
- ExchangeClientCredentials,
- ExchangeClientOptions,
-} from './types';
-
-@Injectable()
-export class ExchangeClientFactory {
- async create(
- exchange: SupportedExchange,
- creds: ExchangeClientCredentials,
- options?: ExchangeClientOptions,
- ): Promise {
- switch (exchange) {
- case 'mexc': {
- return new MexcExchangeClient(creds, options);
- }
- case 'gate': {
- return new GateExchangeClient(creds, options);
- }
- default:
- throw new Error(`Unsupported exchange: ${exchange}`);
- }
- }
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange/exchange.module.ts b/packages/apps/reputation-oracle/server/src/modules/exchange/exchange.module.ts
deleted file mode 100644
index ddc664fdc8..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange/exchange.module.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Module } from '@nestjs/common';
-
-import { ExchangeClientFactory } from './exchange-client.factory';
-
-@Module({
- providers: [ExchangeClientFactory],
- exports: [ExchangeClientFactory],
-})
-export class ExchangeModule {}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange/fixtures/exchange.ts b/packages/apps/reputation-oracle/server/src/modules/exchange/fixtures/exchange.ts
deleted file mode 100644
index bca953a18b..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange/fixtures/exchange.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { faker } from '@faker-js/faker';
-
-import { SupportedExchange } from '@/common/constants';
-
-export function generateGateAccountBalance(tokens: string[] = []) {
- if (tokens.length === 0) {
- throw new Error('At least one token must be specified');
- }
- return tokens.map((token) => ({
- currency: token,
- available: faker.finance.amount(),
- locked: faker.finance.amount(),
- freeze: faker.finance.amount(),
- }));
-}
-
-export function generateMexcAccountBalance(tokens: string[] = []) {
- if (tokens.length === 0) {
- throw new Error('At least one token must be specified');
- }
- return {
- balances: tokens.map((token) => ({
- asset: token,
- free: faker.finance.amount(),
- locked: faker.finance.amount(),
- })),
- };
-}
-
-export function generateExchangeName() {
- return faker.helpers.enumValue(SupportedExchange);
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange/fixtures/index.ts b/packages/apps/reputation-oracle/server/src/modules/exchange/fixtures/index.ts
deleted file mode 100644
index 40165d35a9..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange/fixtures/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './exchange';
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange/gate-exchange.client.spec.ts b/packages/apps/reputation-oracle/server/src/modules/exchange/gate-exchange.client.spec.ts
deleted file mode 100644
index 9b7b60d150..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange/gate-exchange.client.spec.ts
+++ /dev/null
@@ -1,224 +0,0 @@
-jest.mock('@/logger');
-
-import crypto from 'crypto';
-
-import { faker } from '@faker-js/faker';
-import nock from 'nock';
-
-import {
- ExchangeApiClientError,
- ExchangeProviderResponseError,
-} from './errors';
-import { generateGateAccountBalance } from './fixtures';
-import {
- DEVELOP_GATE_API_BASE_URL,
- GateExchangeClient,
-} from './gate-exchange.client';
-
-describe('GateExchangeClient', () => {
- afterAll(() => {
- nock.restore();
- });
-
- afterEach(() => {
- jest.resetAllMocks();
- nock.cleanAll();
- });
-
- describe('signGateRequest', () => {
- it('returns the expected signature for known input', () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const method = faker.string.sample();
- const path = faker.string.sample();
- const query = faker.string.sample();
- const body = faker.string.sample();
- const now = faker.number.int();
-
- const client = new GateExchangeClient({ apiKey, secretKey });
-
- jest.useFakeTimers({ now });
- const { signature, timestamp } = client['signGateRequest'](
- method,
- path,
- query,
- body,
- );
- jest.useRealTimers();
-
- const bodyHash = crypto
- .createHash('sha512')
- .update(body ?? '')
- .digest('hex');
- const payload = [
- method,
- path,
- query,
- bodyHash,
- String(Math.floor(now / 1000)),
- ].join('\n');
- const expectedSignature = crypto
- .createHmac('sha512', secretKey)
- .update(payload)
- .digest('hex');
-
- expect(timestamp).toBe(String(Math.floor(now / 1000)));
- expect(signature).toBe(expectedSignature);
- });
- });
-
- describe('constructor', () => {
- it('throws if credentials are missing', () => {
- expect(
- () => new GateExchangeClient({ apiKey: '', secretKey: '' }),
- ).toThrow(ExchangeApiClientError);
- });
-
- it('sets fields correctly', () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const timeoutMs = faker.number.int();
- const client = new GateExchangeClient(
- { apiKey, secretKey },
- { timeoutMs: timeoutMs },
- );
- expect(client).toBeDefined();
- expect(client['apiKey']).toBe(apiKey);
- expect(client['secretKey']).toBe(secretKey);
- expect(client['timeoutMs']).toBe(timeoutMs);
- });
- });
-
- describe('checkRequiredAccess', () => {
- const path = '/spot/accounts';
-
- it('returns true if fetch is ok', async () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const client = new GateExchangeClient({ apiKey, secretKey });
- const scope = nock(DEVELOP_GATE_API_BASE_URL)
- .get(path)
- .query(true)
- .reply(200);
- const result = await client.checkRequiredAccess();
- scope.done();
- expect(result).toBe(true);
- });
-
- it('returns false if fetch is not ok', async () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const client = new GateExchangeClient({ apiKey, secretKey });
- const scope = nock(DEVELOP_GATE_API_BASE_URL)
- .get(path)
- .query(true)
- .reply(403);
- const result = await client.checkRequiredAccess();
- scope.done();
- expect(result).toBe(false);
- });
-
- it('throws ExchangeApiClientError on fetch error', async () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const client = new GateExchangeClient({ apiKey, secretKey });
- const scope = nock(DEVELOP_GATE_API_BASE_URL)
- .get(path)
- .query(true)
- .replyWithError('network error');
- let thrownError;
- try {
- await client.checkRequiredAccess();
- } catch (error) {
- thrownError = error;
- }
-
- scope.done();
-
- expect(thrownError).toBeInstanceOf(ExchangeApiClientError);
- expect((thrownError as Error).message).toBe(
- 'Failed to make request for exchange',
- );
- });
- });
-
- describe('getAccountBalance', () => {
- const path = '/spot/accounts';
-
- it('throws ExchangeProviderResponseError with response body if fetch not ok', async () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const asset = faker.finance.currencyCode();
- const client = new GateExchangeClient({ apiKey, secretKey });
- const errorPayload = { message: 'forbidden' };
- const scope = nock(DEVELOP_GATE_API_BASE_URL)
- .get(path)
- .query(true)
- .reply(403, errorPayload);
-
- const balancePromise = client.getAccountBalance(asset);
- await expect(balancePromise).rejects.toThrow(
- ExchangeProviderResponseError,
- );
-
- scope.done();
- });
-
- it('returns 0 if asset not found', async () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const asset = faker.finance.currencyCode();
- const client = new GateExchangeClient({ apiKey, secretKey });
- const scope = nock(DEVELOP_GATE_API_BASE_URL)
- .get(path)
- .query(true)
- .reply(200, generateGateAccountBalance(['OTHER']));
- const result = await client.getAccountBalance(asset);
- scope.done();
- expect(result).toBe(0);
- });
-
- it('returns sum of available and locked if asset found', async () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const asset = faker.finance.currencyCode();
- const client = new GateExchangeClient({ apiKey, secretKey });
- const balanceFixture = generateGateAccountBalance([asset]);
- const scope = nock(DEVELOP_GATE_API_BASE_URL)
- .get(path)
- .query(true)
- .reply(200, balanceFixture);
-
- const result = await client.getAccountBalance(asset);
- scope.done();
- expect(result).toBe(
- parseFloat(balanceFixture[0].available) +
- parseFloat(balanceFixture[0].locked),
- );
- });
-
- it('throws ExchangeApiClientError on fetch error', async () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const asset = faker.finance.currencyCode();
- const client = new GateExchangeClient({ apiKey, secretKey });
- const scope = nock(DEVELOP_GATE_API_BASE_URL)
- .get(path)
- .query(true)
- .replyWithError('network error');
- let thrownError;
- try {
- await client.getAccountBalance(asset);
- } catch (error) {
- thrownError = error;
- }
-
- scope.done();
-
- expect(thrownError).toBeInstanceOf(ExchangeApiClientError);
- expect((thrownError as Error).message).toBe(
- 'Failed to make request for exchange',
- );
- });
- });
-});
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange/gate-exchange.client.ts b/packages/apps/reputation-oracle/server/src/modules/exchange/gate-exchange.client.ts
deleted file mode 100644
index ad50394dea..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange/gate-exchange.client.ts
+++ /dev/null
@@ -1,148 +0,0 @@
-import { createHash, createHmac } from 'node:crypto';
-
-import { SupportedExchange } from '@/common/constants';
-import logger from '@/logger';
-import Environment from '@/utils/environment';
-
-import {
- ExchangeApiClientError,
- ExchangeProviderResponseError,
-} from './errors';
-import type {
- ExchangeClient,
- ExchangeClientCredentials,
- ExchangeClientOptions,
-} from './types';
-import { fetchWithHandling } from './utils';
-
-export const GATE_API_BASE_URL = 'https://api.gateio.ws/api/v4';
-export const DEVELOP_GATE_API_BASE_URL =
- 'https://api-testnet.gateapi.io/api/v4';
-
-export class GateExchangeClient implements ExchangeClient {
- readonly id = SupportedExchange.GATE;
- private readonly apiKey: string;
- private readonly secretKey: string;
- private readonly timeoutMs?: number;
- private readonly apiBaseUrl = Environment.isDevelopment()
- ? DEVELOP_GATE_API_BASE_URL
- : GATE_API_BASE_URL;
- private readonly logger = logger.child({
- context: GateExchangeClient.name,
- exchange: this.id,
- });
-
- constructor(
- creds: ExchangeClientCredentials,
- options?: ExchangeClientOptions,
- ) {
- if (!creds?.apiKey || !creds?.secretKey) {
- throw new ExchangeApiClientError('Incomplete credentials for Gate');
- }
- this.apiKey = creds.apiKey;
- this.secretKey = creds.secretKey;
- this.timeoutMs = options?.timeoutMs;
- }
-
- async checkRequiredAccess(): Promise {
- const method = 'GET';
- const path = '/spot/accounts';
- const query = '';
- const body = '';
- const { signature, timestamp } = this.signGateRequest(
- method,
- `/api/v4${path}`,
- query,
- body,
- );
-
- const res = await fetchWithHandling(
- `${this.apiBaseUrl}${path}`,
- {
- KEY: this.apiKey,
- SIGN: signature,
- Timestamp: timestamp,
- Accept: 'application/json',
- },
- this.logger,
- this.timeoutMs,
- );
-
- if (res.ok) return true;
- return false;
- }
-
- async getAccountBalance(asset: string): Promise {
- const method = 'GET';
- const path = '/spot/accounts';
- const query = `currency=${encodeURIComponent(asset)}`;
- const body = '';
- const requestPath = `/api/v4${path}`;
- const { signature, timestamp } = this.signGateRequest(
- method,
- requestPath,
- query,
- body,
- );
- const url = `${this.apiBaseUrl}${path}?${query}`;
-
- const res = await fetchWithHandling(
- url,
- {
- KEY: this.apiKey,
- SIGN: signature,
- Timestamp: timestamp,
- Accept: 'application/json',
- },
- this.logger,
- this.timeoutMs,
- );
-
- if (!res.ok) {
- const errorBody = await res.json();
- throw new ExchangeProviderResponseError(
- this.id,
- res.status,
- errorBody.message as string,
- );
- }
-
- const data = (await res.json()) as Array<{
- currency: string;
- available: string;
- locked?: string;
- freeze?: string;
- }>;
-
- const normalize = (item: {
- currency: string;
- available: string;
- locked?: string;
- freeze?: string;
- }) => {
- const free = parseFloat(item.available) || 0;
- const locked = parseFloat(item.locked ?? item.freeze ?? '0') || 0;
- return free + locked;
- };
-
- const entry = data.find((d) => d.currency === asset);
- return entry ? normalize(entry) : 0;
- }
-
- private signGateRequest(
- method: string,
- path: string,
- query: string,
- body: string,
- ): { signature: string; timestamp: string } {
- const timestamp = String(Math.floor(Date.now() / 1000));
- const bodyHash = createHash('sha512')
- .update(body ?? '')
- .digest('hex');
- const payload = [method, path, query, bodyHash, timestamp].join('\n');
- const signature = createHmac('sha512', this.secretKey)
- .update(payload)
- .digest('hex');
- return { signature, timestamp };
- }
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange/index.ts b/packages/apps/reputation-oracle/server/src/modules/exchange/index.ts
deleted file mode 100644
index 3a0424a866..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export { ExchangeModule } from './exchange.module';
-export { ExchangeClientFactory } from './exchange-client.factory';
-export * from './types';
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange/mexc-exchange.client.spec.ts b/packages/apps/reputation-oracle/server/src/modules/exchange/mexc-exchange.client.spec.ts
deleted file mode 100644
index 5b91f88d3b..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange/mexc-exchange.client.spec.ts
+++ /dev/null
@@ -1,196 +0,0 @@
-jest.mock('@/logger');
-
-import { createHmac } from 'crypto';
-
-import { faker } from '@faker-js/faker';
-import nock from 'nock';
-
-import {
- ExchangeApiClientError,
- ExchangeProviderResponseError,
-} from './errors';
-import { generateMexcAccountBalance } from './fixtures';
-import { MexcExchangeClient, MEXC_API_BASE_URL } from './mexc-exchange.client';
-
-describe('MexcExchangeClient', () => {
- afterAll(() => {
- nock.restore();
- });
-
- afterEach(() => {
- jest.resetAllMocks();
- nock.cleanAll();
- });
-
- describe('constructor', () => {
- it('throws if credentials are missing', () => {
- expect(
- () => new MexcExchangeClient({ apiKey: '', secretKey: '' }),
- ).toThrow(ExchangeApiClientError);
- });
-
- it('sets fields correctly', () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const timeoutMs = faker.number.int();
-
- const client = new MexcExchangeClient(
- { apiKey, secretKey },
- { timeoutMs: timeoutMs },
- );
-
- expect(client).toBeDefined();
- expect(client['apiKey']).toBe(apiKey);
- expect(client['secretKey']).toBe(secretKey);
- expect(client['timeoutMs']).toBe(timeoutMs);
- });
- });
-
- describe('signQuery', () => {
- it('getSignedQuery returns correct structure and signature', () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const client = new MexcExchangeClient({ apiKey, secretKey });
-
- const now = Date.now();
- jest.useFakeTimers({ now });
- const result = client['getSignedQuery']();
- jest.useRealTimers();
-
- expect(result).toHaveProperty('query');
- expect(result).toHaveProperty('signature');
- expect(result.query).toBe(`timestamp=${now}&recvWindow=5000`);
-
- const expectedSignature = createHmac('sha256', secretKey)
- .update(result.query)
- .digest('hex');
- expect(result.signature).toBe(expectedSignature);
- });
- });
-
- describe('checkRequiredAccess', () => {
- const path = '/account';
-
- it('returns true if fetch is ok', async () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const client = new MexcExchangeClient({ apiKey, secretKey });
- const scope = nock(MEXC_API_BASE_URL).get(path).query(true).reply(200);
- const result = await client.checkRequiredAccess();
- scope.done();
- expect(result).toBe(true);
- });
-
- it('returns false if fetch is not ok', async () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const client = new MexcExchangeClient({ apiKey, secretKey });
- const scope = nock(MEXC_API_BASE_URL).get(path).query(true).reply(403);
- const result = await client.checkRequiredAccess();
- scope.done();
- expect(result).toBe(false);
- });
-
- it('throws ExchangeApiClientError on fetch error', async () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const client = new MexcExchangeClient({ apiKey, secretKey });
- const scope = nock(MEXC_API_BASE_URL)
- .get(path)
- .query(true)
- .replyWithError('network error');
- let thrownError: unknown;
- try {
- await client.checkRequiredAccess();
- } catch (error) {
- thrownError = error;
- }
-
- scope.done();
-
- expect(thrownError).toBeInstanceOf(ExchangeApiClientError);
- expect((thrownError as Error).message).toBe(
- 'Failed to make request for exchange',
- );
- });
- });
-
- describe('getAccountBalance', () => {
- const path = '/account';
-
- it('throws ExchangeProviderResponseError with response detail if fetch not ok', async () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const asset = faker.finance.currencyCode();
- const client = new MexcExchangeClient({ apiKey, secretKey });
- const errorPayload = { msg: 'forbidden' };
- const scope = nock(MEXC_API_BASE_URL)
- .get(path)
- .query(true)
- .reply(400, errorPayload);
-
- const balancePromise = client.getAccountBalance(asset);
- await expect(balancePromise).rejects.toThrow(
- ExchangeProviderResponseError,
- );
-
- scope.done();
- });
-
- it('returns 0 if asset not found', async () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const asset = faker.finance.currencyCode();
- const client = new MexcExchangeClient({ apiKey, secretKey });
- const scope = nock(MEXC_API_BASE_URL)
- .get(path)
- .query(true)
- .reply(200, generateMexcAccountBalance(['OTHER']));
- const result = await client.getAccountBalance(asset);
- scope.done();
- expect(result).toBe(0);
- });
-
- it('returns sum of free and locked if asset found', async () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const asset = faker.finance.currencyCode();
- const client = new MexcExchangeClient({ apiKey, secretKey });
- const balanceFixture = generateMexcAccountBalance([asset]);
- const scope = nock(MEXC_API_BASE_URL)
- .get(path)
- .query(true)
- .reply(200, balanceFixture);
- const result = await client.getAccountBalance(asset);
- scope.done();
- expect(result).toBe(
- parseFloat(balanceFixture.balances[0].free) +
- parseFloat(balanceFixture.balances[0].locked),
- );
- });
-
- it('throws ExchangeApiClientError on fetch error', async () => {
- const apiKey = faker.string.sample();
- const secretKey = faker.string.sample();
- const asset = faker.finance.currencyCode();
- const client = new MexcExchangeClient({ apiKey, secretKey });
- const scope = nock(MEXC_API_BASE_URL)
- .get(path)
- .query(true)
- .replyWithError('network error');
- let thrownError: unknown;
- try {
- await client.getAccountBalance(asset);
- } catch (error) {
- thrownError = error;
- }
-
- scope.done();
-
- expect(thrownError).toBeInstanceOf(ExchangeApiClientError);
- expect((thrownError as Error).message).toBe(
- 'Failed to make request for exchange',
- );
- });
- });
-});
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange/mexc-exchange.client.ts b/packages/apps/reputation-oracle/server/src/modules/exchange/mexc-exchange.client.ts
deleted file mode 100644
index 9c3b0d4460..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange/mexc-exchange.client.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-import { createHmac } from 'node:crypto';
-
-import { SupportedExchange } from '@/common/constants';
-import logger from '@/logger';
-
-import {
- ExchangeApiClientError,
- ExchangeProviderResponseError,
-} from './errors';
-import type {
- ExchangeClient,
- ExchangeClientCredentials,
- ExchangeClientOptions,
-} from './types';
-import { fetchWithHandling } from './utils';
-
-export const MEXC_API_BASE_URL = 'https://api.mexc.com/api/v3';
-
-export class MexcExchangeClient implements ExchangeClient {
- readonly id = SupportedExchange.MEXC;
- private readonly apiKey: string;
- private readonly secretKey: string;
- private readonly timeoutMs?: number;
- private readonly logger = logger.child({
- context: MexcExchangeClient.name,
- exchange: this.id,
- });
- readonly recvWindow = 5000;
-
- constructor(
- creds: ExchangeClientCredentials,
- options?: ExchangeClientOptions,
- ) {
- if (!creds?.apiKey || !creds?.secretKey) {
- throw new ExchangeApiClientError('Incomplete credentials for MEXC');
- }
- this.apiKey = creds.apiKey;
- this.secretKey = creds.secretKey;
- this.timeoutMs = options?.timeoutMs;
- }
-
- private signQuery(query: string): string {
- return createHmac('sha256', this.secretKey).update(query).digest('hex');
- }
-
- async checkRequiredAccess(): Promise {
- const path = '/account';
- const { query, signature } = this.getSignedQuery();
- const url = `${MEXC_API_BASE_URL}${path}?${query}&signature=${signature}`;
-
- const res = await fetchWithHandling(
- url,
- { 'X-MEXC-APIKEY': this.apiKey },
- this.logger,
- this.timeoutMs,
- );
- if (res.ok) return true;
- return false;
- }
-
- async getAccountBalance(asset: string): Promise {
- const path = '/account';
- const { query, signature } = this.getSignedQuery();
- const url = `${MEXC_API_BASE_URL}${path}?${query}&signature=${signature}`;
-
- const res = await fetchWithHandling(
- url,
- { 'X-MEXC-APIKEY': this.apiKey },
- this.logger,
- this.timeoutMs,
- );
- if (!res.ok) {
- const errorBody = await res.json();
- throw new ExchangeProviderResponseError(
- this.id,
- res.status,
- errorBody.msg as string,
- );
- }
- const data = (await res.json()) as {
- balances?: Array<{ asset: string; free: string; locked: string }>;
- };
- const balances = data.balances || [];
- const entry = balances.find((b) => b.asset === asset);
- if (!entry) return 0;
- const total =
- (parseFloat(entry.free || '0') || 0) +
- (parseFloat(entry.locked || '0') || 0);
- return total;
- }
-
- private getSignedQuery(): {
- query: string;
- signature: string;
- } {
- const timestamp = Date.now();
- const query = `timestamp=${timestamp}&recvWindow=${this.recvWindow}`;
- const signature = this.signQuery(query);
- return { query, signature };
- }
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange/types.ts b/packages/apps/reputation-oracle/server/src/modules/exchange/types.ts
deleted file mode 100644
index 1fd5cfa68b..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange/types.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import type { SupportedExchange } from '@/common/constants';
-
-export interface ExchangeClientCredentials {
- apiKey: string;
- secretKey: string;
-}
-
-export interface ExchangeClientOptions {
- timeoutMs?: number;
-}
-
-export interface ExchangeClient {
- readonly id: SupportedExchange;
- checkRequiredAccess(): Promise;
- getAccountBalance(asset: string): Promise;
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/exchange/utils.ts b/packages/apps/reputation-oracle/server/src/modules/exchange/utils.ts
deleted file mode 100644
index 8a2fd42fdc..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/exchange/utils.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { DEFAULT_TIMEOUT_MS } from '@/common/constants';
-import Logger from '@/logger';
-
-import { ExchangeApiClientError } from './errors';
-
-export async function fetchWithHandling(
- url: string,
- headers: HeadersInit,
- logger: typeof Logger,
- timeoutMs?: number,
-): Promise {
- try {
- const res = await fetch(url, {
- method: 'GET',
- headers,
- signal: AbortSignal.timeout(timeoutMs || DEFAULT_TIMEOUT_MS),
- });
- return res;
- } catch (error) {
- const message: string = `Failed to make request for exchange`;
- logger.error(message, {
- url,
- error,
- });
- throw new ExchangeApiClientError(message);
- }
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/staking/index.ts b/packages/apps/reputation-oracle/server/src/modules/staking/index.ts
deleted file mode 100644
index fc1296365f..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/staking/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export { StakingModule } from './staking.module';
-export { StakingService } from './staking.service';
-export type { StakeSummaryData } from './types';
diff --git a/packages/apps/reputation-oracle/server/src/modules/staking/staking.controller.ts b/packages/apps/reputation-oracle/server/src/modules/staking/staking.controller.ts
deleted file mode 100644
index c4f711281d..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/staking/staking.controller.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { Controller, Get, Req, UseFilters } from '@nestjs/common';
-import {
- ApiBearerAuth,
- ApiOperation,
- ApiResponse,
- ApiTags,
-} from '@nestjs/swagger';
-
-import { Public } from '@/common/decorators';
-import type { RequestWithUser } from '@/common/types';
-
-import { StakeConfigResponseDto, StakeSummaryResponseDto } from './staking.dto';
-import { StakingControllerErrorsFilter } from './staking.error-filter';
-import { StakingService } from './staking.service';
-
-@ApiTags('Staking')
-@UseFilters(StakingControllerErrorsFilter)
-@Controller('staking')
-export class StakingController {
- constructor(private readonly stakingService: StakingService) {}
-
- @ApiBearerAuth()
- @ApiOperation({ summary: 'Retrieve aggregated staking info' })
- @ApiResponse({ status: 200, type: StakeSummaryResponseDto })
- @Get('/summary')
- async getStakeSummary(
- @Req() request: RequestWithUser,
- ): Promise {
- return this.stakingService.getStakeSummary(request.user.id);
- }
-
- @Public()
- @ApiOperation({ summary: 'Retrieve staking configuration' })
- @ApiResponse({ status: 200, type: StakeConfigResponseDto })
- @Get('/config')
- async getStakeConfig(): Promise {
- return this.stakingService.getStakeConfig();
- }
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/staking/staking.dto.ts b/packages/apps/reputation-oracle/server/src/modules/staking/staking.dto.ts
deleted file mode 100644
index 114430a9da..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/staking/staking.dto.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
-
-export class StakeSummaryResponseDto {
- @ApiProperty({ name: 'exchange_stake' })
- exchangeStake: string;
-
- @ApiProperty({ name: 'on_chain_stake' })
- onChainStake: string;
-
- @ApiPropertyOptional({ name: 'exchange_error' })
- exchangeError?: string;
-
- @ApiPropertyOptional({ name: 'on_chain_error' })
- onChainError?: string;
-}
-
-export class StakeConfigResponseDto {
- @ApiProperty({ name: 'min_threshold' })
- minThreshold: string;
-
- @ApiProperty({ name: 'eligibility_enabled' })
- eligibilityEnabled: boolean;
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/staking/staking.error-filter.ts b/packages/apps/reputation-oracle/server/src/modules/staking/staking.error-filter.ts
deleted file mode 100644
index af660d4d70..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/staking/staking.error-filter.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import {
- ArgumentsHost,
- Catch,
- ExceptionFilter,
- HttpStatus,
-} from '@nestjs/common';
-import { Request, Response } from 'express';
-
-import logger from '@/logger';
-import { UserNotFoundError } from '@/modules/user';
-
-@Catch(UserNotFoundError)
-export class StakingControllerErrorsFilter implements ExceptionFilter {
- private readonly logger = logger.child({
- context: StakingControllerErrorsFilter.name,
- });
-
- catch(exception: Error, host: ArgumentsHost) {
- const ctx = host.switchToHttp();
- const response = ctx.getResponse();
- const request = ctx.getRequest();
- let status = HttpStatus.INTERNAL_SERVER_ERROR;
-
- if (exception instanceof UserNotFoundError) {
- status = HttpStatus.UNPROCESSABLE_ENTITY;
- }
-
- return response.status(status).json({
- message: exception.message,
- timestamp: new Date().toISOString(),
- path: request.url,
- });
- }
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/staking/staking.module.ts b/packages/apps/reputation-oracle/server/src/modules/staking/staking.module.ts
deleted file mode 100644
index a28244e510..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/staking/staking.module.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Module } from '@nestjs/common';
-
-import { ExchangeModule } from '@/modules/exchange/exchange.module';
-import { ExchangeApiKeysModule } from '@/modules/exchange-api-keys';
-import { UserModule } from '@/modules/user';
-import { Web3Module } from '@/modules/web3';
-
-import { StakingController } from './staking.controller';
-import { StakingService } from './staking.service';
-
-@Module({
- imports: [ExchangeApiKeysModule, ExchangeModule, UserModule, Web3Module],
- providers: [StakingService],
- controllers: [StakingController],
- exports: [StakingService],
-})
-export class StakingModule {}
diff --git a/packages/apps/reputation-oracle/server/src/modules/staking/staking.service.spec.ts b/packages/apps/reputation-oracle/server/src/modules/staking/staking.service.spec.ts
deleted file mode 100644
index 7657803a1a..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/staking/staking.service.spec.ts
+++ /dev/null
@@ -1,205 +0,0 @@
-jest.mock('@human-protocol/sdk');
-
-import { faker } from '@faker-js/faker';
-import { createMock } from '@golevelup/ts-jest';
-import { StakingClient } from '@human-protocol/sdk';
-import { Test, TestingModule } from '@nestjs/testing';
-import { ethers } from 'ethers';
-
-import { SupportedExchange } from '@/common/constants';
-import { StakingConfigService, Web3ConfigService } from '@/config';
-import { type ExchangeClient, ExchangeClientFactory } from '@/modules/exchange';
-import { ExchangeApiKeysService } from '@/modules/exchange-api-keys';
-import { UserEntity, UserNotFoundError, UserRepository } from '@/modules/user';
-import { WalletWithProvider, Web3Service } from '@/modules/web3';
-import { mockWeb3ConfigService } from '@/modules/web3/fixtures';
-
-import { StakingService } from './staking.service';
-
-const mockExchangeApiKeysService = createMock();
-const mockExchangeClientFactory = {
- create: jest.fn(),
-};
-const mockExchangeClient = createMock();
-const mockUserRepository = createMock();
-const mockWeb3Service = createMock();
-const mockStakingConfigService: Omit = {
- eligibilityEnabled: true,
- minThreshold: faker.number.int({ min: 1, max: 1000 }),
- asset: 'HMT',
- timeoutMs: faker.number.int({ min: 1000, max: 10000 }),
-};
-const mockedStakingClient = jest.mocked(StakingClient);
-
-describe('StakingService', () => {
- let stakingService: StakingService;
-
- beforeAll(async () => {
- mockExchangeClientFactory.create.mockResolvedValue(mockExchangeClient);
- mockExchangeClient.getAccountBalance.mockReset();
- mockWeb3Service.getSigner.mockReturnValue({
- provider: {},
- } as never);
-
- const module: TestingModule = await Test.createTestingModule({
- providers: [
- StakingService,
- {
- provide: ExchangeApiKeysService,
- useValue: mockExchangeApiKeysService,
- },
- {
- provide: ExchangeClientFactory,
- useValue: mockExchangeClientFactory,
- },
- { provide: UserRepository, useValue: mockUserRepository },
- { provide: Web3Service, useValue: mockWeb3Service },
- { provide: StakingConfigService, useValue: mockStakingConfigService },
- { provide: Web3ConfigService, useValue: mockWeb3ConfigService },
- ],
- }).compile();
-
- stakingService = module.get(StakingService);
- });
-
- afterAll(() => {
- jest.clearAllMocks();
- });
-
- describe('getExchangeStakedBalance', () => {
- const userId = faker.number.int();
- it('returns 0 when user has no exchange keys', async () => {
- mockExchangeApiKeysService.retrieve.mockResolvedValueOnce(null);
-
- await expect(
- stakingService.getExchangeStakedBalance(userId),
- ).resolves.toBe(0);
- });
-
- it('returns balance fetched from exchange client', async () => {
- const keys = {
- exchangeName: SupportedExchange.GATE,
- apiKey: faker.string.sample(),
- secretKey: faker.string.sample(),
- };
- const balance = faker.number.int();
- mockExchangeApiKeysService.retrieve.mockResolvedValueOnce(keys);
- mockExchangeClient.getAccountBalance.mockResolvedValueOnce(balance);
-
- const result = await stakingService.getExchangeStakedBalance(userId);
- expect(mockExchangeClientFactory.create).toHaveBeenCalledWith(
- keys.exchangeName,
- {
- apiKey: keys.apiKey,
- secretKey: keys.secretKey,
- },
- { timeoutMs: mockStakingConfigService.timeoutMs },
- );
- expect(mockExchangeClient.getAccountBalance).toHaveBeenCalledWith(
- mockStakingConfigService.asset,
- );
- expect(result).toBe(balance);
- });
- });
-
- describe('getStakeSummary', () => {
- const user = {
- id: faker.number.int(),
- evmAddress: faker.finance.ethereumAddress(),
- };
- const onChainStake = faker.number.int();
- const exchangeStake = faker.number.int();
- let spyOnGetExchangeStakedBalance: jest.SpyInstance;
- let spyOnGetOnChainStakedBalance: jest.SpyInstance;
-
- beforeAll(() => {
- spyOnGetExchangeStakedBalance = jest
- .spyOn(stakingService, 'getExchangeStakedBalance')
- .mockImplementation();
- spyOnGetOnChainStakedBalance = jest
- .spyOn(stakingService, 'getOnChainStakedBalance')
- .mockImplementation();
- });
-
- afterAll(() => {
- spyOnGetExchangeStakedBalance.mockRestore();
- spyOnGetOnChainStakedBalance.mockRestore();
- });
-
- it('throws when user is not found', async () => {
- mockUserRepository.findOneById.mockResolvedValueOnce(null);
-
- await expect(
- stakingService.getStakeSummary(user.id),
- ).rejects.toBeInstanceOf(UserNotFoundError);
- });
-
- it('returns aggregated exchange and on-chain stakes', async () => {
- mockUserRepository.findOneById.mockResolvedValueOnce(user as UserEntity);
- spyOnGetExchangeStakedBalance.mockResolvedValueOnce(exchangeStake);
- spyOnGetOnChainStakedBalance.mockResolvedValueOnce(onChainStake);
-
- const result = await stakingService.getStakeSummary(user.id);
-
- expect(spyOnGetExchangeStakedBalance).toHaveBeenCalledWith(user.id);
- expect(spyOnGetOnChainStakedBalance).toHaveBeenCalledWith(
- user.evmAddress,
- );
- expect(result).toEqual({
- exchangeStake: exchangeStake.toString(),
- onChainStake: onChainStake.toString(),
- });
- });
-
- it('skips on-chain lookup when user has no address', async () => {
- mockUserRepository.findOneById.mockResolvedValueOnce({
- ...user,
- evmAddress: null,
- } as UserEntity);
- spyOnGetExchangeStakedBalance.mockResolvedValueOnce(exchangeStake);
-
- const result = await stakingService.getStakeSummary(user.id);
-
- expect(spyOnGetOnChainStakedBalance).not.toHaveBeenCalled();
- expect(result).toEqual({
- exchangeStake: exchangeStake.toString(),
- onChainStake: '0',
- });
- });
- });
-
- describe('getOnChainStakedBalance', () => {
- it('returns total staked balance', async () => {
- const address = faker.finance.ethereumAddress();
- const stakedAmount = ethers.toBigInt(
- faker.number.int({ min: 500, max: 1000000 }),
- );
- const mockProvider = {};
- mockWeb3Service.getSigner.mockReturnValueOnce({
- provider: mockProvider,
- } as WalletWithProvider);
-
- const getStakerInfoMock = jest.fn().mockResolvedValue({ stakedAmount });
- mockedStakingClient.build.mockResolvedValueOnce({
- getStakerInfo: getStakerInfoMock,
- } as unknown as StakingClient);
-
- const result = await stakingService.getOnChainStakedBalance(address);
-
- expect(mockedStakingClient.build).toHaveBeenCalledWith(mockProvider);
- expect(getStakerInfoMock).toHaveBeenCalledWith(address);
- expect(result).toBe(Number(ethers.formatEther(stakedAmount)));
- });
- });
-
- describe('getStakeConfig', () => {
- it('returns current staking configuration', async () => {
- const result = await stakingService.getStakeConfig();
-
- expect(result).toEqual({
- minThreshold: mockStakingConfigService.minThreshold.toString(),
- eligibilityEnabled: mockStakingConfigService.eligibilityEnabled,
- });
- });
- });
-});
diff --git a/packages/apps/reputation-oracle/server/src/modules/staking/staking.service.ts b/packages/apps/reputation-oracle/server/src/modules/staking/staking.service.ts
deleted file mode 100644
index 4103b91e91..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/staking/staking.service.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-import { StakingClient } from '@human-protocol/sdk';
-import { Injectable } from '@nestjs/common';
-import { ethers } from 'ethers';
-
-import { SupportedExchange } from '@/common/constants';
-import { StakingConfigService, Web3ConfigService } from '@/config';
-import logger from '@/logger';
-import { ExchangeClientFactory } from '@/modules/exchange/exchange-client.factory';
-import { ExchangeApiKeysService } from '@/modules/exchange-api-keys';
-import { UserNotFoundError, UserRepository } from '@/modules/user';
-import { Web3Service } from '@/modules/web3';
-import { formatStake } from '@/utils/stake';
-
-import { StakeConfigData, StakeSummaryData } from './types';
-
-@Injectable()
-export class StakingService {
- private readonly logger = logger.child({
- context: StakingService.name,
- });
-
- constructor(
- private readonly exchangeApiKeysService: ExchangeApiKeysService,
- private readonly exchangeClientFactory: ExchangeClientFactory,
- private readonly userRepository: UserRepository,
- private readonly web3Service: Web3Service,
- private readonly stakingConfigService: StakingConfigService,
- private readonly web3ConfigService: Web3ConfigService,
- ) {}
-
- async getExchangeStakedBalance(userId: number): Promise {
- const apiKeys = await this.exchangeApiKeysService.retrieve(userId);
- if (!apiKeys) {
- return 0;
- }
-
- const client = await this.exchangeClientFactory.create(
- apiKeys.exchangeName as SupportedExchange,
- {
- apiKey: apiKeys.apiKey,
- secretKey: apiKeys.secretKey,
- },
- { timeoutMs: this.stakingConfigService.timeoutMs },
- );
-
- return client.getAccountBalance(this.stakingConfigService.asset);
- }
-
- async getOnChainStakedBalance(address: string): Promise {
- const chainId = this.web3ConfigService.reputationNetworkChainId;
- const provider = this.web3Service.getSigner(chainId).provider;
-
- const stakingClient = await StakingClient.build(provider);
- const stakerInfo = await stakingClient.getStakerInfo(address);
-
- return Number(ethers.formatEther(stakerInfo.stakedAmount ?? 0n));
- }
-
- async getStakeSummary(userId: number): Promise {
- const user = await this.userRepository.findOneById(userId);
- if (!user) {
- throw new UserNotFoundError(userId);
- }
-
- const summary: StakeSummaryData = {
- exchangeStake: '0',
- onChainStake: '0',
- };
-
- try {
- summary.exchangeStake = formatStake(
- await this.getExchangeStakedBalance(userId),
- );
- } catch (error) {
- summary.exchangeError = error.message
- ? error.message
- : 'Unable to fetch exchange stake';
- this.logger.warn('Failed to retrieve exchange stake', {
- userId,
- error,
- });
- }
-
- if (user.evmAddress) {
- try {
- summary.onChainStake = formatStake(
- await this.getOnChainStakedBalance(user.evmAddress),
- );
- } catch (error) {
- summary.onChainError = error.message
- ? error.message
- : 'Unable to fetch on-chain stake';
- this.logger.warn('Failed to retrieve on-chain stake', {
- userId,
- evmAddress: user.evmAddress,
- error,
- });
- }
- }
-
- return summary;
- }
-
- async getStakeConfig(): Promise {
- return {
- minThreshold: this.stakingConfigService.minThreshold.toString(),
- eligibilityEnabled: this.stakingConfigService.eligibilityEnabled,
- };
- }
-}
diff --git a/packages/apps/reputation-oracle/server/src/modules/staking/types.ts b/packages/apps/reputation-oracle/server/src/modules/staking/types.ts
deleted file mode 100644
index fcf1e16436..0000000000
--- a/packages/apps/reputation-oracle/server/src/modules/staking/types.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export type StakeSummaryData = {
- exchangeStake: string;
- onChainStake: string;
- exchangeError?: string;
- onChainError?: string;
-};
-
-export type StakeConfigData = {
- minThreshold: string;
- eligibilityEnabled: boolean;
-};
diff --git a/packages/apps/reputation-oracle/server/src/utils/stake.ts b/packages/apps/reputation-oracle/server/src/utils/stake.ts
deleted file mode 100644
index 49988b7c22..0000000000
--- a/packages/apps/reputation-oracle/server/src/utils/stake.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-const stakeFormatter = new Intl.NumberFormat(undefined, {
- maximumFractionDigits: 18,
- notation: 'standard',
- useGrouping: false,
-});
-export const formatStake = (value: number) => stakeFormatter.format(value);