Skip to content

Commit

Permalink
handle case where contractAddress is invalid
Browse files Browse the repository at this point in the history
  • Loading branch information
mikestarrdev committed Apr 9, 2024
1 parent 433aca5 commit a164681
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 54 deletions.
80 changes: 50 additions & 30 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { cn } from './lib/utils';
import { useValidateOrder } from './hooks/useValidateOrder';
import { Header } from './features/ui/header';
import { Select } from './features/ui/select';
import { useAppStore, useChainStore } from './store/store';
import {
SwapContractAddressStore,
useAppStore,
useChainStore,
useSwapContractAddressStore,
} from './store/store';
import { useDomainInfo } from './hooks/useDomainInfo';
import { truncateAddress } from './utils/truncateAddress';
import { useFormatSchemaValidationErrors } from './hooks/useFormatSchemaValidationErrors';
Expand All @@ -18,7 +23,20 @@ function App() {

const { isCheckEnabled, setIsCheckEnabled } = useAppStore();
const { selectedChainId, setSelectedChainId } = useChainStore();
const { eip712Domain, protocolFee } = useDomainInfo(selectedChainId);
const swapContractAddress = useSwapContractAddressStore(
(state: SwapContractAddressStore) => state.swapContractAddress
);

let swapContract: string | undefined;
let domainName: string | undefined;
let domainVersion: string | undefined;
let protocolFeeFormatted: number | undefined;

const { eip712Domain, protocolFee } = useDomainInfo({
chainId: selectedChainId,
swapContract: swapContractAddress,
});

const {
schemaValidationResult,
orderErrors,
Expand All @@ -28,25 +46,28 @@ function App() {
} = useValidateOrder({
order: orderText,
isUrl: urlMode,
swapContract: swapContract,
onSetChain: (newId) => {
if (selectedChainId !== newId) setSelectedChainId(newId);
if (selectedChainId !== newId) {
setSelectedChainId(newId);
}
},
});

const formattedSchemaValidationErrors = useFormatSchemaValidationErrors(
schemaValidationError
);

let swapContract: string | undefined;
let domainName: string | undefined;
let domainVersion: string | undefined;
let protocolFeeFormatted: number | undefined;

if (eip712Domain?.status === 'success') {
swapContract = truncateAddress(eip712Domain.result[4]);
domainName = eip712Domain.result[1];
domainVersion = eip712Domain.result[2];
} else {
swapContract = 'Error reading contract';
domainName = 'Error reading contract';
domainVersion = 'Error reading contract';
}

if (protocolFee?.status === 'success') {
protocolFeeFormatted = Number(protocolFee.result);
}
Expand Down Expand Up @@ -94,6 +115,7 @@ function App() {
const formattedErrors = ErrorDisplay({
formattedSchemaValidationErrors,
orderErrors,
eip721DomainStatus: eip712Domain?.status,
});

// this is used to render "No errors found" display
Expand All @@ -102,28 +124,40 @@ function App() {
orderErrors,
});

const isDisplayErrors = formattedErrors ? formattedErrors : noErrorDisplay;

return (
<React.Fragment>
<Header />
<main className="container flex flex-col w-[849px] h-fit py-8 gap-4 border">
<h1 className="font-bold text-[24px]">Inspect an order</h1>
<div className="flex gap-2">
<button
onClick={() => setUrlMode(false)}
<Button
size="sm"
variant="outline"
onClick={() => {
setOrderText('');
setUrlMode(false);
}}
className={cn({
underline: !urlMode,
})}
>
text mode
</button>
<button
onClick={() => setUrlMode(true)}
Text mode
</Button>
<Button
size="sm"
variant="outline"
onClick={() => {
setOrderText('');
setUrlMode(true);
}}
className={cn({
underline: urlMode,
})}
>
URL mode
</button>
</Button>
</div>

{urlMode ? (
Expand Down Expand Up @@ -190,21 +224,7 @@ function App() {
</div>
<div className="w-1/2 px-6">
<h2 className="font-bold">Issues</h2>
<pre className="whitespace-pre h-full">
{formattedErrors && formattedErrors.length > 0
? formattedErrors
: noErrorDisplay}
{/* {JSON.stringify(
{
orderErrors,
contractCallError,
orderParsingError,
schemaValidationError,
},
null,
2
)} */}
</pre>
<pre className="whitespace-pre h-full">{isDisplayErrors}</pre>
</div>
</div>
)}
Expand Down
15 changes: 7 additions & 8 deletions src/features/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@ const buttonVariants = cva(
variants: {
variant: {
default:
'bg-airswap-blue text-white shadow hover:bg-primary/90 rounded-sm',
destructive:
'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
'bg-airswap-blue text-white font-bold shadow hover:bg-primary/90 rounded-sm uppercase',
outline:
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground uppercase font-bold',
secondary:
'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80 uppercas font-bolde',
ghost:
'hover:bg-accent hover:text-accent-foreground uppercase font-bold',
link: 'text-primary underline-offset-4 hover:underline uppercase font-bold',
},
size: {
default: 'h-12 w-[126px] px-4 py-2',
Expand Down Expand Up @@ -54,4 +53,4 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
);
Button.displayName = 'Button';

export { Button, buttonVariants };
export { Button };
18 changes: 18 additions & 0 deletions src/features/ui/errorDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ type SchemaValidationError = {
export const ErrorDisplay = ({
formattedSchemaValidationErrors,
orderErrors,
eip721DomainStatus,
}: {
formattedSchemaValidationErrors: SchemaValidationError | undefined;
orderErrors: string[] | undefined;
eip721DomainStatus: 'success' | 'failure' | undefined;
}) => {
console.log('eip721DomainStatus', eip721DomainStatus);
if (formattedSchemaValidationErrors) {
return formattedSchemaValidationErrors?.map((error) => {
return (
Expand Down Expand Up @@ -43,6 +46,21 @@ export const ErrorDisplay = ({
</div>
);
});
} else if (eip721DomainStatus === 'failure') {
return (
<div className="flex flex-row my-4 text-wrap">
<div className="p-3 h-1/2 rounded-full border">
<FaExclamation size={12} />
</div>
<div className="flex flex-col ml-4 text-[13px]">
<p className="mb-1.5 font-bold">swapContract</p>
<p className="text-textDark font-normal">
Error reading eip721Domain smart contract function. Double check
your contract address
</p>
</div>
</div>
);
} else {
return undefined;
}
Expand Down
5 changes: 1 addition & 4 deletions src/features/ui/loadingOrFailed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ export const LoadingOrFailed = ({
return (
<>
<div className="flex flex-col items-center justify-center my-20">
<div
id="circle"
className="flex justify-center items-center h-[100px] w-[100px] bg-[#171A23] rounded-full"
>
<div className="flex justify-center items-center h-[100px] w-[100px] bg-[#171A23] rounded-full">
{isShowLoadingState && <FiXSquare size={22} />}
{isEmptyState && <MdOutlineLibraryBooks size={22} />}
</div>
Expand Down
3 changes: 2 additions & 1 deletion src/features/ui/noErrorDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export const NoErrorDisplay = ({
orderErrors: string[] | undefined;
}) => {
const isNoErrors =
!formattedSchemaValidationErrors && orderErrors?.length === 0;
!formattedSchemaValidationErrors &&
(orderErrors?.length === 0 || !orderErrors);

if (isNoErrors) {
return (
Expand Down
9 changes: 3 additions & 6 deletions src/features/ui/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const Select = () => {
<RadixSelect.Item
value={chain.value}
key={chain.value}
className="py-1 px-2 bg-background text-lightGray border border-blueExtraDark first:rounded-t-sm last:rounded-b-sm hover:bg-blueExtraDark hover:border-blueExtraDark active:border-white"
className="py-1 px-2 bg-background text-lightGray first:rounded-t-sm last:rounded-b-sm hover:bg-blueExtraDark"
>
<RadixSelect.ItemText>
{chain.label}: {chain.value}
Expand All @@ -37,8 +37,6 @@ export const Select = () => {
}
};

// console.log(isSelectDisabled, selectedChainId);

return (
<>
<RadixSelect.Root
Expand All @@ -49,14 +47,13 @@ export const Select = () => {
onOpenChange={handleIsSelectOpen}
>
<RadixSelect.Trigger
className="flex items-center px-3 py-1 bg-blueGray border border-blueGray rounded-md font-semibold uppercase"
className="flex items-center px-3 py-1 bg-blueGray border border-blueExtraDark rounded-sm font-semibold uppercase"
aria-label="chain id"
>
<RadixSelect.Value
placeholder={
isSelectDisabled ? selectedChainId.toString() : 'chain Id'
}
// defaultValue={isSelectDisabled ? selectedChainId.toString() : undefined}
/>

<RadixSelect.Icon className="ml-2">
Expand All @@ -74,7 +71,7 @@ export const Select = () => {
<RadixSelect.Portal>
<RadixSelect.Content
position="popper"
className="h-[340px] border border-blueExtraDark rounded-md"
className="h-[340px] rounded-md border"
>
<RadixSelect.ScrollUpButton />
<RadixSelect.Viewport>{options}</RadixSelect.Viewport>
Expand Down
15 changes: 13 additions & 2 deletions src/hooks/useDomainInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,22 @@ import { useReadContracts } from 'wagmi';
import { swapErc20Abi } from '@/abi/swapErc20Abi';
import { useContractAddress } from './useContractAddress';

export const useDomainInfo = (chainId: number | undefined) => {
/**
*
* @param swapContract this is inputted by a user and will override `address` obtained from `useContractAddress` hook if a user enters one
* @returns
*/
export const useDomainInfo = ({
chainId,
swapContract,
}: {
chainId: number | undefined;
swapContract: string | undefined;
}) => {
const address = useContractAddress({ chainId });

const wagmiContractConfig = {
address,
address: (swapContract as `0x${string}`) || address,
abi: swapErc20Abi,
};

Expand Down
31 changes: 28 additions & 3 deletions src/hooks/useValidateOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,35 @@ import { hexToString } from 'viem';
import { useCheckOrder } from './useCheckOrder';
import { signedOrderSchema } from '../utils/orderSchema';
import { parseOrderFromUrl } from '../utils/parseOrderFromUrl';
import { useSelectStore } from '@/store/store';
import {
ChainStore,
SwapContractAddressStore,
useChainStore,
useSelectStore,
useSwapContractAddressStore,
} from '@/store/store';
import { SelectStore } from '@/store/store';

export const useValidateOrder = ({
order,
isUrl,
swapContract,
onSetChain,
}: {
order?: string;
isUrl: boolean;
swapContract: string | undefined;
onSetChain?: (chainId: number) => void;
}) => {
const setIsSelectDisabled = useSelectStore(
(state: SelectStore) => state.setIsSelectDisabled
);
const selectedChainId = useChainStore(
(state: ChainStore) => state.selectedChainId
);
const setSwapContractAddress = useSwapContractAddressStore(
(state: SwapContractAddressStore) => state.setSwapContractAddress
);

let _order;
let orderParsingError;
Expand All @@ -32,19 +46,30 @@ export const useValidateOrder = ({

const schemaValid = schemaValidationResult.success;

// if schema contains a chainId, set set `setSelectedChainId` with that. `setSelectedChainId` lives in the chain Store
if (schemaValid && schemaValidationResult.data.chainId) {
const chainId = schemaValidationResult.data.chainId;
// if chainId is present, disable the selector
setIsSelectDisabled(true);
onSetChain?.(chainId);
} else {
// if scheme does not contain chainId, enable the selector
setIsSelectDisabled(false);
}

// if swapContractAddress is present, we want to pass it into the store, then into `usecontractAddress` hook to set eip721domain info
if (schemaValid && schemaValidationResult.data.swapContract) {
console.log('custom swapAddress');
const contractAddress = schemaValidationResult.data.swapContract;
setSwapContractAddress(contractAddress);
console.log(contractAddress);
}

const { data: orderErrors, error: contractCallError } = useCheckOrder({
// FIXME: don't hardcode this - take from order if supplied, or get default for current chain.
swapContract: {
chainId: 11155111,
address: '0xD82E10B9A4107939e55fCCa9B53A9ede6CF2fC46',
chainId: selectedChainId,
address: swapContract as `0x${string}`,
},
enabled: schemaValid,
order: schemaValid ? schemaValidationResult.data : undefined,
Expand Down
12 changes: 12 additions & 0 deletions src/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ export interface ChainStore {
setSelectedChainId: (chain: number) => void;
}

export interface SwapContractAddressStore {
swapContractAddress: string | undefined;
setSwapContractAddress: (address: string) => void;
}

export const useSelectStore = create<SelectStore>((set) => ({
isSelectDisabled: false,
setIsSelectDisabled: (disabled) => set({ isSelectDisabled: disabled }),
Expand All @@ -29,3 +34,10 @@ export const useAppStore = create<AppStore>((set) => ({
isCheckEnabled: false,
setIsCheckEnabled: (enabled) => set({ isCheckEnabled: enabled }),
}));

export const useSwapContractAddressStore = create<SwapContractAddressStore>(
(set) => ({
swapContractAddress: undefined,
setSwapContractAddress: (address) => set({ swapContractAddress: address }),
})
);

0 comments on commit a164681

Please sign in to comment.