-
Notifications
You must be signed in to change notification settings - Fork 13
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
[Feature] Fixed issues with max rewards + improved rewards section for audits #549
Changes from all commits
d847d46
c339e3c
b658db3
3a987be
9123729
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import { IVault, IVulnerabilitySeverity } from "@hats-finance/shared"; | ||
import { Pill, VaultNftRewardCard } from "components"; | ||
import { IVault, IVulnerabilitySeverity, IVulnerabilitySeverityV2 } from "@hats-finance/shared"; | ||
import InfoIcon from "@mui/icons-material/InfoOutlined"; | ||
import { Pill, VaultNftRewardCard, WithTooltip } from "components"; | ||
import { useSeverityRewardInfo } from "hooks/severities/useSeverityRewardInfo"; | ||
import { useTranslation } from "react-i18next"; | ||
import { formatNumber } from "utils"; | ||
|
@@ -14,20 +15,40 @@ interface VaultSeverityRewardCardProps { | |
|
||
export function VaultSeverityRewardCard({ vault, severity, severityIndex, noNft = false }: VaultSeverityRewardCardProps) { | ||
const { t } = useTranslation(); | ||
const { rewardPercentage, rewardPrice, rewardColor } = useSeverityRewardInfo(vault, severityIndex); | ||
const { rewardPercentage, rewardPrice, rewardCap, rewardColor } = useSeverityRewardInfo(vault, severityIndex); | ||
|
||
const severityName = severity?.name.toLowerCase().replace("severity", "") ?? ""; | ||
const showCap = vault.version === "v2" && vault.description?.severities.some((sev) => !!sev.capAmount); | ||
Comment on lines
+18
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The However, I noticed that you're using optional chaining ( |
||
|
||
return ( | ||
<StyledVaultSeverityRewardCard color={rewardColor} noNft={noNft}> | ||
<Pill isSeverity transparent textColor={rewardColor} text={severityName} /> | ||
<StyledVaultSeverityRewardCard columns={2 + (noNft ? 0 : 1) + (showCap ? 1 : 0)} color={rewardColor}> | ||
<div className="severity-name"> | ||
<Pill isSeverity transparent textColor={rewardColor} text={severityName} /> | ||
</div> | ||
<div className="severity-prize"> | ||
<div> | ||
<span>{`${rewardPercentage.toFixed(2)}%`}</span> | ||
<span className="tiny"> {t("ofVault")} </span> | ||
<span className="tiny"> {t("ofRewards")} </span> | ||
</div> | ||
<span className="price">~{`$${formatNumber(rewardPrice)}`}</span> | ||
</div> | ||
{showCap && ( | ||
<> | ||
{(severity as IVulnerabilitySeverityV2).capAmount ? ( | ||
<WithTooltip text={t("maxRewardCapExplanation")}> | ||
<div className="severity-prize"> | ||
<div className="title-container"> | ||
<span className="tiny">{t("maxRewardCap")}</span> | ||
<InfoIcon fontSize="small" /> | ||
</div> | ||
<span className="price">~{`$${formatNumber(rewardCap)}`}</span> | ||
</div> | ||
</WithTooltip> | ||
) : ( | ||
<div /> | ||
)} | ||
</> | ||
)} | ||
Comment on lines
23
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This section of code handles the rendering of the severity reward card. It includes the severity name, reward percentage, reward price, and optionally the reward cap and NFT reward card. The reward cap is only shown if One potential issue here is the casting of - {(severity as IVulnerabilitySeverityV2).capAmount ? (
+ {('capAmount' in severity && severity.capAmount) ? ( This change uses the |
||
{!noNft && ( | ||
<div className="severity-nft"> | ||
<VaultNftRewardCard vault={vault} severity={severity} type="tiny" /> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
import { formatUnits } from "ethers/lib/utils"; | ||
import { useVaultsTotalPrices } from "hooks/vaults/useVaultsTotalPrices"; | ||
import { IVault } from "types"; | ||
import { generateColorsArrayInBetween } from "utils/colors.utils"; | ||
|
@@ -15,60 +14,60 @@ export const getSeveritiesColorsArray = (vault: IVault | undefined): string[] => | |
export function useSeverityRewardInfo(vault: IVault | undefined, severityIndex: number) { | ||
const { totalPrices } = useVaultsTotalPrices(vault ? vault.multipleVaults ?? [vault] : []); | ||
|
||
if (!vault || !vault.description) return { rewardPrice: 0, rewardPercentage: 0, rewardColor: INITIAL_SEVERITY_COLOR }; | ||
if (!vault || !vault.description) | ||
return { rewardPrice: 0, rewardPercentage: 0, rewardCap: 0, rewardColor: INITIAL_SEVERITY_COLOR }; | ||
|
||
const isAudit = vault.description && vault.description["project-metadata"].type === "audit"; | ||
const showIntendedAmounts = vault.amountsInfo?.showCompetitionIntendedAmount ?? false; | ||
const SEVERITIES_COLORS = getSeveritiesColorsArray(vault); | ||
|
||
if (vault.version === "v2") { | ||
const severity = vault.description.severities[severityIndex]; | ||
if (!severity) return { rewardPrice: 0, rewardPercentage: 0, rewardColor: INITIAL_SEVERITY_COLOR }; | ||
if (!severity) return { rewardPrice: 0, rewardPercentage: 0, rewardCap: 0, rewardColor: INITIAL_SEVERITY_COLOR }; | ||
|
||
const sumTotalPrices = Object.values(totalPrices).reduce((a, b = 0) => a + b, 0); | ||
// const maxBountyPercentage = Number(vault.maxBounty) / 10000; // Number between 0 and 1; | ||
// TODO: remove this when we have the new vault contract version | ||
const maxBountyPercentage = Number(isAudit ? 10000 : vault.maxBounty) / 10000; | ||
const rewardPercentage = +severity.percentage * maxBountyPercentage; | ||
const rewardPercentage = +severity.percentage; | ||
|
||
let rewardPrice: number = 0; | ||
let rewardCap: number = 0; | ||
if (vault.multipleVaults && sumTotalPrices) { | ||
rewardPrice = sumTotalPrices * (rewardPercentage / 100); | ||
} else if (vault.amountsInfo?.tokenPriceUsd) { | ||
rewardPrice = | ||
(showIntendedAmounts | ||
? vault.amountsInfo.competitionIntendedAmount?.deposited.tokens ?? 0 | ||
: Number(formatUnits(vault.honeyPotBalance, vault.stakingTokenDecimals))) * | ||
: vault.amountsInfo.maxRewardAmount.tokens) * | ||
(rewardPercentage / 100) * | ||
vault.amountsInfo?.tokenPriceUsd; | ||
rewardCap = (severity.capAmount ?? 0) * vault.amountsInfo?.tokenPriceUsd; | ||
} | ||
|
||
const orderedSeverities = vault.description.severities.map((severity) => severity.percentage).sort((a, b) => a - b); | ||
const rewardColor: string = SEVERITIES_COLORS[orderedSeverities.indexOf(severity.percentage) ?? 0]; | ||
|
||
return { rewardPrice, rewardPercentage, rewardColor }; | ||
return { rewardPrice, rewardPercentage, rewardCap, rewardColor }; | ||
} else { | ||
Comment on lines
+17
to
48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The changes in this section seem to be well thought out. The addition of the - let rewardPrice: number = 0;
+ let rewardPrice: number = vault.amountsInfo?.tokenPriceUsd ?? DEFAULT_TOKEN_PRICE; |
||
const severity = vault.description.severities[severityIndex]; | ||
if (!severity) return { rewardPrice: 0, rewardPercentage: 0, rewardColor: INITIAL_SEVERITY_COLOR }; | ||
if (!severity) return { rewardPrice: 0, rewardPercentage: 0, rewardCap: 0, rewardColor: INITIAL_SEVERITY_COLOR }; | ||
|
||
const sumTotalPrices = Object.values(totalPrices).reduce((a, b = 0) => a + b, 0); | ||
const rewardPercentage = (Number(vault.rewardsLevels[severity.index]) / 10000) * 100; | ||
|
||
let rewardPrice: number = 0; | ||
let rewardCap: number = 0; | ||
if (vault.multipleVaults && sumTotalPrices) { | ||
rewardPrice = sumTotalPrices * (rewardPercentage / 100); | ||
} else if (vault.amountsInfo?.tokenPriceUsd) { | ||
rewardPrice = | ||
(showIntendedAmounts | ||
? vault.amountsInfo.competitionIntendedAmount?.deposited.tokens ?? 0 | ||
: Number(formatUnits(vault.honeyPotBalance, vault.stakingTokenDecimals))) * | ||
: vault.amountsInfo.maxRewardAmount.tokens) * | ||
(rewardPercentage / 100) * | ||
vault.amountsInfo?.tokenPriceUsd; | ||
} | ||
|
||
const orderedSeverities = vault.description.severities.map((severity) => severity.index).sort((a, b) => a - b); | ||
const rewardColor: string = SEVERITIES_COLORS[orderedSeverities.indexOf(severity.index) ?? 0]; | ||
|
||
return { rewardPrice, rewardPercentage, rewardColor }; | ||
return { rewardPrice, rewardPercentage, rewardCap, rewardColor }; | ||
Comment on lines
49
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import { formatUnits } from "@ethersproject/units"; | ||
import { IMaster, IPayoutGraph, IUserNft, IVault } from "@hats-finance/shared"; | ||
import { BigNumber } from "ethers"; | ||
import { BigNumber, ethers } from "ethers"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The import statement has been modified to include - import { BigNumber } from "ethers";
+ import { BigNumber, ethers } from "ethers"; |
||
import { appChains } from "settings"; | ||
|
||
export const parseMasters = (masters: IMaster[], chainId: number) => { | ||
|
@@ -49,14 +49,32 @@ export const populateVaultsWithPricing = (vaults: IVault[], tokenPrices: number[ | |
const isTestnet = appChains[vault.chainId].chain.testnet; | ||
const tokenPrice: number = isTestnet ? 1387.65 : (tokenPrices && tokenPrices[vault.stakingToken]) ?? 0; | ||
const depositedAmountTokens = Number(formatUnits(vault.honeyPotBalance, vault.stakingTokenDecimals)); | ||
const isAudit = vault.description?.["project-metadata"].type === "audit"; | ||
|
||
const maxRewardFactor = vault.version === "v1" ? +vault.rewardsLevels[vault.rewardsLevels.length - 1] : +vault.maxBounty; | ||
console.log(vault); | ||
|
||
const governanceSplit = BigNumber.from(vault.governanceHatRewardSplit).eq(ethers.constants.MaxUint256) | ||
? vault.master.defaultGovernanceHatRewardSplit | ||
: vault.governanceHatRewardSplit; | ||
const hackerHatsSplit = BigNumber.from(vault.hackerHatRewardSplit).eq(ethers.constants.MaxUint256) | ||
? vault.master.defaultHackerHatRewardSplit | ||
: vault.hackerHatRewardSplit; | ||
|
||
// In v2 vaults the split sum (immediate, vested, committee) is 100%. So we need to calculate the split factor to get the correct values. | ||
// In v1 this is not a probem. So the factor is 1. | ||
const splitFactor = vault.version === "v1" ? 1 : (10000 - Number(governanceSplit) - Number(hackerHatsSplit)) / 100 / 100; | ||
|
||
const governanceFee = Number(governanceSplit) / 100 / 100; | ||
const committeeFee = (Number(vault.committeeRewardSplit) / 100 / 100) * splitFactor; | ||
|
||
const maxRewardFactor = 1 - governanceFee - committeeFee; | ||
|
||
return { | ||
...vault, | ||
amountsInfo: { | ||
showCompetitionIntendedAmount: | ||
vault.description?.["project-metadata"].type === "audit" && | ||
isAudit && | ||
vault.description && | ||
Comment on lines
+52
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic for determining whether to show the competition intended amount has been updated. Previously, it was based solely on whether the vault type was "audit". Now, it also checks if the start time of the project is more than 48 hours in the future and if there is an intended competition amount defined. This makes the condition more robust and ensures that the competition intended amount is only shown when it is relevant. - showCompetitionIntendedAmount:
- vault.description?.["project-metadata"].type === "audit" &&
+ showCompetitionIntendedAmount:
+ isAudit &&
vault.description &&
vault.description["project-metadata"].starttime &&
vault.description["project-metadata"].starttime > new Date().getTime() / 1000 + 48 * 3600 && // 48 hours
!!vault.description?.["project-metadata"].intendedCompetitionAmount && |
||
vault.description["project-metadata"].starttime && | ||
vault.description["project-metadata"].starttime > new Date().getTime() / 1000 + 48 * 3600 && // 48 hours | ||
!!vault.description?.["project-metadata"].intendedCompetitionAmount && | ||
|
@@ -69,9 +87,8 @@ export const populateVaultsWithPricing = (vaults: IVault[], tokenPrices: number[ | |
usd: +vault.description?.["project-metadata"].intendedCompetitionAmount * tokenPrice, | ||
}, | ||
maxReward: { | ||
tokens: +vault.description?.["project-metadata"].intendedCompetitionAmount * (maxRewardFactor / 100 / 100), | ||
usd: | ||
+vault.description?.["project-metadata"].intendedCompetitionAmount * tokenPrice * (maxRewardFactor / 100 / 100), | ||
tokens: +vault.description?.["project-metadata"].intendedCompetitionAmount * maxRewardFactor, | ||
usd: +vault.description?.["project-metadata"].intendedCompetitionAmount * tokenPrice * maxRewardFactor, | ||
Comment on lines
+90
to
+91
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The calculation of the maximum reward in tokens and USD has been updated. Previously, the - tokens: +vault.description?.["project-metadata"].intendedCompetitionAmount * (maxRewardFactor / 100 / 100),
- usd:
- +vault.description?.["project-metadata"].intendedCompetitionAmount * tokenPrice * (maxRewardFactor / 100 / 100),
+ tokens: +vault.description?.["project-metadata"].intendedCompetitionAmount * maxRewardFactor,
+ usd: +vault.description?.["project-metadata"].intendedCompetitionAmount * tokenPrice * maxRewardFactor, |
||
}, | ||
} | ||
: undefined, | ||
|
@@ -80,8 +97,8 @@ export const populateVaultsWithPricing = (vaults: IVault[], tokenPrices: number[ | |
usd: depositedAmountTokens * tokenPrice, | ||
}, | ||
maxRewardAmount: { | ||
tokens: depositedAmountTokens * (maxRewardFactor / 100 / 100), | ||
usd: depositedAmountTokens * tokenPrice * (maxRewardFactor / 100 / 100), | ||
tokens: depositedAmountTokens * maxRewardFactor, | ||
usd: depositedAmountTokens * tokenPrice * maxRewardFactor, | ||
Comment on lines
+100
to
+101
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The calculation of the maximum reward amount in tokens and USD has been updated in the same way as the maximum reward. The changes are correct given the new calculation of - tokens: depositedAmountTokens * (maxRewardFactor / 100 / 100),
- usd: depositedAmountTokens * tokenPrice * (maxRewardFactor / 100 / 100),
+ tokens: depositedAmountTokens * maxRewardFactor,
+ usd: depositedAmountTokens * tokenPrice * maxRewardFactor, |
||
}, | ||
}, | ||
} as IVault; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CodeRabbit
The change from
depositedAmount
tomaxRewardAmount
seems appropriate given the context. However, it's important to ensure thatmaxRewardAmount
is always defined and has a valid value before this operation to avoid potential runtime errors or incorrect calculations.This change ensures that
millify
function is only called whenmaxRewardAmount
is not null or undefined.