diff --git a/apps/shell/src/app/app.tsx b/apps/shell/src/app/app.tsx
index 8076182fb..284f69c9e 100644
--- a/apps/shell/src/app/app.tsx
+++ b/apps/shell/src/app/app.tsx
@@ -39,6 +39,16 @@ const WalletCheckPage = lazy(async () => {
const { WalletCheckPage } = await import('@haqq/shell-airdrop');
return { default: WalletCheckPage };
});
+const CreateVestingAccountPage = lazy(async () => {
+ const { CreateVestingAccountPage } = await import(
+ '@haqq/shell/services-pages'
+ );
+ return { default: CreateVestingAccountPage };
+});
+const VestingInfoPage = lazy(async () => {
+ const { VestingInfoPage } = await import('@haqq/shell/services-pages');
+ return { default: VestingInfoPage };
+});
export function App() {
return (
@@ -87,6 +97,9 @@ export function App() {
}
/>
+ } />
+ } />
+
} />
} />
diff --git a/libs/shell/services-pages/.babelrc b/libs/shell/services-pages/.babelrc
new file mode 100644
index 000000000..1ea870ead
--- /dev/null
+++ b/libs/shell/services-pages/.babelrc
@@ -0,0 +1,12 @@
+{
+ "presets": [
+ [
+ "@nx/react/babel",
+ {
+ "runtime": "automatic",
+ "useBuiltIns": "usage"
+ }
+ ]
+ ],
+ "plugins": []
+}
diff --git a/libs/shell/services-pages/.eslintrc.json b/libs/shell/services-pages/.eslintrc.json
new file mode 100644
index 000000000..75b85077d
--- /dev/null
+++ b/libs/shell/services-pages/.eslintrc.json
@@ -0,0 +1,18 @@
+{
+ "extends": ["plugin:@nx/react", "../../../.eslintrc.json"],
+ "ignorePatterns": ["!**/*"],
+ "overrides": [
+ {
+ "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
+ "rules": {}
+ },
+ {
+ "files": ["*.ts", "*.tsx"],
+ "rules": {}
+ },
+ {
+ "files": ["*.js", "*.jsx"],
+ "rules": {}
+ }
+ ]
+}
diff --git a/libs/shell/services-pages/README.md b/libs/shell/services-pages/README.md
new file mode 100644
index 000000000..bbb860937
--- /dev/null
+++ b/libs/shell/services-pages/README.md
@@ -0,0 +1,7 @@
+# services-pages
+
+This library was generated with [Nx](https://nx.dev).
+
+## Running unit tests
+
+Run `nx test services-pages` to execute the unit tests via [Jest](https://jestjs.io).
diff --git a/libs/shell/services-pages/jest.config.ts b/libs/shell/services-pages/jest.config.ts
new file mode 100644
index 000000000..a21030752
--- /dev/null
+++ b/libs/shell/services-pages/jest.config.ts
@@ -0,0 +1,18 @@
+/* eslint-disable */
+export default {
+ displayName: 'services-pages',
+ preset: '../../../jest.preset.js',
+ transform: {
+ '^.+\\.[tj]sx?$': [
+ '@swc/jest',
+ {
+ jsc: {
+ parser: { syntax: 'typescript', tsx: true },
+ transform: { react: { runtime: 'automatic' } },
+ },
+ },
+ ],
+ },
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
+ coverageDirectory: '../../../coverage/libs/shell/services-pages',
+};
diff --git a/libs/shell/services-pages/project.json b/libs/shell/services-pages/project.json
new file mode 100644
index 000000000..fd34e4bbd
--- /dev/null
+++ b/libs/shell/services-pages/project.json
@@ -0,0 +1,30 @@
+{
+ "name": "services-pages",
+ "$schema": "../../../node_modules/nx/schemas/project-schema.json",
+ "sourceRoot": "libs/shell/services-pages/src",
+ "projectType": "library",
+ "tags": [],
+ "targets": {
+ "lint": {
+ "executor": "@nx/linter:eslint",
+ "outputs": ["{options.outputFile}"],
+ "options": {
+ "lintFilePatterns": ["libs/shell/services-pages/**/*.{ts,tsx,js,jsx}"]
+ }
+ },
+ "test": {
+ "executor": "@nx/jest:jest",
+ "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
+ "options": {
+ "jestConfig": "libs/shell/services-pages/jest.config.ts",
+ "passWithNoTests": true
+ },
+ "configurations": {
+ "ci": {
+ "ci": true,
+ "codeCoverage": true
+ }
+ }
+ }
+ }
+}
diff --git a/libs/shell/services-pages/src/index.ts b/libs/shell/services-pages/src/index.ts
new file mode 100644
index 000000000..48ad3ff24
--- /dev/null
+++ b/libs/shell/services-pages/src/index.ts
@@ -0,0 +1,2 @@
+export * from './lib/create-vesting-account-page/create-vesting-account-page';
+export * from './lib/vesting-info-page/vesting-info-page';
diff --git a/libs/shell/services-pages/src/lib/create-vesting-account-page/create-vesting-account-page.tsx b/libs/shell/services-pages/src/lib/create-vesting-account-page/create-vesting-account-page.tsx
new file mode 100644
index 000000000..f8c233a02
--- /dev/null
+++ b/libs/shell/services-pages/src/lib/create-vesting-account-page/create-vesting-account-page.tsx
@@ -0,0 +1,499 @@
+import { ethToHaqq, haqqToEth, useAddress, useClipboard } from '@haqq/shared';
+import { Button, Container } from '@haqq/shell-ui-kit';
+import clsx from 'clsx';
+import { useCallback, useEffect, useState } from 'react';
+import { isAddress } from 'viem';
+import { useVestingActions } from '../use-vesting-actions/use-vesting-actions';
+
+function formatDate(date: Date) {
+ return new Intl.DateTimeFormat('en-US', {
+ day: 'numeric',
+ month: 'short',
+ year: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ timeZone: 'GMT',
+ }).format(date);
+}
+
+type Period = {
+ length: number;
+ amount: {
+ denom: string;
+ amount: string;
+ }[];
+};
+
+function createUnsignedTransaction(
+ fromAddress: string,
+ toAddress: string,
+ startTime: string,
+ lockupPeriods: Period[],
+ vestingPeriods: Period[],
+) {
+ return {
+ body: {
+ messages: [
+ {
+ '@type': '/haqq.vesting.v1.MsgConvertIntoVestingAccount',
+ from_address: fromAddress,
+ to_address: toAddress,
+ start_time: startTime,
+ lockup_periods: lockupPeriods,
+ vesting_periods: vestingPeriods,
+ merge: true,
+ stake: false,
+ validator_address: '',
+ },
+ ],
+ memo: '',
+ timeout_height: '0',
+ extension_options: [],
+ non_critical_extension_options: [],
+ },
+ auth_info: {
+ signer_infos: [],
+ fee: {
+ amount: [{ denom: 'aISLM', amount: '61559740000000000' }],
+ gas_limit: '3077987',
+ payer: '',
+ granter: '',
+ },
+ tip: null,
+ },
+ signatures: [],
+ };
+}
+
+export function CreateVestingAccountPage() {
+ return (
+
+
+
+
+ Create vesting account
+
+
+
+
+
+
+ );
+}
+
+function CreateVestingAccountForm() {
+ const [fromAccount, setFromAccount] = useState('');
+ const [isFromValid, setFromValid] = useState(false);
+ const [fromAddresses, setFromAddresses] = useState<{
+ eth: string;
+ haqq: string;
+ }>({
+ eth: '',
+ haqq: '',
+ });
+ const [targetAccount, setTargetAccount] = useState('');
+ const [isTargetValid, setTargetValid] = useState(false);
+ const [targetAddresses, setTargetAddresses] = useState<{
+ eth: string;
+ haqq: string;
+ }>({
+ eth: '',
+ haqq: '',
+ });
+ const [amount, setAmount] = useState(10000);
+ const [lockup, setLockup] = useState(5);
+ const [unlock, setUnlock] = useState(60);
+ const [startTime, setStartTime] = useState(1695695564);
+ const { getPeriods } = useVestingActions();
+ const [generatedTx, setGeneratedTx] = useState({});
+ const { haqqAddress } = useAddress();
+ const { copyText } = useClipboard();
+ // const toast = useToast();
+ // const { chain } = useNetwork();
+ // const invalidateQueries = useQueryInvalidate();
+
+ // const handleCreateNewClick = useCallback(async () => {
+ // const grantPromise = createNew(
+ // targetAddresses.haqq,
+ // amount,
+ // startTime,
+ // lockup,
+ // unlock,
+ // );
+
+ // await toast.promise(grantPromise, {
+ // loading: Revoke in progress,
+ // success: (tx) => {
+ // console.log('Revoke successful', { tx });
+ // const txHash = tx.txhash;
+
+ // return (
+ //
+ //
+ //
Revoke successful
+ //
+ //
+ //
+ // {getFormattedAddress(txHash)}
+ //
+ //
+ //
+ //
+ // );
+ // },
+ // error: (error) => {
+ // return {error.message};
+ // },
+ // });
+
+ // // invalidateQueries([
+ // // [chain?.id, 'grants-granter'],
+ // // [chain?.id, 'grants-grantee'],
+ // // ]);
+ // }, [
+ // amount,
+ // createNew,
+ // lockup,
+ // startTime,
+ // targetAddresses.haqq,
+ // toast,
+ // unlock,
+ // ]);
+
+ useEffect(() => {
+ if (fromAccount.startsWith('0x')) {
+ console.log('validate as eth');
+ try {
+ const isValidEthAddress = isAddress(fromAccount);
+
+ if (isValidEthAddress) {
+ const haqq = ethToHaqq(fromAccount);
+ setFromValid(true);
+ setFromAddresses({
+ eth: fromAccount,
+ haqq,
+ });
+ } else {
+ setFromValid(false);
+ setFromAddresses({
+ eth: fromAccount,
+ haqq: '',
+ });
+ }
+ } catch {
+ setFromValid(false);
+ setFromAddresses({
+ eth: fromAccount,
+ haqq: '',
+ });
+ }
+ } else if (fromAccount.startsWith('haqq1')) {
+ console.log('validate as bech32');
+ try {
+ const eth = haqqToEth(fromAccount);
+ setFromValid(true);
+ setFromAddresses({
+ haqq: fromAccount,
+ eth: eth,
+ });
+ } catch {
+ setFromValid(false);
+ setFromAddresses({
+ haqq: fromAccount,
+ eth: '',
+ });
+ }
+ }
+ }, [fromAccount]);
+
+ useEffect(() => {
+ if (targetAccount.startsWith('0x')) {
+ console.log('validate as eth');
+ try {
+ const isValidEthAddress = isAddress(targetAccount);
+
+ if (isValidEthAddress) {
+ const haqq = ethToHaqq(targetAccount);
+ setTargetValid(true);
+ setTargetAddresses({
+ eth: targetAccount,
+ haqq,
+ });
+ } else {
+ setTargetValid(false);
+ setTargetAddresses({
+ eth: targetAccount,
+ haqq: '',
+ });
+ }
+ } catch {
+ setTargetValid(false);
+ setTargetAddresses({
+ eth: targetAccount,
+ haqq: '',
+ });
+ }
+ } else if (targetAccount.startsWith('haqq1')) {
+ console.log('validate as bech32');
+ try {
+ const eth = haqqToEth(targetAccount);
+ setTargetValid(true);
+ setTargetAddresses({
+ haqq: targetAccount,
+ eth: eth,
+ });
+ } catch {
+ setTargetValid(false);
+ setTargetAddresses({
+ haqq: targetAccount,
+ eth: '',
+ });
+ }
+ }
+ }, [targetAccount]);
+
+ useEffect(() => {
+ const { lockupPeriods, vestingPeriods } = getPeriods(
+ amount,
+ lockup,
+ unlock,
+ );
+
+ const date = new Date();
+ date.setTime(startTime * 1000);
+ let startTimeIsoString = date.toISOString();
+ startTimeIsoString = startTimeIsoString.replace('.000Z', 'Z');
+
+ const tx = createUnsignedTransaction(
+ fromAddresses.haqq,
+ targetAddresses.haqq,
+ startTimeIsoString,
+ lockupPeriods,
+ vestingPeriods,
+ );
+
+ setGeneratedTx(tx);
+ }, [
+ amount,
+ getPeriods,
+ haqqAddress,
+ lockup,
+ startTime,
+ fromAddresses.haqq,
+ targetAddresses.haqq,
+ unlock,
+ ]);
+
+ const handleSaveFileClick = useCallback(() => {
+ const jsonString = JSON.stringify(generatedTx, null, 2);
+ const blob = new Blob([jsonString], { type: 'application/json' });
+ const blobUrl = URL.createObjectURL(blob);
+ const downloadLink = document.createElement('a');
+ downloadLink.href = blobUrl;
+ downloadLink.download = `${targetAddresses.haqq}-convert-to-vesting-tx.json`;
+ downloadLink.click();
+ URL.revokeObjectURL(blobUrl);
+ }, [generatedTx, targetAddresses.haqq]);
+ const [isCopied, setIsCopied] = useState(false);
+
+ const handleCopyClick = useCallback(async () => {
+ const jsonString = JSON.stringify(generatedTx, null, 2);
+ await copyText(jsonString);
+ setIsCopied(true);
+ setTimeout(() => {
+ setIsCopied(false);
+ }, 2000);
+ }, [copyText, generatedTx]);
+
+ return (
+
+
+
+
+
+
+
+
+ {JSON.stringify(generatedTx, null, 2)}
+
+
+
+ {!isTargetValid && (
+
+ Wrong parameters
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+function Input({
+ value,
+ onChange,
+ placeholder,
+ label,
+ id,
+}: {
+ id: string;
+ placeholder: string;
+ value: string;
+ label: string;
+ onChange: (value: string) => void;
+}) {
+ return (
+
+
+
+
+
+ {
+ onChange(event.currentTarget.value);
+ }}
+ />
+
+
+ );
+}
diff --git a/libs/shell/services-pages/src/lib/use-vesting-actions/use-vesting-actions.tsx b/libs/shell/services-pages/src/lib/use-vesting-actions/use-vesting-actions.tsx
new file mode 100644
index 000000000..516799a9a
--- /dev/null
+++ b/libs/shell/services-pages/src/lib/use-vesting-actions/use-vesting-actions.tsx
@@ -0,0 +1,315 @@
+import { useCallback, useMemo } from 'react';
+import { MessageMsgCreateClawbackVestingAccount } from '@evmos/transactions';
+import {
+ useAddress,
+ getChainParams,
+ useCosmosService,
+ mapToCosmosChain,
+} from '@haqq/shared';
+import { useNetwork } from 'wagmi';
+
+export function useVestingActions() {
+ const { chain } = useNetwork();
+ // const { data: walletClient } = useWalletClient();
+ const { getAccountBaseInfo, getPubkey } = useCosmosService();
+ const { haqqAddress, ethAddress } = useAddress();
+
+ const haqqChain = useMemo(() => {
+ if (!chain || chain.unsupported) {
+ return undefined;
+ }
+
+ const chainParams = getChainParams(chain.id);
+ return mapToCosmosChain(chainParams);
+ }, [chain]);
+
+ const getSender = useCallback(
+ async (address: string, pubkey: string) => {
+ try {
+ const accInfo = await getAccountBaseInfo(address);
+
+ if (!accInfo) {
+ throw new Error('no base account info');
+ }
+
+ return {
+ accountAddress: address,
+ sequence: parseInt(accInfo.sequence, 10),
+ accountNumber: parseInt(accInfo.account_number, 10),
+ pubkey,
+ };
+ } catch (error) {
+ console.error((error as Error).message);
+ throw error;
+ }
+ },
+ [getAccountBaseInfo],
+ );
+
+ // const signTransaction = useCallback(
+ // async (msg: TxGenerated, sender: Sender) => {
+ // if (haqqChain && ethAddress && walletClient) {
+ // const signature = await walletClient.request({
+ // method: 'eth_signTypedData_v4',
+ // params: [ethAddress as `0x${string}`, JSON.stringify(msg.eipToSign)],
+ // });
+
+ // const extension = signatureToWeb3Extension(
+ // haqqChain,
+ // sender,
+ // signature,
+ // );
+ // const rawTx = createTxRawEIP712(
+ // msg.legacyAmino.body,
+ // msg.legacyAmino.authInfo,
+ // extension,
+ // );
+
+ // return rawTx;
+ // } else {
+ // throw new Error('No haqqChain');
+ // }
+ // },
+ // [ethAddress, haqqChain, walletClient],
+ // );
+
+ const getPeriods = useCallback(
+ (amount: number, lockupInDays: number, vestingInDays: number) => {
+ const cliff = lockupInDays * 24 * 60 * 60;
+ const periodLength = 24 * 60 * 60;
+ const periods = vestingInDays;
+ const denom = 'aISLM';
+ const lockupPeriods = [];
+ const aislmAmount = BigInt(amount * 10 ** 18);
+ let restAmount = aislmAmount;
+ let unlockAmount = aislmAmount / BigInt(periods);
+ let length: number = cliff;
+
+ for (let i = 0; i < periods; i++) {
+ if (i === periods - 1) {
+ unlockAmount = restAmount;
+ }
+
+ const unlockCoins = {
+ denom: denom,
+ amount: unlockAmount.toString(),
+ };
+
+ const period = {
+ length: length,
+ amount: [unlockCoins],
+ };
+
+ lockupPeriods.push(period);
+ restAmount -= unlockAmount;
+
+ if (i === 0) {
+ length = periodLength;
+ }
+ }
+
+ return {
+ lockupPeriods,
+ vestingPeriods: [] as typeof lockupPeriods,
+ };
+ },
+ [],
+ );
+
+ const getParams = useCallback(
+ async (
+ fromAddress: string,
+ toAddress: string,
+ amount: number,
+ startTime: number,
+ lockupInDays: number,
+ vestingInDays: number,
+ ) => {
+ const { lockupPeriods, vestingPeriods } = getPeriods(
+ amount,
+ lockupInDays,
+ vestingInDays,
+ );
+
+ const createParams: MessageMsgCreateClawbackVestingAccount = {
+ fromAddress,
+ toAddress,
+ startTime,
+ lockupPeriods,
+ vestingPeriods,
+ merge: false,
+ };
+
+ return createParams;
+ },
+ [getPeriods],
+ );
+
+ const handleCreateNew = useCallback(
+ async (
+ toAddress: string,
+ amount: number,
+ startTime: number,
+ lockupInDays: number,
+ vestingInDays: number,
+ ) => {
+ console.log('handleCreateNew', {
+ toAddress,
+ startTime,
+ lockupInDays,
+ vestingInDays,
+ });
+ const pubkey = await getPubkey(ethAddress as string);
+ const sender = await getSender(haqqAddress as string, pubkey);
+ // const memo = `Create vesting account with address ${toAddress}`;
+
+ if (sender && haqqChain && ethAddress) {
+ // const createParams = await getParams(
+ // sender.accountAddress,
+ // toAddress,
+ // amount,
+ // startTime,
+ // lockupInDays,
+ // vestingInDays,
+ // );
+
+ // const msg = createTxCreateClawbackVestingAccount(
+ // haqqChain,
+ // sender,
+ // DEFAULT_FEE,
+ // memo,
+ // createParams,
+ // );
+ // console.log({ msg });
+
+ // const rawTx = await signTransaction(msg, sender);
+ // const txResponse = await broadcastTransaction(rawTx);
+
+ // return txResponse;
+ return { txhash: 'foo' };
+ } else {
+ throw new Error('No sender');
+ }
+ },
+ [ethAddress, getPubkey, getSender, haqqAddress, haqqChain],
+ );
+
+ return useMemo(() => {
+ return {
+ createNew: handleCreateNew,
+ getParams,
+ getPeriods,
+ };
+ }, [getParams, getPeriods, handleCreateNew]);
+}
+
+// const MSG_CONVERT_TO_CLAWBACK_VESTING_ACCOUNT = {};
+
+// function createTxCreateClawbackVestingAccount(
+// chain: any,
+// sender: any,
+// fee: any,
+// memo: any,
+// params: any,
+// ) {
+// const feeObject = generateFee(
+// fee.amount,
+// fee.denom,
+// fee.gas,
+// sender.accountAddress,
+// );
+// const types = generateTypes(MSG_CONVERT_TO_CLAWBACK_VESTING_ACCOUNT);
+// const msg = createEipMsgConvertToClawbackVestingAccount(
+// params.fromAddress,
+// params.toAddress,
+// params.startTime,
+// params.lockupPeriods,
+// params.vestingPeriods,
+// params.merge,
+// );
+// const messages = generateMessage(
+// sender.accountNumber.toString(),
+// sender.sequence.toString(),
+// chain.cosmosChainId,
+// memo,
+// feeObject,
+// msg,
+// );
+// const eipToSign = createEIP712(types, chain.chainId, messages);
+// const msgCosmos = createComsosMsgConvertToClawbackVestingAccount(
+// params.fromAddress,
+// params.toAddress,
+// params.startTime,
+// params.lockupPeriods,
+// params.vestingPeriods,
+// params.merge,
+// );
+// const tx = createTransaction(
+// msgCosmos,
+// memo,
+// fee.amount,
+// fee.denom,
+// Number.parseInt(fee.gas, 10),
+// 'ethsecp256',
+// sender.pubkey,
+// sender.sequence,
+// sender.accountNumber,
+// chain.cosmosChainId,
+// );
+// return {
+// signDirect: tx.signDirect,
+// legacyAmino: tx.legacyAmino,
+// eipToSign,
+// };
+// }
+
+// export function createComsosMsgConvertToClawbackVestingAccount(
+// fromAddress: string,
+// toAddress: string,
+// startTime: number,
+// lockupPeriods: Period[],
+// vestingPeriods: Period[],
+// merge: boolean,
+// ) {
+// // const msg = createProtoMsgConvertToClawbackVestingAccount(
+// // fromAddress,
+// // toAddress,
+// // startTime,
+// // lockupPeriods,
+// // vestingPeriods,
+// // merge,
+// // );
+
+// return {
+// message: 'msg',
+// path: 'haqq.vesting.v1.MsgConvertIntoVestingAccount',
+// };
+// }
+
+// export function createEipMsgConvertToClawbackVestingAccount(
+// from_address: string,
+// to_address: string,
+// start_time: number,
+// lockup_periods: Period[],
+// vesting_periods: Period[],
+// merge: boolean,
+// ) {
+// // EIP712 requires the date to be a string in format YYYY-MM-DDTHH:MM:SSZ
+// const date = new Date();
+// date.setTime(start_time * 1000);
+// let startTime = date.toISOString();
+// startTime = startTime.replace('.000Z', 'Z');
+
+// return {
+// type: 'haqq/MsgConvertIntoVestingAccount',
+// value: {
+// from_address,
+// to_address,
+// start_time: startTime,
+// lockup_periods,
+// vesting_periods,
+// merge,
+// stake: false,
+// },
+// };
+// }
diff --git a/libs/shell/services-pages/src/lib/vesting-info-page/vesting-info-page.tsx b/libs/shell/services-pages/src/lib/vesting-info-page/vesting-info-page.tsx
new file mode 100644
index 000000000..20bd6db91
--- /dev/null
+++ b/libs/shell/services-pages/src/lib/vesting-info-page/vesting-info-page.tsx
@@ -0,0 +1,7 @@
+export function VestingInfoPage() {
+ return (
+
+
VestingInfoPage
+
+ );
+}
diff --git a/libs/shell/services-pages/tsconfig.json b/libs/shell/services-pages/tsconfig.json
new file mode 100644
index 000000000..4daaf45cd
--- /dev/null
+++ b/libs/shell/services-pages/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "jsx": "react-jsx",
+ "allowJs": false,
+ "esModuleInterop": false,
+ "allowSyntheticDefaultImports": true,
+ "strict": true
+ },
+ "files": [],
+ "include": [],
+ "references": [
+ {
+ "path": "./tsconfig.lib.json"
+ },
+ {
+ "path": "./tsconfig.spec.json"
+ }
+ ],
+ "extends": "../../../tsconfig.base.json"
+}
diff --git a/libs/shell/services-pages/tsconfig.lib.json b/libs/shell/services-pages/tsconfig.lib.json
new file mode 100644
index 000000000..21799b3e6
--- /dev/null
+++ b/libs/shell/services-pages/tsconfig.lib.json
@@ -0,0 +1,24 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../../dist/out-tsc",
+ "types": [
+ "node",
+
+ "@nx/react/typings/cssmodule.d.ts",
+ "@nx/react/typings/image.d.ts"
+ ]
+ },
+ "exclude": [
+ "jest.config.ts",
+ "src/**/*.spec.ts",
+ "src/**/*.test.ts",
+ "src/**/*.spec.tsx",
+ "src/**/*.test.tsx",
+ "src/**/*.spec.js",
+ "src/**/*.test.js",
+ "src/**/*.spec.jsx",
+ "src/**/*.test.jsx"
+ ],
+ "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
+}
diff --git a/libs/shell/services-pages/tsconfig.spec.json b/libs/shell/services-pages/tsconfig.spec.json
new file mode 100644
index 000000000..25b7af8f6
--- /dev/null
+++ b/libs/shell/services-pages/tsconfig.spec.json
@@ -0,0 +1,20 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../../dist/out-tsc",
+ "module": "commonjs",
+ "types": ["jest", "node"]
+ },
+ "include": [
+ "jest.config.ts",
+ "src/**/*.test.ts",
+ "src/**/*.spec.ts",
+ "src/**/*.test.tsx",
+ "src/**/*.spec.tsx",
+ "src/**/*.test.js",
+ "src/**/*.spec.js",
+ "src/**/*.test.jsx",
+ "src/**/*.spec.jsx",
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/tsconfig.base.json b/tsconfig.base.json
index ca49a6e75..09e98ae90 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -116,6 +116,7 @@
"@haqq/shell-ui-kit": ["libs/shell/ui-kit/src/index.ts"],
"@haqq/shell/authz-page": ["libs/shell/authz-page/src/index.ts"],
"@haqq/shell/index-page": ["libs/shell/index-page/src/index.ts"],
+ "@haqq/shell/services-pages": ["libs/shell/services-pages/src/index.ts"],
"@haqq/staking/ui-kit": ["libs/staking/ui-kit/src/index.ts"],
"@haqq/staking/utils": ["libs/staking/utils/src/index.ts"],
"@haqq/staking/validator-details-page": [