Skip to content

Commit

Permalink
refactor: use multicall to read all contracts info at once
Browse files Browse the repository at this point in the history
  • Loading branch information
JeanNeiverth committed Sep 25, 2024
1 parent bc8db21 commit 70b44c5
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 86 deletions.
11 changes: 3 additions & 8 deletions apps/claim-vesting/src/app/ClaimVestingApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export function ClaimVestingApp() {
const { actions, provider } = initCoWHookDapp({ onContext: setContext });
const web3Provider = new Web3Provider(provider);
const signer = web3Provider.getSigner();
console.log(provider);

setActions(actions);
setSigner(signer);
Expand All @@ -48,10 +47,6 @@ export function ClaimVestingApp() {
const { errorMessage, formattedClaimableAmount, tokenSymbol, loading } =
useClaimVestingData({ chainId, account, debouncedAddress });

useEffect(() => {
console.log("errorMessage", errorMessage);
}, [errorMessage]);

return (
<Wrapper>
<button onClick={toggleTheme} className="p-2 text-yellow-700">
Expand All @@ -73,11 +68,11 @@ export function ClaimVestingApp() {
</Row>
</ContentWrapper>
{errorMessage ? (
<span className="text-center mb-6">{errorMessage}</span>
<span className="text-center my-[25px]">{errorMessage}</span>
) : loading ? (
<span>Loading...</span>
<span className="text-center my-[25px]">Loading...</span>
) : (
<ButtonPrimary>
<ButtonPrimary disabled={debouncedAddress === ""}>
<span>Add hook</span>
</ButtonPrimary>
)}
Expand Down
157 changes: 92 additions & 65 deletions apps/claim-vesting/src/hooks/useClaimVestingData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,46 +41,60 @@ export const useClaimVestingData = ({
[chainId]
);

/// ---------------- VESTING DATA ----------------
/// ---------------- VESTING DATA ---------------- ///

const readVesting = useCallback(
async (address: Address) => {
const claimableAmountWei =
publicClient &&
(await publicClient.readContract({
address: address,
abi: VestingEscrowAbi,
functionName: "unclaimed",
}));
const vestingContract = {
address: address,
abi: VestingEscrowAbi,
} as const;

const recipient =
const vestingResults =
publicClient &&
(await publicClient.readContract({
address: address,
abi: VestingEscrowAbi,
functionName: "recipient",
}));
const tokenAddress =
publicClient &&
(await publicClient.readContract({
address: address,
abi: VestingEscrowAbi,
functionName: "token",
(await publicClient.multicall({
contracts: [
{
...vestingContract,
functionName: "unclaimed",
},
{
...vestingContract,
functionName: "recipient",
},
{
...vestingContract,
functionName: "token",
},
],
}));

vestingResults?.forEach((result) => {
const status = result?.status;
const error = result?.error;

// When the address is not a vesting contract
if (
status === "failure" &&
error?.name === "ContractFunctionExecutionError"
)
throw new Error("Address is not a valid vesting contract");

// Other unexpected errors
if (status === "failure") throw new Error("Unexpected error");
});

return {
claimableAmountWei,
recipient,
tokenAddress,
claimableAmountWei: vestingResults && vestingResults[0],
recipient: vestingResults && vestingResults[1],
tokenAddress: vestingResults && vestingResults[2],
};
},
[publicClient]
);

const shouldFetchVesting = isAddress(debouncedAddress);

console.log("debouncedAddress", debouncedAddress);

const {
data: vestingData,
isLoading: isLoadingVesting,
Expand All @@ -93,41 +107,42 @@ export const useClaimVestingData = ({
refreshInterval: 0,
});

const claimableAmountWei = vestingData?.claimableAmountWei;
const recipient = vestingData?.recipient;
const tokenAddress = vestingData?.tokenAddress;

useEffect(() => {
console.log("vestingData", vestingData);
}, [vestingData]);

useEffect(() => {
console.log("vesting Error", errorVesting);
}, [errorVesting]);
const claimableAmountWei = vestingData?.claimableAmountWei?.result;
const recipient = vestingData?.recipient?.result;
const tokenAddress = vestingData?.tokenAddress?.result;

/// ---------------- TOKEN DATA ----------------
/// ---------------- TOKEN DATA ---------------- ///

const readToken = useCallback(
async (address: Address) => {
const symbol =
publicClient &&
(await publicClient.readContract({
address: address,
abi: erc20Abi,
functionName: "symbol",
}));
const tokenContract = {
address: address,
abi: erc20Abi,
} as const;

const decimals =
const tokenResults =
publicClient &&
(await publicClient.readContract({
address: address,
abi: erc20Abi,
functionName: "decimals",
(await publicClient.multicall({
contracts: [
{
...tokenContract,
functionName: "symbol",
},
{
...tokenContract,
functionName: "decimals",
},
],
}));

tokenResults?.forEach((result) => {
// Unexpected errors with token
if (result.status === "failure") throw new Error("Unexpected error");
});

return {
symbol: symbol,
decimals: decimals,
symbol: tokenResults && tokenResults[0],
decimals: tokenResults && tokenResults[1],
};
},
[publicClient]
Expand All @@ -145,19 +160,20 @@ export const useClaimVestingData = ({
refreshInterval: 0,
});

const tokenSymbol = tokenData?.symbol ? String(tokenData?.symbol) : "";
const decimals = tokenData?.decimals; //TODO: treat decimals
const tokenSymbol = tokenData?.symbol?.result
? String(tokenData.symbol.result)
: "";
const decimals = tokenData?.decimals?.result;

/// --------------------------------------------
useEffect(() => {
console.log("tokenData", tokenData);
}, [tokenData]);
/// -------------------------------------------- ///

useEffect(() => {
console.log("tokenData Error", errorToken);
}, [errorToken]);

const errorMessage = getErrorMessage({ account, recipient });
const errorMessage = getErrorMessage({
debouncedAddress,
account,
recipient,
errorVesting,
errorToken,
});
const formattedClaimableAmount =
claimableAmountWei && decimals
? formatUnits(claimableAmountWei, Number(decimals))
Expand All @@ -169,16 +185,27 @@ export const useClaimVestingData = ({
function getErrorMessage({
account,
recipient,
errorVesting,
debouncedAddress,
errorToken,
}: {
account: string | undefined;
recipient: string | undefined;
debouncedAddress: string;
errorVesting: Error;
errorToken: Error;
}) {
console.log(account);
console.log(recipient);
if (!(isAddress(debouncedAddress) || debouncedAddress === ""))
return "Insert a valid Ethereum address";

if (!account) return "Please connect your wallet first";

if (account !== recipient) return "You are not the contract recipient";
if (recipient && account !== recipient)
return "You are not the contract recipient";

if (errorVesting) return errorVesting.message;

if (errorToken) return errorToken.message;

return undefined;
}
5 changes: 5 additions & 0 deletions packages/cow-hooks-ui/src/AddressInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ const Label = ({ label }: { label: string }) => (
export const AddressInput = ({
theme,
label,
errorMessage,
onChange,
...props
}: {
theme: string;
label: string;
errorMessage?: string;
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
}) => (
<div className="flex flex-col w-full">
Expand All @@ -29,5 +31,8 @@ export const AddressInput = ({
)}
{...props}
/>
<span className="text-red-700 text-sm text-start ml-2.5 mt-1">
{errorMessage}
</span>
</div>
);
24 changes: 11 additions & 13 deletions packages/cow-hooks-ui/src/ButtonPrimary.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ReactNode } from "react";

import clsx from "clsx";
export const ButtonPrimary = ({
children,
disabled = false,
Expand All @@ -9,19 +9,17 @@ export const ButtonPrimary = ({
disabled?: boolean;
}) => (
<button
className={`
bg-color-primary text-color-button-text text-lg font-semibold border-none shadow-none rounded-2xl
relative min-h-[58px] transition-colors duration-200 ease-in-out mt-2 mb-2 flex flex-row flex-wrap items-center justify-center
focus:shadow-none focus:transform-none focus:bg-color-primary-lighter
hover:shadow-none hover:transform-none hover:bg-color-primary-lighter
active:shadow-none active:transform-none active:bg-color-primary-lighter
disabled:border-transparent disabled:outline-none disabled:cursor-auto disabled:shadow-none
${
disabled
? "disabled:bg-color-bg2 disabled:text-color-white disabled:opacity-70"
: "disabled:bg-color-background disabled:text-color-info disabled:opacity-100"
className={clsx(
"flex flex-row flex-wrap items-center justify-center text-lg font-semibold border-none shadow-none rounded-2xl relative min-h-[58px] mt-2 mb-2",
{
"bg-color-primary text-color-button-text transition-colors duration-200 ease-in-out focus:shadow-none focus:transform-none focus:bg-color-primary-lighter hover:shadow-none hover:transform-none hover:bg-color-primary-lighter active:shadow-none active:transform-none active:bg-color-primary-lighter":
!disabled,
},
{
"border-transparent outline-none cursor-auto shadow-none opacity-80 bg-color-primary text-color-button-text text-opacity-80":
disabled,
}
`}
)}
disabled={disabled}
{...props}
>
Expand Down

0 comments on commit 70b44c5

Please sign in to comment.