Skip to content

Commit

Permalink
Merge pull request #780 from hats-finance/develop
Browse files Browse the repository at this point in the history
Bonus points feat
  • Loading branch information
shayzluf authored Nov 27, 2024
2 parents ed0bc24 + 5da7ff6 commit 48a18cc
Show file tree
Hide file tree
Showing 38 changed files with 1,419 additions and 170 deletions.
7 changes: 7 additions & 0 deletions packages/shared/src/types/bonusSubmissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface IClaimedIssue {
vaultAddress: string;
issueNumber: string;
claimedBy: string;
claimedAt: Date;
expiresAt: Date;
}
1 change: 1 addition & 0 deletions packages/shared/src/types/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export interface IBaseEditedVaultDescription {
isPrivateAudit?: boolean;
isContinuousAudit?: boolean;
requireMessageSignature?: boolean;
bonusPointsEnabled?: boolean;
messageToSign?: string;
whitelist: { address: string }[];
endtime?: number;
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from "./payout";
export * from "./safe";
export * from "./submissions";
export * from "./profile";
export * from "./bonusSubmissions";
4 changes: 4 additions & 0 deletions packages/shared/src/types/payout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ export type GithubIssue = {
createdBy: number;
labels: string[];
validLabels: string[];
bonusPointsLabels: {
needsFix: boolean;
needsTest: boolean;
};
createdAt: string;
body: string;
txHash?: string;
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ interface IBaseVaultDescription {
isPrivateAudit?: boolean;
isContinuousAudit?: boolean;
requireMessageSignature?: boolean;
bonusPointsEnabled?: boolean;
messageToSign?: string;
whitelist: { address: string }[];
endtime?: number;
Expand Down
2 changes: 2 additions & 0 deletions packages/web/src/components/Button/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ const getVariableByTextColor = (textColor: ButtonProps["textColor"], styleType:
return "--primary";
case "error":
return "--error-red";
case "white":
return "--white";
default:
if (styleType === "invisible") return "--secondary";
return "--white";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,22 @@ type FormSupportFilesInputProps = {
name: string;
onChange: (data: ISavedFile[]) => void;
error?: { message?: string; type: string };
uploadTo?: "db" | "ipfs";
noFilesAttachedInfo?: boolean;
};

export const FormSupportFilesInputComponent = (
{ colorable = false, isDirty = false, name, onChange, label, error, value }: FormSupportFilesInputProps,
{
colorable = false,
isDirty = false,
name,
onChange,
label,
error,
value,
uploadTo = "db",
noFilesAttachedInfo,
}: FormSupportFilesInputProps,
ref
) => {
const { t } = useTranslation();
Expand All @@ -50,7 +62,7 @@ export const FormSupportFilesInputComponent = (
return alert(t("invalid-file-type"));
}

filesToUploadPromises.push(FilesService.uploadFileToDB(file));
filesToUploadPromises.push(FilesService.uploadFileToDB(file, uploadTo === "ipfs"));
}

const uploadedFiles = await Promise.all(filesToUploadPromises);
Expand All @@ -76,17 +88,19 @@ export const FormSupportFilesInputComponent = (
<p>{isUploadingFiles ? `${t("uploadingFiles")}...` : label ?? ""}</p>
</label>

<div className="files-attached-container">
<p>{t("filesAttached")}:</p>
<ul className="files">
{value?.map((file, idx) => (
<li key={idx}>
<CloseIcon className="remove-icon" onClick={() => handleRemoveFile(idx)} />
<p>{file.name}</p>
</li>
))}
</ul>
</div>
{!noFilesAttachedInfo && (
<div className="files-attached-container">
<p>{t("filesAttached")}:</p>
<ul className="files">
{value?.map((file, idx) => (
<li key={idx}>
<CloseIcon className="remove-icon" onClick={() => handleRemoveFile(idx)} />
<p>{file.name}</p>
</li>
))}
</ul>
</div>
)}

{error && <span className="error">{error.message}</span>}
</StyledFormSupportFilesInput>
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const supportedExtensions = ["txt", "sol", "ts", "js"];
export const supportedExtensions = ["txt", "sol", "ts", "js", "md", "json"];
20 changes: 17 additions & 3 deletions packages/web/src/components/VaultCard/VaultCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ export const VaultCard = ({
ONE_LINER_FALLBACK[vault.id] ??
"Nulla facilisi. Donec nec dictum eros. Cras et velit viverra, dapibus velit fringilla, bibendum mi aptent. Class aptent taciti sociosqu ad litora.";

const bonusPointsEnabled = vault.description?.["project-metadata"]?.bonusPointsEnabled;

const getAuditStatusPill = () => {
if (!vault.description) return null;
if (!vault.description["project-metadata"].endtime) return null;
Expand Down Expand Up @@ -470,9 +472,10 @@ export const VaultCard = ({
{!reducedStyles && (
<div className="vault-actions">
<div className="assets">
<span className="subtitle">{auditPayout ? t("paidAssets") : t("assetsInVault")}</span>
<VaultAssetsPillsList auditPayout={auditPayout} vaultData={vaultData} />

<div className="assets-list">
<span className="subtitle">{auditPayout ? t("paidAssets") : t("assetsInVault")}</span>
<VaultAssetsPillsList auditPayout={auditPayout} vaultData={vaultData} />
</div>
<OptedInList editSessionIdOrAddress={vault.id} />
</div>
<div className="actions">
Expand All @@ -487,6 +490,17 @@ export const VaultCard = ({
{t("deposits")}
</Button>
)}
{isAudit && vault.dateStatus === "on_time" && !auditPayout && !hideSubmit && bonusPointsEnabled && (
<Button
disabled={noActions}
size="medium"
filledColor={isAudit ? "primary" : "secondary"}
styleType="outlined"
onClick={goToSubmitVulnerability}
>
{t("claimFixAndTest")}
</Button>
)}
{(!isAudit || (isAudit && vault.dateStatus === "on_time" && !auditPayout)) && !hideSubmit && (
<Button
disabled={noActions}
Expand Down
12 changes: 9 additions & 3 deletions packages/web/src/components/VaultCard/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ export const StyledVaultCard = styled.div<{
margin-top: ${getSpacing(3)};
display: grid;
align-items: flex-start;
grid-template-columns: ${isAudit ? "2fr 3fr" : "1fr 1fr"};
grid-template-columns: ${isAudit ? "1fr 3fr" : "1fr 1fr"};
gap: ${getSpacing(2)};
@media (max-width: ${breakpointsDefinition.mediumMobile}) {
Expand All @@ -249,8 +249,8 @@ export const StyledVaultCard = styled.div<{
.assets {
display: flex;
flex-wrap: wrap;
align-items: center;
flex-direction: column;
align-items: flex-start;
gap: ${getSpacing(1)};
@media (max-width: ${breakpointsDefinition.mediumMobile}) {
Expand All @@ -260,6 +260,12 @@ export const StyledVaultCard = styled.div<{
gap: ${getSpacing(2)};
}
.assets-list {
display: flex;
align-items: center;
gap: ${getSpacing(1)};
}
.subtitle {
color: var(--grey-400);
font-size: var(--xxsmall);
Expand Down
4 changes: 2 additions & 2 deletions packages/web/src/hooks/vaults/useVaultApy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const useVaultApy = (vault?: IVault): IVaultApy[] => {
vault.amountsInfo?.depositedAmount ? +vault.amountsInfo.depositedAmount.tokens : 0
);

apys.push({ rewardController: controller, apy });
if (!isNaN(apy)) apys.push({ rewardController: controller, apy });
}

setVaultApys(apys);
Expand Down Expand Up @@ -87,7 +87,7 @@ function calculateAPY(
// Calculate the APY
// Note: vaultTokenStaked should be the total value of tokens staked in the vault
// in the same currency as tokenPrice for an accurate APY calculation
const apy = (annualRewardValue / vaultTokenStaked) * 100;
const apy = vaultTokenStaked > 0 ? (annualRewardValue / vaultTokenStaked) * 100 : 0;

return apy > 100 ? +apy.toFixed(0) : +apy.toFixed(2);
}
Expand Down
34 changes: 34 additions & 0 deletions packages/web/src/languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,20 @@
"labeledAs": "Labeled as",
"onlyShowLabeledIssues": "Show only labeled issues",
"ghIssue": "GH issue",
"newSubmission": "New issue",
"complementSubmission": "Add Fix & Test to an existing issue",
"pathEndsWithFileNameError": "Invalid path (path needs to end with file name)",
"youNeedToConnectYourWallet": "You need to connect your wallet.",
"youAreNotInTopLeaderboardPercentage": "This action is reserved for participants in the top of HATS leaderboard. Improve your ranking to unlock or connect with your profile.",
"claimIssue": "Claim issue",
"claimIssueDescription": "-CODE STYLE ALERT-\n\nPlease note you need to write the fix and the test in the same style of the project code.",
"claimedByYou": "Claimed by you",
"issueAlreadyClaimed": "This issue has already been claimed.",
"bonusPointsTitle": "New: Boost Your Rewards, Claim and Submit Fixes and Tests!",
"bonusPointsDescription": "As a top performer, you now have the exclusive opportunity to claim and submit fixes and tests for identified issues.\n <ul><li>Increase your potential earnings by up to 15% per issue</li> <li>Demonstrate your problem-solving skills</li> <li>Contribute directly to improving security</li></ul> \n <ul><li>Ensure you're in the top 20% of the leaderboard and won 5k of reward</li> <li>Connect your profile if you haven't already</li> <li>Browse the issues below and click \"Claim fix and test\" to get started</li> <li>The fix and the test should be written in the same style of the project code.</li></ul>",
"bonusPointsReminder": "Remember, you have 12 hours to submit after claiming. Happy fixing and testing!",
"bonusPointsEnabled": "Enable bonus points?",
"claimFixAndTest": "Claim fix and test",
"MyWallet": {
"overview": "Overview",
"pointValue": "Point value",
Expand Down Expand Up @@ -1331,6 +1345,26 @@
"decryptedSubmissionExplanation": "The submission is decrypted and public. This normally happens on Audit Competitions.",
"submissionSubmittedViaAuditWizard": "Submission submitted via auditwizard.io",
"editSubmission": "Edit submission",
"submissionDescription": "Submission description",
"selectIssueToComplement": "Type the issue title you want to add a Fix & test",
"firstYouNeedToClaimSomeIssues": "First you need to claim fix & test for some issue",
"claimFixAndTest": "Claim fix & test",
"githubIssue": "GitHub issue",
"selectGithubIssue": "Select GitHub issue",
"addTestFiles": "Add test files",
"addTestFilesExplanation": "The test should fail with the current code base due to the issue and should pass when the issue will be fixed.",
"addFixFiles": "Add fix files",
"addFixFilesExplanation": "The fix should be a valid fix for the issue and the test should pass with the fixed code.",
"addTestPR": "Add test PR",
"addFixPR": "Add fix PR",
"selectTestFiles": "Select test files",
"selectFixFiles": "Select fix files",
"filePath": "File path",
"filePathPlaceholder": "src/path/to/file.sol",
"testNotApplicable": "Test not applicable",
"youNeedUploadAtLeastOneFile": "You need to upload at least one file",
"pocIsApplicable": "PoC is applicable",
"pocIsNotApplicable": "PoC is not applicable",
"terms": {
"bugBounty": {
"submissionSection": "<p>Your submission will be sent to the committee who will process your submission.</p> <p>The committee of the vault will respond within 24 hours to acknowledge the receipt of submission via the communication channel you provided.<p/>",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ export async function createPayoutFromSubmissions(
}

export async function getGithubIssuesFromVault(vault: IVault): Promise<GithubIssue[]> {
if (!vault) return [];

const extractTxHashFromBody = (issue: GithubIssue): any => {
// const txHash = issue.body.match(/(0x[a-fA-F0-9]{64})/)?.[0];
const txHash = issue.body.match(/(\*\*Submission hash \(on-chain\):\*\* (.*)\n)/)?.[2] ?? undefined;
Expand All @@ -228,6 +230,10 @@ export async function getGithubIssuesFromVault(vault: IVault): Promise<GithubIss
validLabels: issue.labels
.filter((label: any) => severitiesOrder.includes((label.name as string).toLowerCase()))
.map((label: any) => (label.name as string).toLowerCase()),
bonusPointsLabels: {
needsFix: issue.labels.some((label: any) => (label.name as string).toLowerCase() === "needs-fix"),
needsTest: issue.labels.some((label: any) => (label.name as string).toLowerCase() === "needs-test"),
},
createdAt: issue.created_at,
body: issue.body,
txHash: extractTxHashFromBody(issue),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { GithubIssue } from "@hats.finance/shared";
import PrevIcon from "@mui/icons-material/ArrowBackIosNewOutlined";
import NextIcon from "@mui/icons-material/ArrowForwardIosOutlined";
import { Alert, Pill, WithTooltip } from "components";
import { getSeveritiesColorsArray } from "hooks/severities/useSeverityRewardInfo";
import useWindowDimensions from "hooks/useWindowDimensions";
import moment from "moment";
import PublicSubmissionCard from "pages/Honeypots/VaultDetailsPage/Sections/VaultSubmissionsSection/PublicSubmissionCard/PublicSubmissionCard";
import { IGithubIssue } from "pages/Honeypots/VaultDetailsPage/types";
import { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { formatNumber, ipfsTransformUri } from "utils";
Expand Down Expand Up @@ -153,16 +153,18 @@ export const HackerActivity = ({ activity }: IHackerActivityProps) => {
<div className="submissions">
{findings?.length === 0 && <Alert type="info" content={t("HackerProfile.thereIsNoSubmissionToShow")} />}
{findings?.map((finding, idx) => {
const submission: IGithubIssue = {
_id: finding.id,
createdAt: new Date(+finding.createdAt * 1000),
vaultId: payout.vault!.id,
severity: finding.submissionDataStructure?.severity,
repoName: "",
issueData: {
issueFiles: [],
issueTitle: finding.submissionDataStructure?.title ?? "",
issueDescription: finding.submissionDataStructure?.content ?? "",
const submission: GithubIssue = {
createdAt: new Date(+finding.createdAt * 1000).toISOString(),
body: finding.submissionDataStructure?.content ?? "",
title: finding.submissionDataStructure?.title ?? "",
validLabels: finding.submissionDataStructure?.severity ? [finding.submissionDataStructure.severity] : [],
id: +finding.id,
number: -1,
labels: [],
createdBy: 0,
bonusPointsLabels: {
needsFix: false,
needsTest: false,
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const VaultAsset = ({ vault }: VaultAssetProps) => {
const { isShowing: isShowingDepositModal, show: showDepositModal, hide: hideDepositModal } = useModal();

const vaultApy = useVaultApy(vault);
console.log(vaultApy);

const isAudit = vault.description && vault.description["project-metadata"].type === "audit";
const depositsDisabled = !vault.committeeCheckedIn || vault.depositPause;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { VaultAssetsPillsList, WithTooltip } from "components";
import millify from "millify";
import { useTranslation } from "react-i18next";
import { formatNumber } from "utils";
import { VaultNftRewards } from "./VaultNftRewards/VaultNftRewards";
import { VaultRewardDivision } from "./VaultRewardDivision/VaultRewardDivision";
import { VaultSeverityRewards } from "./VaultSeverityRewards/VaultSeverityRewards";
import { StyledRewardsSection } from "./styles";
Expand All @@ -18,6 +17,7 @@ export const VaultRewardsSection = ({ vault }: VaultRewardsSectionProps) => {
const { t } = useTranslation();

const isAudit = vault.description && vault.description["project-metadata"].type === "audit";
const bonusPointsEnabled = (vault.description && vault.description["project-metadata"].bonusPointsEnabled) ?? false;
const showIntended = vault.amountsInfo?.showCompetitionIntendedAmount ?? false;

return (
Expand Down Expand Up @@ -90,6 +90,25 @@ export const VaultRewardsSection = ({ vault }: VaultRewardsSectionProps) => {
<div className="card">
<h4 className="title">{t("severityRewards")}</h4>
<VaultSeverityRewards vault={vault} />
{bonusPointsEnabled && (
<div className="bonus-points-info-container">
<p>Bonuses</p>
<div className="points">
<div className="point">
<span className="secondary-text">Fix:</span>
<span>
<strong>10%</strong> of issue points
</span>
</div>
<div className="point">
<span className="primary-text">Test:</span>
<span>
<strong>5%</strong> of issue points
</span>
</div>
</div>
</div>
)}
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 48a18cc

Please sign in to comment.