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

[FEAT]: Bonus points submissions #778

Merged
merged 26 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6115108
chore: changes on dilesInput
fonstack Oct 23, 2024
5b547a5
chore: minor UI change
fonstack Oct 24, 2024
70dc63e
chore: new submission flow
fonstack Oct 24, 2024
b08b535
chore: advances with claim fix/test
fonstack Oct 30, 2024
be3e054
chore: more advances
fonstack Oct 30, 2024
5e84dee
chore: different test/prod leaderboard params
fonstack Oct 30, 2024
83f4bd5
chore: added `needs-test` and `needs-fix` to model
fonstack Oct 30, 2024
c8b93cf
chore: showing complementary labels in submission form
fonstack Oct 30, 2024
76fad4e
fix: showing only issues that needs something in the select input
fonstack Oct 30, 2024
ea48380
feat: implemented path autocomplete
fonstack Oct 30, 2024
73a9ca5
refactor: changed `fixIsNotApplicable` to `testIsNotApplicable`
fonstack Nov 5, 2024
c6d7c9c
cchore: improvement
fonstack Nov 5, 2024
2b79867
chore: now PoC is applicable option begins undefined
fonstack Nov 5, 2024
a0acae8
fix: checking if issue is expired in order to show it as option
fonstack Nov 5, 2024
5ed3355
chore: added feature banner
fonstack Nov 5, 2024
74f7606
chore: added `bonusPointsEnabled` functionality to vaults
fonstack Nov 5, 2024
40b8fd1
chore: now `bonusPointEnabled` true by default + only in advanced mode
fonstack Nov 6, 2024
53969ef
chore: added code style alert
fonstack Nov 6, 2024
36b9c9c
chore: added bonus points info
fonstack Nov 7, 2024
71d9fb3
fix
fonstack Nov 7, 2024
7343ffe
Update packages/web/src/components/FormControls/FormSupportFilesInput…
shayzluf Nov 8, 2024
2472edd
chore: improved error handling in
fonstack Nov 8, 2024
19baa41
chore: fixed submission formSchema
fonstack Nov 8, 2024
9edcab0
chore: comment rolledback
fonstack Nov 8, 2024
870cd74
chore: removed log
fonstack Nov 8, 2024
1e3c00c
Merge branch 'feat/split-submissions' of https://github.com/hats-fina…
fonstack Nov 8, 2024
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
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;
}
fonstack marked this conversation as resolved.
Show resolved Hide resolved
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
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 @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve type safety with optional chaining

The nested object access could be safer using optional chaining.

-  const bonusPointsEnabled = (vault.description && vault.description["project-metadata"].bonusPointsEnabled) ?? false;
+  const bonusPointsEnabled = vault.description?.["project-metadata"]?.bonusPointsEnabled ?? false;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const bonusPointsEnabled = (vault.description && vault.description["project-metadata"].bonusPointsEnabled) ?? false;
const bonusPointsEnabled = vault.description?.["project-metadata"]?.bonusPointsEnabled ?? false;
🧰 Tools
🪛 Biome

[error] 20-20: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

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
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,40 @@ export const StyledRewardsSection = styled.div<{ showIntended: boolean; isAudit:
grid-column-start: 1;
grid-column-end: 3;
}

.bonus-points-info-container {
border-top: 1px solid var(--primary-light);
width: 100%;
display: flex;
flex-direction: column;
gap: ${getSpacing(1)};
padding-top: ${getSpacing(3)};

p {
font-weight: 700;
}

.points {
display: flex;
gap: ${getSpacing(2)};

.point {
display: flex;
align-items: center;
gap: ${getSpacing(1)};

.secondary-text {
color: var(--secondary);
font-weight: 700;
}

.primary-text {
color: var(--primary);
font-weight: 700;
}
}
}
}
}

.card {
Expand Down
Loading
Loading