Commit daf122d9 authored by tom's avatar tom

first draft

parent a10fff5f
import type { NextApiRequest } from 'next';
import handler from 'lib/api/handler';
const getUrl = (req: NextApiRequest) => {
return `/account/v1/user/custom_abis/${ req.query.id }`;
};
const customAbiHandler = handler(getUrl, [ 'DELETE', 'PUT' ]);
export default customAbiHandler;
import type { CustomAbis } from 'types/api/account';
import handler from 'lib/api/handler';
const customAbiHandler = handler<CustomAbis>(() => '/account/v1/user/custom_abis', [ 'GET', 'POST' ]);
export default customAbiHandler;
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import CustomAbi from 'ui/pages/CustomAbi';
const CustomAbiPage: NextPage = () => {
return (
<>
<Head><title>Custom ABI</title></Head>
<CustomAbi/>
</>
);
};
export default CustomAbiPage;
......@@ -65,3 +65,27 @@ export interface WatchlistAddressNew {
}
export type WatchlistAddresses = Array<WatchlistAddress>
export type CustomAbis = Array<CustomAbi>
export interface CustomAbi {
name: string;
id: number;
contract_address_hash: string;
abi: Array<AbiItem>;
}
export interface AbiItem {
type: 'function';
stateMutability: 'nonpayable' | 'view';
payable: boolean;
outputs: Array<AbiInputOutput>;
name: string;
inputs: Array<AbiInputOutput>;
constant: boolean;
}
interface AbiInputOutput {
type: 'uint256';
name: string;
}
import {
Box,
Button,
FormControl,
FormLabel,
Input,
} from '@chakra-ui/react';
// import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useCallback, useEffect } from 'react';
import type { ControllerRenderProps } from 'react-hook-form';
import { useForm, Controller } from 'react-hook-form';
import type { CustomAbi } from 'types/api/account';
type Props = {
data?: CustomAbi;
onClose: () => void;
}
type Inputs = {
contract_address_hash: string;
name: string;
}
// idk, maybe there is no limit
const NAME_MAX_LENGTH = 100;
const CustomAbiForm: React.FC<Props> = ({ data }) => {
const { control, formState: { errors }, setValue } = useForm<Inputs>();
// const queryClient = useQueryClient();
useEffect(() => {
setValue('contract_address_hash', data?.contract_address_hash || '');
setValue('name', data?.name || '');
}, [ setValue, data ]);
// const updateApiKey = (data: Inputs) => {
// const body = JSON.stringify({ name: data.name });
// if (!data.token) {
// return fetch('/api/account/api-keys', { method: 'POST', body });
// }
// return fetch(`/api/account/api-keys/${ data.token }`, { method: 'PUT', body });
// };
// const mutation = useMutation(updateApiKey, {
// onSuccess: async(data) => {
// const response: CustomAbi = await data.json();
// queryClient.setQueryData([ 'api-keys' ], (prevData: CustomAbis | undefined) => {
// const isExisting = prevData && prevData.some((item) => item.api_key === response.api_key);
// if (isExisting) {
// return prevData.map((item) => {
// if (item.api_key === response.api_key) {
// return response;
// }
// return item;
// });
// }
// return [ ...(prevData || []), response ];
// });
// onClose();
// },
// // eslint-disable-next-line no-console
// onError: console.error,
// });
// const onSubmit: SubmitHandler<Inputs> = useCallback((data) => {
// mutation.mutate(data);
// }, [ mutation ]);
const renderContractAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'contract_address_hash'>}) => {
return (
<FormControl variant="floating" id="contract_address_hash" isRequired>
<Input
{ ...field }
isInvalid={ Boolean(errors.contract_address_hash) }
/>
<FormLabel>Smart contract address (0x...)</FormLabel>
</FormControl>
);
}, [ errors ]);
const renderNameInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'name'>}) => {
return (
<FormControl variant="floating" id="name" isRequired>
<Input
{ ...field }
isInvalid={ Boolean(errors.name) }
maxLength={ NAME_MAX_LENGTH }
/>
<FormLabel>Project name</FormLabel>
</FormControl>
);
}, [ errors ]);
return (
<>
<Box marginBottom={ 5 }>
<Controller
name="contract_address_hash"
control={ control }
render={ renderContractAddressInput }
/>
</Box>
<Box marginBottom={ 8 }>
<Controller
name="name"
control={ control }
rules={{
maxLength: NAME_MAX_LENGTH,
}}
render={ renderNameInput }
/>
</Box>
<Box marginTop={ 8 }>
<Button
size="lg"
variant="primary"
// onClick={ handleSubmit(onSubmit) }
disabled={ Object.keys(errors).length > 0 }
// isLoading={ mutation.isLoading }
>
{ data ? 'Save' : 'Create custom ABI' }
</Button>
</Box>
</>
);
};
export default React.memo(CustomAbiForm);
import React, { useCallback } from 'react';
import type { CustomAbi } from 'types/api/account';
import FormModal from 'ui/shared/FormModal';
import CustomAbiForm from './CustomAbiForm';
type Props = {
isOpen: boolean;
onClose: () => void;
data?: CustomAbi;
}
const CustomAbiModal: React.FC<Props> = ({ isOpen, onClose, data }) => {
const title = data ? 'Edit custom ABI' : 'New custom ABI';
const text = 'Double check the ABI matches the contract to prevent errors or incorrect results.';
const renderForm = useCallback(() => {
return <CustomAbiForm data={ data } onClose={ onClose }/>;
}, [ data, onClose ]);
return (
<FormModal<CustomAbi>
isOpen={ isOpen }
onClose={ onClose }
title={ title }
text={ text }
data={ data }
renderForm={ renderForm }
/>
);
};
export default React.memo(CustomAbiModal);
import { Box, Button, HStack, Text, Spinner, useDisclosure } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import React, { useCallback, useState } from 'react';
import type { CustomAbi, CustomAbis } from 'types/api/account';
// import ApiKeyTable from 'ui/apiKey/ApiKeyTable/ApiKeyTable';
// import DeleteApiKeyModal from 'ui/apiKey/DeleteApiKeyModal';
import CustomAbiModal from 'ui/customAbi/CustomAbiModal/CustomAbiModal';
import AccountPageHeader from 'ui/shared/AccountPageHeader';
import Page from 'ui/shared/Page/Page';
const DATA_LIMIT = 3;
const CustomAbiPage: React.FC = () => {
const customAbiModalProps = useDisclosure();
// const deleteModalProps = useDisclosure();
const [ customAbiModalData, setCustomAbiModalData ] = useState<CustomAbi>();
// const [ deleteModalData, setDeleteModalData ] = useState<CustomAbi>();
const { data, isLoading, isError } = useQuery<unknown, unknown, CustomAbis>([ 'custom-abis' ], async() => {
const response = await fetch('/api/account/custom-abis');
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
});
// const onEditClick = useCallback((data: CustomAbi) => {
// setCustomAbiModalData(data);
// customAbiModalProps.onOpen();
// }, [ customAbiModalProps ]);
const onCustomAbiModalClose = useCallback(() => {
setCustomAbiModalData(undefined);
customAbiModalProps.onClose();
}, [ customAbiModalProps ]);
// const onDeleteClick = useCallback((data: CustomAbi) => {
// setDeleteModalData(data);
// deleteModalProps.onOpen();
// }, [ deleteModalProps ]);
// const onDeleteModalClose = useCallback(() => {
// setDeleteModalData(undefined);
// deleteModalProps.onClose();
// }, [ deleteModalProps ]);
const content = (() => {
if (isLoading || isError) {
return <Spinner/>;
}
const canAdd = data.length < DATA_LIMIT;
return (
<>
{ /* { data.length > 0 && (
<ApiKeyTable
data={ data }
onDeleteClick={ onDeleteClick }
onEditClick={ onEditClick }
limit={ DATA_LIMIT }
/>
) } */ }
<HStack marginTop={ 8 } spacing={ 5 }>
<Button
variant="primary"
size="lg"
onClick={ customAbiModalProps.onOpen }
disabled={ !canAdd }
>
Add custom ABI
</Button>
{ !canAdd && (
<Text fontSize="sm" variant="secondary">
{ `You have added the maximum number of API keys (${ DATA_LIMIT }). Contact us to request additional keys.` }
</Text>
) }
</HStack>
</>
);
})();
return (
<Page>
<Box h="100%">
<AccountPageHeader text="Custom ABI"/>
<Text marginBottom={ 12 }>
Add custom ABIs for any contract and access when logged into your account. Helpful for debugging, functional testing and contract interaction.
</Text>
{ content }
</Box>
<CustomAbiModal { ...customAbiModalProps } onClose={ onCustomAbiModalClose } data={ customAbiModalData }/>
{ /* { deleteModalData && <DeleteApiKeyModal { ...deleteModalProps } onClose={ onDeleteModalClose } data={ deleteModalData }/> } */ }
</Page>
);
};
export default CustomAbiPage;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment