Skip to content

Instantly share code, notes, and snippets.

@kumarparth380
Last active December 21, 2023 03:03
Show Gist options
  • Save kumarparth380/b7ecdc19037267b3011e1576d26b6724 to your computer and use it in GitHub Desktop.
Save kumarparth380/b7ecdc19037267b3011e1576d26b6724 to your computer and use it in GitHub Desktop.
Custom Hooks with Typescript
import { MutableRefObject, useCallback, useContext, useMemo, useRef } from "react";
import {
AsyncState,
ethErrorHandler,
useAsync,
useEthAddress,
useLibrary,
useSigner,
useTokenCtx
} from ".";
import { CreateCampaignContext, CreateCampaignCtx } from "../context/campaign";
import { DeployCampaignForm, getAmountStaked, getMaxRewardTokens, TokenGetter } from "../contracts";
import { Campaign, CampaignStatus, StakingRewardToken } from "../core";
import { CampaignCtx } from "../providers/campaigns";
import {
actionDeployCampaign,
actionDepositRewards,
actionGetCampaign,
actionGetCampaigns,
actionGetOwnerCampaigns,
actionGetUserRewards,
actionSetAmountStaked,
actionSetCampaignActivity,
actionUpdateStatus
} from "../state/campaigns/actions";
import { CampaignState } from "../state/campaigns/reducer";
import { Action } from "../state/common";
import { Signer } from "@ethersproject/abstract-signer";
import { Provider } from "@ethersproject/providers";
import { useMaybeGetToken } from "./tokens";
import { isSecondsAgo, parseValue } from "../util";
import { BigNumber } from "@ethersproject/bignumber";
import {
selectCampaign,
selectCampaignsByStatus,
selectUserRewardsByCampaign,
selectUserRewardsByCampaignToken
} from "../state/campaigns/selectors";
import { ContractTransaction } from "@ethersproject/contracts";
import { selectTokenByAddress } from "../state/tokens/selectors";
import { defaultEthErrorMessages } from "../error";
export const useCampaignCtx = () => useContext(CampaignCtx);
export const useCampaigns = (status?: CampaignStatus): string[] => {
const { state } = useCampaignCtx();
return status ? selectCampaignsByStatus(state, status) : state.addresses;
};
export const useCampaign = (address: string): Campaign => {
const { state } = useCampaignCtx();
return selectCampaign(state, address);
};
export const useCreateCampaignCtx = (): CreateCampaignContext => useContext(CreateCampaignCtx);
// get campaigns by owner
export const useGetOwnerCampaigns = (address: string) =>
useCampaignGetter((tokenGetter, provider) =>
actionGetOwnerCampaigns(address, tokenGetter, provider)
);
// get all campaigns
export const useGetCampaigns = () =>
useCampaignGetter((tokenGetter, provider) => actionGetCampaigns(tokenGetter, provider));
// utility hook to interact with campaign getter actions
// ensures token requests are optimized
const useCampaignGetter = (
actionGetter: (
tokenGetter: TokenGetter,
provider: Provider | Signer
) => Promise<Action<CampaignState>>
) => {
const { dispatch } = useCampaignCtx();
const [getToken, dispatchTokens] = useMaybeGetToken();
const lib = useLibrary();
const lastRun = useRef<Date | null>(null);
return useCallback(
async (onSuccess?: () => void, onFail?: () => void) => {
try {
if (!lib || (lastRun.current && isSecondsAgo(lastRun.current, 10))) return;
lastRun.current = new Date();
const saveCampaigns = await actionGetter(getToken, lib);
dispatchTokens();
dispatch(saveCampaigns);
onSuccess && onSuccess();
} catch (err) {
console.log("get campaign error: ", err);
onFail && onFail();
}
},
[lib, dispatch, actionGetter, getToken, dispatchTokens]
);
};
// get single campaign based on address
export const useGetCampaign = () => {
const { dispatch } = useCampaignCtx();
const lib = useLibrary();
const [getToken, dispatchTokens] = useMaybeGetToken();
const lastRun = useRef<Date | null>(null);
return useCallback(
async (address: string) => {
try {
if (!lib || (lastRun.current && isSecondsAgo(lastRun.current, 10))) {
return;
}
lastRun.current = new Date();
dispatch(actionSetCampaignActivity(address, true));
const action = await actionGetCampaign(address, getToken, lib);
dispatchTokens();
dispatch(action);
} catch (err) {
console.log("err: ", err);
} finally {
dispatch(actionSetCampaignActivity(address, false));
}
},
[dispatch, lib, getToken, dispatchTokens]
);
};
export const useDeployCampaign = (): ((form: DeployCampaignForm) => Promise<string | null>) => {
const { dispatch } = useCampaignCtx();
const signer = useSigner();
const [getToken, dispatchTokens] = useMaybeGetToken();
return useCallback(
async (form: DeployCampaignForm): Promise<string | null> => {
try {
if (!signer) return null;
const action = await actionDeployCampaign(form, getToken, signer);
dispatchTokens();
dispatch(action);
return action.payload.addresses[0] ? action.payload.addresses[0] : null;
} catch (err) {
return null;
}
},
[signer, dispatch, getToken, dispatchTokens]
);
};
export const useDepositRewards = () => {
const { state, dispatch } = useCampaignCtx();
const signer = useSigner();
return useCallback(
async (
tokenAddr: string,
value: BigNumber,
campaignAddr: string
): Promise<ContractTransaction | null> => {
try {
const campaign = selectCampaign(state, campaignAddr);
if (!signer || !campaign.address) return null;
const [action, tx] = await actionDepositRewards(tokenAddr, value, campaign, signer);
dispatch(action);
return tx;
} catch (err) {
console.log(err);
return null;
}
},
[state, signer, dispatch]
);
};
export const useCheckCampaignStatus = () => {
const lib = useLibrary();
const { state, dispatch } = useCampaignCtx();
const lastRun = useRef<Date | null>(null);
const lastCampaign = useRef<string>("");
return useCallback(
async (campaignAddr: string) => {
try {
if (
!lib ||
(lastRun.current &&
isSecondsAgo(lastRun.current, 10) &&
campaignAddr === lastCampaign.current)
)
return;
lastRun.current = new Date();
lastCampaign.current = campaignAddr;
const campaign = selectCampaign(state, campaignAddr);
const action = await actionUpdateStatus(campaign, lib);
dispatch(action);
} catch (err) {
console.log(err);
}
},
[lib, state, dispatch]
);
};
export const useCheckUserRewards = () => {
const addr = useEthAddress();
const signer = useSigner();
const { state, dispatch } = useCampaignCtx();
const lastRun = useRef<Date | null>(null);
return useCallback(
async (campaignAddr: string) => {
try {
const campaign = selectCampaign(state, campaignAddr);
if (!signer || !addr || !campaign.address || throttledBlock(lastRun, 3)) return;
lastRun.current = new Date();
const action = await actionGetUserRewards(addr, campaign, signer);
dispatch(action);
} catch (err) {
console.log(err);
}
},
[addr, signer, state, dispatch]
);
};
const throttledBlock = (lastRun: MutableRefObject<Date | null>, seconds: number) =>
lastRun.current && isSecondsAgo(lastRun.current, seconds);
export const useGetMaxRewardTokens = () => {
const library = useLibrary();
const { state } = useCampaignCtx();
return useCallback(
async (campaignAddr: string, stakeAmount: BigNumber) => {
try {
const campaign = selectCampaign(state, campaignAddr);
if (!library || !stakeAmount || isNaN(Number(stakeAmount))) return;
const maxRewardTokens = await getMaxRewardTokens(campaign, stakeAmount, library);
return maxRewardTokens;
} catch (err) {
console.log(err);
}
},
[library, state]
);
};
export const useUserRewards = (campaignAddr: string) => {
const addr = useEthAddress();
const { state } = useCampaignCtx();
return selectUserRewardsByCampaign(state, addr || "", campaignAddr);
};
export const useGetAmountStaked = (): [
(campaignAddr: string, tokenAddr: string) => Promise<void>,
AsyncState
] => {
const addr = useEthAddress();
const signer = useSigner();
const asyncState = useAsync();
const { managedExec } = asyncState;
const { dispatch } = useCampaignCtx();
const _getAmount = useCallback(
async (campaignAddr: string, tokenAddr: string) => {
if (!addr || !signer || !campaignAddr || !tokenAddr) return;
const amount = await managedExec(
() => getAmountStaked(addr, campaignAddr, signer),
ethErrorHandler(defaultEthErrorMessages, "failed to get amount staked")
);
if (amount) {
dispatch(actionSetAmountStaked(campaignAddr, addr, tokenAddr, amount));
}
},
[signer, addr, managedExec, dispatch]
);
return [_getAmount, asyncState];
};
export const useDeployCampaignForm = (): DeployCampaignForm => {
const { values } = useCreateCampaignCtx();
const { state } = useTokenCtx();
const rewards = useMemo<StakingRewardToken[]>(() => {
return values.rewards.map((r) => {
const token = selectTokenByAddress(state, r.address);
const value = parseValue(r.value, token.decimals);
return {
address: token.address,
value
};
});
}, [values.rewards, state]);
return {
rewards,
start: values.start,
end: values.end,
stakeTokenAddress: values.stakeTokenAddress
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment