Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: style issue, add next ICR on indexer profile, lazy collect status, add approve allowance if user set a invalid value #689

Merged
merged 8 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# SubQuery Network App

dApp for indexers, consumers and delegators interact with SubQuery Network. You can find this at [https://kepler.subquery.network](https://kepler.subquery.network/)
dApp for indexers, consumers and delegators interact with SubQuery Network. You can find this at
[https://app.subquery.network](https://app.subquery.network/)
1 change: 1 addition & 0 deletions src/components/IndexerDetails/PlansTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ const DoPurchase: React.FC<DoPurchaseProps> = ({
]}
text={modalText}
onClick={() => purchasePlan(plan.creator, last(plan.id.split(':')))}
allowanceContractAddress={ApproveContract.PlanManager}
renderContent={(onSubmit, onCancel, isLoading, error) => {
return renderAsync(planManagerAllowance.result, {
loading: () => <Spinner />,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ export const ModalClaimIndexerRewards: React.FC<IModalClaimIndexerRewards> = ({
try {
setIsLoading(true);
assert(contracts, 'Contracts not available');
// const batchCollectAndDistribute = await contracts.rewardsHelper.batchCollectAndDistributeRewards(indexer, 20);
// const batchCollectAndDistributeResult = await batchCollectAndDistribute.wait();

const approvalTx = await contracts.rewardsHelper.indexerCatchup(indexer);
const approvalTxResult = await approvalTx.wait();

Expand Down
9 changes: 7 additions & 2 deletions src/components/ProjectOverview/ProjectOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,12 @@ const ProjectOverview: React.FC<Props> = ({ project, metadata, deploymentDescrip

return (
<div className={styles.container}>
<div style={{ display: 'flex', flexDirection: 'column', flex: 5 }}>
{/* note the width on this element, if inner elements need width:100% to overflow or ...,
need add a width on the parent elements, flex-grow can make it dynamic,
width: 1px equal 500px,
but suggest add a reasonable value in case this rule change in the future
*/}
<div style={{ display: 'flex', flexDirection: 'column', flex: 5, width: 500 }}>
<div style={{ width: '100%' }}>
<Expand>
<Markdown.Preview>{metadata.description || 'N/A'}</Markdown.Preview>
Expand Down Expand Up @@ -220,7 +225,7 @@ const ProjectOverview: React.FC<Props> = ({ project, metadata, deploymentDescrip
<Typography variant="large" weight={600}>
{t('projectOverview.deploymentDescription')}
</Typography>
<div style={{ width: 670, marginTop: 8 }}>
<div style={{ width: '100%', marginTop: 8 }}>
<Expand>
<Markdown.Preview>{deploymentDescription || 'N/A'}</Markdown.Preview>
</Expand>
Expand Down
23 changes: 21 additions & 2 deletions src/components/TransactionModal/TransactionModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { MdErrorOutline } from 'react-icons/md';
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import { ApproveContract } from '@components/ModalApproveToken';
import { NotificationType, openNotification } from '@components/Notification';
import { useSQToken } from '@containers';
import { ContractTransaction } from '@ethersproject/contracts';
import { useAddAllowance } from '@hooks/useAddAllowance';
import { Button } from '@subql/components';
import { ButtonProps } from '@subql/components/dist/common/button/Button';
import { Tooltip } from 'antd';
import clsx from 'clsx';
import { constants } from 'ethers';

import { AsyncData, parseError } from '../../utils';
import { AsyncData, isInsufficientAllowance, parseError } from '../../utils';
import { Modal } from '../Modal';
import { ModalInput } from '../ModalInput';
import styles from './TransactionModal.module.css';
Expand Down Expand Up @@ -75,6 +79,7 @@ export type TransactionModalProps<P, T extends string> = {
className?: string;
buttonClassName?: string;
currentConfirmButtonLoading?: boolean;
allowanceContractAddress?: ApproveContract;
};

export interface TransactionModalRef {
Expand Down Expand Up @@ -108,6 +113,7 @@ const TransactionModal = React.forwardRef<TransactionModalRef, TransactionModalP
className = '',
buttonClassName = '',
currentConfirmButtonLoading = false,
allowanceContractAddress = ApproveContract.Staking,
},
ref,
) => {
Expand All @@ -117,6 +123,9 @@ const TransactionModal = React.forwardRef<TransactionModalRef, TransactionModalP
const [successModalText, setSuccessModalText] = React.useState<string | undefined>();
const [failureModalText, setFailureModalText] = React.useState<string | undefined>();

const { addAllowance } = useAddAllowance();
const { balance } = useSQToken();

React.useEffect(() => {
if (initialCheck) {
const { error } = initialCheck;
Expand Down Expand Up @@ -151,9 +160,19 @@ const TransactionModal = React.forwardRef<TransactionModalRef, TransactionModalP
const wrapTxAction = (action: typeof onClick, rethrow?: boolean) => async (params: string) => {
try {
if (!showModal || !action) return;
let tx: ContractTransaction;

const tx = await action(params, showModal);
setIsLoading(true);
try {
tx = await action(params, showModal);
} catch (e: any) {
if (isInsufficientAllowance(e)) {
await addAllowance(allowanceContractAddress, (balance.result.data || constants.MaxUint256)?.toString());
tx = await action(params, showModal);
} else {
throw e;
}
}
resetModal();
openNotification({ title: t('transaction.submmited') });
const result = await tx.wait();
Expand Down
32 changes: 32 additions & 0 deletions src/hooks/useAddAllowance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: Apache-2.0

import { ApproveContract } from '@components';
import { openNotification } from '@subql/components';

import { useWeb3Store } from 'src/stores';

export const useAddAllowance = () => {
const { contracts } = useWeb3Store();

const addAllowance = async (contractName: ApproveContract, allowance: string) => {
try {
if (!contracts) throw new Error('Contracts not available');
openNotification({
type: 'info',
description: 'Allowance not enough, increase allowance first',
duration: 5000,
});
const tx = await contracts.sqToken.approve(contracts[contractName].address, allowance);
await tx?.wait();
return tx;
} catch (e) {
console.error(e);
throw e;
}
};

return {
addAllowance,
};
};
22 changes: 16 additions & 6 deletions src/hooks/useEra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,22 @@ export function useEra(): {
limitContract(() => eraManager.eraStartTime(), makeCacheKey('eraStartTime')),
]);

const era: Era = {
startTime: bnToDate(startTime),
estEndTime: bnToDate(startTime.add(period)),
period: period.toNumber(),
index: index.toNumber(),
};
let era: Era;
if (startTime && period && index) {
era = {
startTime: bnToDate(startTime),
estEndTime: bnToDate(startTime.add(period)),
period: period.toNumber(),
index: index.toNumber(),
};
} else {
era = {
startTime: new Date(),
estEndTime: new Date(),
period: 0,
index: 0,
};
}

return era;
}, [contracts]);
Expand Down
1 change: 0 additions & 1 deletion src/hooks/useRewardCollectStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export function useRewardCollectStatus(

const fetchStatus = async (_?: boolean) => {
if (!contracts) return false;

try {
setLoading(true);
const lastClaimedEra = await limitContract(
Expand Down
3 changes: 2 additions & 1 deletion src/pages/consumer/OfferMarketplace/AcceptOffer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import dayjs from 'dayjs';

import { useWeb3Store } from 'src/stores';

import { DeploymentInfo, SummaryList } from '../../../components';
import { ApproveContract, DeploymentInfo, SummaryList } from '../../../components';
import TransactionModal from '../../../components/TransactionModal';
import { useWeb3 } from '../../../containers';
import {
Expand Down Expand Up @@ -220,6 +220,7 @@ export const AcceptOffer: React.FC<Props> = ({ deployment, offer, requiredBlockH
onSuccess={() => onAcceptOffer()}
onClick={handleClick}
onClose={() => setCurStep(0)}
allowanceContractAddress={ApproveContract.PurchaseOfferMarket}
renderContent={(onSubmit, _, isLoading, error) => {
if (curStep === 0) {
return <OfferSummary curStep={curStep} onNext={() => setCurStep(curStep + 1)} offer={offer} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export const RewardsLineChart = (props: { account?: string; title?: string; data
dataDimensionsName={dataDimensionsName}
chartData={renderRewards}
onTriggerTooltip={(index, curDate) => {
return `<div class="col-flex" style="width: 280px">
return `<div class="col-flex" style="width: 280px; font-size: 12px;">
<span>${curDate.format('MMM D, YYYY')}</span>
<div class="flex-between" style="margin-top: 8px;">
<span>Total</span>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/delegator/DoDelegate/DoDelegate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const DoDelegate: React.FC<DoDelegateProps> = ({
const { contracts } = useWeb3Store();
const { stakingAllowance } = useSQToken();
const requireTokenApproval = useMemo(() => stakingAllowance?.result.data?.isZero(), [stakingAllowance?.result.data]);
const rewardClaimStatus = useRewardCollectStatus(indexerAddress);
const rewardClaimStatus = useRewardCollectStatus(indexerAddress, true);

// note why we don't use useGetIndexerLazy.
// In apollo-client, if two different query use same fragment, and the query result in the two query is different,
Expand Down
4 changes: 3 additions & 1 deletion src/pages/delegator/DoUndelegate/DoUndelegate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ export const DoUndelegate: React.FC<DoUndelegateProps> = ({ indexerAddress, onSu
};

return renderAsync(mergeAsync(rewardClaimStatus, lockPeriod, delegation), {
error: (error) => '',
error: (error) => {
return '';
},
loading: () => <Spinner />,
data: (data) => {
const [indexerRewards, lock, targetDelegation] = data;
Expand Down
4 changes: 2 additions & 2 deletions src/pages/delegator/MyDelegation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,15 +157,15 @@ export const MyDelegation: React.FC = () => {
// TODO: sort by GraphQL
.sort((a, b) => (`${a.id}` > `${b.id}` ? -1 : 1))
.map((delegation) => ({
value: mapEraValue(parseRawEraValue(delegation?.amount as RawEraValue, era?.index), (v) =>
value: mapEraValue(parseRawEraValue((delegation?.amount as RawEraValue) || '0', era?.index), (v) =>
formatEther(v ?? 0),
),
indexer: delegation.indexerId,
indexerActive: delegation?.indexer?.active,
}))
.filter(
(delegation) =>
parseEther(delegation.value.current).gt('0') || parseEther(delegation?.value?.after ?? '0').gt('0'),
parseEther(delegation.value.current || '0').gt('0') || parseEther(delegation?.value?.after ?? '0').gt('0'),
),
mergeAsync(delegations, currentEra),
);
Expand Down
16 changes: 15 additions & 1 deletion src/pages/indexer/IndexerProfile/IndexerProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { notEmpty, parseError } from '@utils';
import { isRPCError } from '@utils';
import { TOKEN } from '@utils/constants';
import { formatNumber, formatSQT, truncateToDecimalPlace } from '@utils/numberFormatters';
import { Skeleton, Tag } from 'antd';
import { Skeleton, Tag, Tooltip } from 'antd';
import clsx from 'clsx';
import { toChecksumAddress } from 'ethereum-checksum-address';
import { constants } from 'ethers';
Expand Down Expand Up @@ -280,6 +280,20 @@ const IndexerProfile: FC = () => {
{getCommission(fetchedResult?.indexer?.commission || 0, currentEra.data?.index).current} %
</Typography>
</div>
<div className={clsx(styles.cardContentLine, 'flex-between')}>
<Typography variant="small" style={{ visibility: 'hidden' }}>
bigo
</Typography>
<Tooltip title="Estimated for next Era">
<Typography
variant="small"
type="secondary"
style={{ transform: 'scale(0.83333) translateX(7px)', marginLeft: 3 }}
>
{getCommission(fetchedResult?.indexer?.commission || 0, currentEra.data?.index).after} %
</Typography>
</Tooltip>
</div>

<div className={clsx(styles.cardContentLine, 'flex-between')}>
<Typography variant="small" type="secondary">
Expand Down
26 changes: 25 additions & 1 deletion src/utils/parseError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,23 @@ export const isRPCError = (msg: Error | string | undefined): boolean => {
return false;
};

export const isInsufficientAllowance = (msg: Error | string | undefined): boolean => {
if (!msg) return false;

if (msg instanceof Error) {
if (msg.message.includes('insufficient allowance')) {
return true;
}
return false;
}

if (msg.includes('insufficient allowance')) {
return true;
}

return false;
};

export function parseError(
error: any,
options: { alert?: boolean; defaultGeneralMsg?: string | null; errorMappings?: typeof errorsMapping } = {
Expand Down Expand Up @@ -136,6 +153,12 @@ export function parseError(
}
};

const insufficientAllowance = () => {
if (isInsufficientAllowance(rawErrorMsg)) {
return 'Insufficient allowance. Please set a reasonable value when set spending cap';
}
};

const generalErrorMsg = () => {
try {
if (!rawErrorMsg.includes('Failed to fetch')) {
Expand All @@ -158,9 +181,10 @@ export function parseError(
mappingError() ??
mapContractError() ??
userDeniedSignature() ??
callRevert() ??
insufficientAllowance() ??
RpcUnavailableMsg() ??
insufficientFunds() ??
callRevert() ??
options.defaultGeneralMsg ??
generalErrorMsg()
);
Expand Down
Loading