Skip to content
Draft
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b514519
Add 1st draft of the new types
Y3drk Apr 29, 2026
d7278fd
Refactor data to match the drafted data model + add mock images and a…
Y3drk Apr 29, 2026
8e692b6
Basic UI adaptation to the new data model, pt. 1
Y3drk Apr 29, 2026
f35b9b5
Basic UI adaptation to the new data model, pt.2
Y3drk May 5, 2026
2fce38b
Apply ai assistant's suggestions, pt.1
Y3drk May 6, 2026
d7e3f4c
Refine apps' benchmarks data organization
Y3drk May 7, 2026
5ef4e6f
JSDocs improvements, pt.1
Y3drk May 7, 2026
cb4969c
Refine best practices data organization
Y3drk May 7, 2026
9ae8c2d
Adjust UI according to established goals
Y3drk May 7, 2026
eaec4c3
Update CONTRIBUTING.md and polish some JSDocs
Y3drk May 7, 2026
1b96f1a
Apply ai assistant's suggestions, pt.2
Y3drk May 7, 2026
9e9aa00
Apply 05/08/26 review feedback (GitHub + Google Doc), pt.1 - majority…
Y3drk May 11, 2026
bb1b2c6
Remove all placeholder content and align acceptance test benchmark re…
Y3drk May 11, 2026
0c060b6
Apply 05/11/26 Slack feedback
Y3drk May 11, 2026
8dd6549
Apply ai assistant's suggestions, pt.3
Y3drk May 11, 2026
5542d92
Resolve conflicts with main
Y3drk May 11, 2026
e6773bf
UI double-check + initial work on technical details content refinement
Y3drk May 11, 2026
e79ae89
Apply ai assistant's suggestions, pt.4
Y3drk May 12, 2026
7efbbef
Fix audited dependency vulnerabilities
Y3drk May 12, 2026
2d93d38
Apply 05/11/26 Google doc feedback
Y3drk May 12, 2026
161dd35
Apply 05/12/26 GitHub review feedback
Y3drk May 12, 2026
9faf4b4
Apply ai assistant's suggestions, pt.5
Y3drk May 12, 2026
8d3a637
Apply 05/12/26 Google Doc UI feedback
Y3drk May 12, 2026
afd0fbd
Apply ai assistant's suggestions, pt.6
Y3drk May 12, 2026
d4a4482
Apply ai assistant's suggestions, pt.7 + minor fixes after self-review
Y3drk May 12, 2026
25d43d7
Apply 05/13/26 GitHub review feedback
Y3drk May 13, 2026
35c7e4a
Apply ai assistant's suggestions, pt.8
Y3drk May 13, 2026
c935f7f
Upload final versions of adjusted contract naming season OG images
Y3drk May 13, 2026
1870bc0
Apply ai assistant's suggestions, pt.9
Y3drk May 13, 2026
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
141 changes: 115 additions & 26 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ To add yourself:
1. Add your `AccountId` to the `contributors` collection in [ensawards.org/data/contributors/index.ts](ensawards.org/data/contributors/index.ts) (if this is your first contribution).
2. Reference yourself in the contributions array of the entity you are updating. If you update the same entity multiple times, update the `lastUpdated` field for each change with the timestamp of your most recent update to that entity.

For reference see [ensawards.org/data/apps/metamask-wallet/benchmarks.ts](ensawards.org/data/apps/metamask-wallet/benchmarks.ts).
For reference see [ensawards.org/data/apps/metamask-wallet/benchmarks/index.tsx](ensawards.org/data/apps/metamask-wallet/benchmarks/index.tsx).

### Adding a new `Project`

Expand Down Expand Up @@ -198,9 +198,16 @@ Best practices are structured hierarchically and can be added on two levels:

Defines a specific requirement that an app or protocol must meet to pass a benchmark test. They are grouped into categories.

1. Create a new `BestPractice` default-export and its definition in `ensawards.org/data/ens-best-practices/[category]/[bestPractice].ts`, where `[category]` is the category slug and `[bestPractice]` is the practice slug. Make sure to also call `defineBestPractice()` on it. For reference see [ensawards.org/data/ens-best-practices/contract-naming/name-your-smart-contracts.ts](ensawards.org/data/ens-best-practices/contract-naming/name-your-smart-contracts.ts).
1. Create a new `BestPractice` default-export and its definition in `ensawards.org/data/ens-best-practices/[category]/[bestPractice]/index.ts`, where `[category]` is the category slug and `[bestPractice]` is the practice slug. Make sure to also call `defineBestPractice()` on it. For reference see [ensawards.org/data/ens-best-practices/contract-naming/name-your-smart-contracts/index.ts](ensawards.org/data/ens-best-practices/contract-naming/name-your-smart-contracts/index.ts).
2. Make sure to follow its data model defined in the [ensawards.org/data/ens-best-practices/types.ts](ensawards.org/data/ens-best-practices/types.ts) file.
```typescript
export interface BestPracticeTechnicalDetails {
useCaseSummary: JSX.Element;
desiredOutcome: JSX.Element;
implementationRecommendations: JSX.Element;
acceptanceTests: [AcceptanceTest, ...AcceptanceTest[]];
}

export interface BestPracticeAbstract<
BestPracticeT extends BestPracticeType,
AppliesToT extends BestPracticeTarget,
Expand All @@ -212,16 +219,7 @@ export interface BestPracticeAbstract<
description: string;
category: BestPracticeCategory; // each best practice belongs to exactly one best practice category
appliesTo: AppliesToT[];
technicalDetails: {
main: {
header: string;
content: string;
};
sides: {
header: string;
content: string;
}[];
};
technicalDetails: BestPracticeTechnicalDetails;
contributions: [Contribution, ...Contribution[]]; // Remember to add yourself as a contributor
}

Expand All @@ -233,8 +231,45 @@ export interface BestPracticeApp

export type BestPractice = BestPracticeProtocol | BestPracticeApp;
```
3. In your PR describe your reasoning for adding it.
4. If you want your best practice to be a part of a new category, learn how to add one below.
3. Add technical details as simple JSX elements in the created directory (`technicalDetails.tsx`). For reference, see [ensawards.org/data/ens-best-practices/contract-naming/name-your-smart-contracts/technicalDetails.tsx](ensawards.org/data/ens-best-practices/contract-naming/name-your-smart-contracts/technicalDetails.tsx).
* Make sure to follow the `AcceptanceTest` data model available in the [ensawards.org/data/acceptance-tests/types.ts](ensawards.org/data/acceptance-tests/types.ts) file.
```typescript
/** A unique identifier for an acceptance test.
*
* @invariant Must be unique across all acceptance tests.
* @invariant Must match {@link ENSAWARDS_SLUG_PATTERN}.
*/
export type AcceptanceTestSlug = string;

/**
* Represents an acceptance test that an app can be evaluated against.
*/
export interface AcceptanceTest {
/**
* Unique identifier for the acceptance test.
*/
acceptanceTestSlug: AcceptanceTestSlug;

/**
Comment thread
lightwalker-eth marked this conversation as resolved.
* Description of the acceptance test, which should provide clear and detailed information
* about the criteria and requirements that an {@link App} must meet to pass the test.
* This may include specific functionalities to be tested, user interactions to be evaluated, and any relevant technical details or considerations.
*
* @note The description should not include examples of passed or failed benchmarks, there are dedicated fields for that
* (see {@link AcceptanceTest.examplePass}, {@link AcceptanceTest.examplePartialPass}, or {@link AcceptanceTest.exampleFail}).
*/
description: JSX.Element;

/**
* Examples of benchmark results that illustrate what a passing, partially passing, or failing result looks like for this acceptance test.
*/
examplePass: AcceptanceTestBenchmarkPass;
examplePartialPass?: AcceptanceTestBenchmarkPartialPass;
exampleFail?: AcceptanceTestBenchmarkFail;
}
```
4. In your PR describe your reasoning for adding it.
5. If you want your best practice to be a part of a new category, learn how to add one below.

#### `BestPracticeCategory`

Expand Down Expand Up @@ -263,21 +298,20 @@ export interface BestPracticeCategory {

### Suggest a `benchmark update`

1. To suggest a benchmark update for an existing app, modify its `benchmarks` record in the [ensawards.org/data/apps/[app-directory]/benchmarks.ts](ensawards.org/data/apps/rainbow-wallet/benchmarks.ts) file where `[app-directory]` represents the slug of the relevant app. These records define relationships between benchmarks and best practices. Their data model is available at [ensawards.org/data/ens-best-practices/types.ts](ensawards.org/data/ens-best-practices/types.ts).
1. To suggest a benchmark update for an existing app, modify its `benchmarks` record in the [ensawards.org/data/apps/[app-directory]/benchmarks/index.tsx](ensawards.org/data/apps/rainbow-wallet/benchmarks/index.tsx) file where `[app-directory]` represents the slug of the relevant app. These records define relationships between benchmarks and best practices. Their data model is available at [ensawards.org/data/ens-best-practices/types.ts](ensawards.org/data/ens-best-practices/types.ts).

```typescript
/**
* Defines relations between {@link BestPracticeSlug} and {@link AppBenchmark}
* for the related {@link BestPractice}.
* Defines relations between {@link BestPracticeSlug} and the {@link AcceptanceTestBenchmarks}
* of the related {@link BestPractice} for a given app.
*
* @invariant An explicit key for each `BestPracticeSlug` should be added to this `Record` for each available {@link BestPractice}.
* If an app doesn't have a benchmark completed for a `BestPractice` then the benchmark should be explicitly set to `undefined`.
* Otherwise, the value should be an `AppBenchmark` describing how the related app performed for the `BestPractice`.
* @invariant An explicit key for each `BestPracticeSlug` should be added to this `Record` for each applicable {@link BestPractice}.
* The value should be the related {@link AcceptanceTestBenchmarks}.
*/
export type BestPracticeBenchmarks = Record<BestPracticeSlug, AppBenchmark | undefined>;
export type BestPracticeBenchmarks = Record<BestPracticeSlug, AcceptanceTestBenchmarks>;
```

For reference see [ensawards.org/data/apps/blockscout-explorer/benchmarks.ts](ensawards.org/data/apps/blockscout-explorer/benchmarks.ts) for an example benchmarks record.
For reference see [ensawards.org/data/apps/blockscout-explorer/benchmarks/index.tsx](ensawards.org/data/apps/blockscout-explorer/benchmarks/index.tsx) for an example benchmarks record.

2. Make sure to follow the benchmark data model. It is available in the [ensawards.org/data/benchmarks/types.ts](ensawards.org/data/benchmarks/types.ts) file.
```typescript
Expand All @@ -289,19 +323,74 @@ export const BenchmarkResults = {

export type BenchmarkResult = (typeof BenchmarkResults)[keyof typeof BenchmarkResults];

export interface AppBenchmark {
result: BenchmarkResult;
/**
* Represents the benchmarks of an {@link App} against {@link AcceptanceTest}s of {@link BestPractice}.
*
* @invariant An explicit key for each {@link AcceptanceTestSlug} should be added to this `Record`
* for each {@link AcceptanceTest} available on a given best practice.
* If an app doesn't have a benchmark completed for an `AcceptanceTest` then the benchmark should be explicitly set to `undefined`.
* Otherwise, the value should be an `AcceptanceTestBenchmark` describing how the related app performed for the `AcceptanceTest`.
*/
export type AcceptanceTestBenchmarks = Record<
AcceptanceTestSlug,
AcceptanceTestBenchmark | undefined
>;
```

Where `AcceptanceTestBenchmark` represents the benchmark performed on a single acceptance test. Its data model is available in the [ensawards.org/data/acceptance-tests/types.ts](ensawards.org/data/acceptance-tests/types.ts) file.
Comment thread
Y3drk marked this conversation as resolved.

```typescript
/**
* Represents the benchmark of an {@link AcceptanceTest} on an {@link App} against a {@link BestPractice}.
*/
export interface AcceptanceTestBenchmarkAbstract<BenchmarkResultT extends BenchmarkResult> {
/** The result of the benchmark */
result: BenchmarkResultT;

/** A record of all contributors involved in the addition or maintenance of the benchmark's data.
*
* @invariant Multiple {@link Contribution} from the same contributor on the same app benchmark are not allowed.
* When a contributor makes updates to their existing contribution,
* @invariant Multiple {@link Contribution} from the same contributor on the same app benchmark are not allowed.
* When a contributor makes updates to their existing contribution,
* they should update the `lastUpdated` timestamp of their existing `Contribution`.
*/
contributions: [Contribution, ...Contribution[]];

/**
* Notes about how the benchmark result was determined, which may include details about the testing process,
* any challenges encountered, and explanations, as well as a visual proof, for the final result.
*/
notes: JSX.Element;
}

/**
* Represents a benchmark of an {@link AcceptanceTest} on an {@link App} against a {@link BestPractice},
* that has fully passed our criteria.
Comment thread
Y3drk marked this conversation as resolved.
Outdated
*/
export interface AcceptanceTestBenchmarkPass
extends AcceptanceTestBenchmarkAbstract<typeof BenchmarkResults.Pass> {}

/**
* Represents a benchmark of an {@link AcceptanceTest} on an {@link App} against a {@link BestPractice},
* that has partially passed our criteria.
Comment thread
Y3drk marked this conversation as resolved.
*/
export interface AcceptanceTestBenchmarkPartialPass
extends AcceptanceTestBenchmarkAbstract<typeof BenchmarkResults.PartialPass> {}

/**
* Represents a benchmark of an {@link AcceptanceTest} on an {@link App} against a {@link BestPractice},
* that hasn't passed our criteria.
Comment thread
Y3drk marked this conversation as resolved.
*/
export interface AcceptanceTestBenchmarkFail
extends AcceptanceTestBenchmarkAbstract<typeof BenchmarkResults.Fail> {}

export type AcceptanceTestBenchmark =
| AcceptanceTestBenchmarkPass
| AcceptanceTestBenchmarkPartialPass
| AcceptanceTestBenchmarkFail;
```

3. Add notes made during the benchmarking process in the form of a simple JSX element that is a part of the new item in the `benchmarks` record. For reference, see [ensawards.org/data/apps/walletchan-wallet/benchmarks/index.tsx](ensawards.org/data/apps/walletchan-wallet/benchmarks/index.tsx).


## Using `Biome` and `Prettier` together

Expand Down
22 changes: 22 additions & 0 deletions ensawards.org/data/acceptance-tests/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ACCEPTANCE_TESTS } from "data/acceptance-tests";
import { isValidSlug } from "data/shared/slugs";
import { describe, expect, it } from "vitest";

import { areStringsUnique } from "@/utils";

describe("Acceptance test data", () => {
it("Should have valid and unique slugs", () => {
const slugArray: string[] = [];

ACCEPTANCE_TESTS.forEach((acceptanceTest) => {
expect(
isValidSlug(acceptanceTest.acceptanceTestSlug),
`Slug={${acceptanceTest.acceptanceTestSlug}} is not valid`,
).toEqual(true);

slugArray.push(acceptanceTest.acceptanceTestSlug);
});

expect(areStringsUnique(slugArray), `Slugs for Acceptance Tests are not unique`).toEqual(true);
});
});
5 changes: 5 additions & 0 deletions ensawards.org/data/acceptance-tests/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ENS_BEST_PRACTICES } from "data/ens-best-practices";

export const ACCEPTANCE_TESTS = [
...ENS_BEST_PRACTICES.flatMap((bestPractice) => bestPractice.technicalDetails.acceptanceTests),
];
85 changes: 85 additions & 0 deletions ensawards.org/data/acceptance-tests/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { type BenchmarkResult, BenchmarkResults } from "data/benchmarks/types";
import type { Contribution } from "data/contributors/types";
import type { JSX } from "react";

/** A unique identifier for an acceptance test.
*
* @invariant Must be unique across all acceptance tests.
* @invariant Must match {@link ENSAWARDS_SLUG_PATTERN}.
*/
export type AcceptanceTestSlug = string;

/**
* Represents an acceptance test that an app can be evaluated against.
*/
export interface AcceptanceTest {
Comment thread
lightwalker-eth marked this conversation as resolved.
/**
* Unique identifier for the acceptance test.
*/
acceptanceTestSlug: AcceptanceTestSlug;

/**
* Description of the acceptance test, which should provide clear and detailed information
Comment thread
Y3drk marked this conversation as resolved.
Outdated
* about the criteria and requirements that an {@link App} must meet to pass the test.
* This may include specific functionalities to be tested, user interactions to be evaluated, and any relevant technical details or considerations.
*
* @note The description should not include examples of passed or failed benchmarks, there are dedicated fields for that
* (see {@link AcceptanceTest.examplePass}, {@link AcceptanceTest.examplePartialPass}, or {@link AcceptanceTest.exampleFail}).
*/
description: JSX.Element;

/**
* Examples of benchmark results that illustrate what a passing, partially passing, or failing result looks like for this acceptance test.
Comment thread
Y3drk marked this conversation as resolved.
Outdated
*/
examplePass: AcceptanceTestBenchmarkPass;
examplePartialPass?: AcceptanceTestBenchmarkPartialPass;
exampleFail?: AcceptanceTestBenchmarkFail;
}

/**
* Represents the benchmark of an {@link AcceptanceTest} on an {@link App} against a {@link BestPractice}.
*/
export interface AcceptanceTestBenchmarkAbstract<BenchmarkResultT extends BenchmarkResult> {
/** The result of the benchmark */
result: BenchmarkResultT;

/** A record of all contributors involved in the addition or maintenance of the benchmark's data.
*
* @invariant Multiple {@link Contribution} from the same contributor on the same app benchmark are not allowed.
* When a contributor makes updates to their existing contribution,
* they should update the `lastUpdated` timestamp of their existing `Contribution`.
*/
contributions: [Contribution, ...Contribution[]];

/**
* Notes about how the benchmark result was determined, which may include details about the testing process,
* any challenges encountered, and explanations, as well as a visual proof, for the final result.
*/
notes: JSX.Element;
}

/**
* Represents a benchmark of an {@link AcceptanceTest} on an {@link App} against a {@link BestPractice},
* that has fully passed our criteria.
*/
export interface AcceptanceTestBenchmarkPass
extends AcceptanceTestBenchmarkAbstract<typeof BenchmarkResults.Pass> {}

/**
* Represents a benchmark of an {@link AcceptanceTest} on an {@link App} against a {@link BestPractice},
* that has partially passed our criteria.
*/
export interface AcceptanceTestBenchmarkPartialPass
extends AcceptanceTestBenchmarkAbstract<typeof BenchmarkResults.PartialPass> {}

/**
* Represents a benchmark of an {@link AcceptanceTest} on an {@link App} against a {@link BestPractice},
* that hasn't passed our criteria.
*/
export interface AcceptanceTestBenchmarkFail
extends AcceptanceTestBenchmarkAbstract<typeof BenchmarkResults.Fail> {}

export type AcceptanceTestBenchmark =
| AcceptanceTestBenchmarkPass
| AcceptanceTestBenchmarkPartialPass
| AcceptanceTestBenchmarkFail;
70 changes: 70 additions & 0 deletions ensawards.org/data/acceptance-tests/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { ACCEPTANCE_TESTS } from "data/acceptance-tests";
import type {
AcceptanceTest,
AcceptanceTestBenchmark,
AcceptanceTestSlug,
} from "data/acceptance-tests/types";
import type { AppSlug } from "data/apps/types";
import {
type AcceptanceTestBenchmarks,
type BenchmarkResult,
BenchmarkResults,
} from "data/benchmarks/types";
import { getAppBenchmarks } from "data/benchmarks/utils.ts";

/** Returns an {@link AcceptanceTest} by {@link AcceptanceTestSlug}. */
export const getAcceptanceTestBySlug = (slug: AcceptanceTestSlug): AcceptanceTest | undefined => {
return ACCEPTANCE_TESTS.find((acceptanceTest) => acceptanceTest.acceptanceTestSlug === slug);
};

/** Returns all {@link AcceptanceTestBenchmark}s of an `App` by {@link AppSlug}. */
export const getAcceptanceTestBenchmarksByApp = (
appSlug: AppSlug,
): (AcceptanceTestBenchmark | undefined)[] => {
const appBenchmarks = getAppBenchmarks(appSlug);

return Object.values(appBenchmarks).flatMap((acceptanceTestBenchmarks) =>
Object.values(acceptanceTestBenchmarks),
);
};

/**
* Generalizes multiple `AcceptanceTestBenchmark`s into a single `BenchmarkResult` based on the following rules:
* - Returns {@link BenchmarkResults.Pass} if all defined benchmarks are {@link BenchmarkResults.Pass} or {@link BenchmarkResults.PartialPass}
* - Returns {@link BenchmarkResults.Fail} if all defined benchmarks are {@link BenchmarkResults.Fail},
* - Returns {@link BenchmarkResults.PartialPass} if:
* - at least one defined benchmark is {@link BenchmarkResults.Fail} and at least one defined benchmark is {@link BenchmarkResults.Pass} or {@link BenchmarkResults.PartialPass},
* - or all defined benchmarks are {@link BenchmarkResults.PartialPass},
* - returns `undefined` if all benchmarks are `undefined` (pending).
*/
export const generalizeAcceptanceTestBenchmarks = (
acceptanceTestBenchmarks: AcceptanceTestBenchmarks,
): BenchmarkResult | undefined => {
const benchmarkResults = Object.values(acceptanceTestBenchmarks).map(
Comment thread
Y3drk marked this conversation as resolved.
(benchmark) => benchmark?.result,
);

// Explicitly treating passs and partial pass equally
Comment thread
Y3drk marked this conversation as resolved.
Outdated
Comment thread
Y3drk marked this conversation as resolved.
Outdated
const hasPass = benchmarkResults.some(
(result) => result === BenchmarkResults.Pass || result === BenchmarkResults.PartialPass,
);
const hasFail = benchmarkResults.some((result) => result === BenchmarkResults.Fail);

const allPartialPass = benchmarkResults.every(
(result) => result === BenchmarkResults.PartialPass,
);

if ((hasPass && hasFail) || allPartialPass) {
return BenchmarkResults.PartialPass;
Comment thread
Y3drk marked this conversation as resolved.
}

if (hasPass) {
return BenchmarkResults.Pass;
}

if (hasFail) {
Comment thread
Y3drk marked this conversation as resolved.
Outdated
Comment thread
Y3drk marked this conversation as resolved.
Outdated
return BenchmarkResults.Fail;
}

return undefined;
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Loading
Loading