This repository was archived by the owner on Aug 5, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
release: merge dev branch to main #244
Merged
Changes from 11 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
8297535
dev: Add `deposit-address` best practice (#233)
Y3drk 72aac95
feat: etherscan acceptance test (#234)
sevenzing a0b674a
feat: ATs for blockscout and 1inch (#235)
sevenzing 34626fd
feat: Benchmark wallets on `deposit-addresses` best practice (#236)
Y3drk 5383065
feat: add builders for notes to decrease boilerplate (#237)
sevenzing ae81489
feat: rearrange benchmarks folders for all apps (#238)
sevenzing 1cc435e
feat: add benchmarks for aave, uniswap and lido (#239)
sevenzing a9455e4
feat: Benchmark wallets on `deposit-addresses` best practice, batch 2…
Y3drk 35ed166
feat: add benchmarks for `binance`, `bybit` and `okx` (#240)
sevenzing 3e7f20b
add benchmarks for raibow-wallet,status-wallet,trust-wallet (#243)
sevenzing ad36a80
Add benchmarks for exchanges: coinbase, crypto.com and robinhood + ad…
Y3drk b61262e
add explain why AT8 is failed in example (#245)
sevenzing a6da292
small fix
sevenzing File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,338 @@ | ||
| --- | ||
| name: generate-app-benchmark | ||
| description: >- | ||
| Generate ENS Awards app benchmark `index.tsx` files for a target best practice | ||
| from a `manual.json` test log and nearby proof images. Use when asked to | ||
| unwrap/scaffold benchmark boilerplate for one or more apps against a best | ||
| practice (e.g. "deposit-addresses"), turning manual test results into typed | ||
| AcceptanceTestBenchmarks. | ||
| disable-model-invocation: true | ||
| --- | ||
|
|
||
| # Generate App Benchmark | ||
|
|
||
| Turn a hand-written `manual.json` test log plus nearby proof images into a typed | ||
| `index.tsx` benchmark file for a target best practice, for each app in a list. | ||
|
|
||
| ## Inputs | ||
|
|
||
| 1. **apps** — list of app slugs (directory names under `ensawards.org/data/apps/`). | ||
| 2. **best practice** — target `bestPracticeSlug` (e.g. `deposit-addresses`). | ||
| 3. **contributor** — a key from the `contributors` record (e.g. `llev`). | ||
| 4. **datetime** (optional) — ISO-8601 UTC timestamp for the contribution's | ||
| `lastUpdated`. Default to the machine's current time: | ||
| `date -u +%Y-%m-%dT%H:%M:%SZ`. | ||
|
|
||
| ## Workflow | ||
|
|
||
| Copy this checklist and track progress: | ||
|
|
||
| ``` | ||
| - [ ] Step 1: Resolve the best practice's ordered acceptance test slugs | ||
| - [ ] Step 2: Resolve datetime (use input or current UTC time) | ||
| - [ ] For each app: | ||
| - [ ] Step 3: Locate manual.json + proof media | ||
| - [ ] Step 3b: Normalize proof media to .png / .gif | ||
| - [ ] Step 4: Generate the best-practice index.tsx | ||
| - [ ] Step 5: Wire it into the app's main benchmarks/index.tsx | ||
| - [ ] Step 6: Typecheck | ||
| ``` | ||
|
|
||
| ### Step 1: Resolve acceptance test slugs + note builders (once) | ||
|
|
||
| The `manual.json` test `id`s map **positionally** (1-based) to the best | ||
| practice's acceptance tests, in declaration order. Read them from the best | ||
| practice's technical details: | ||
|
|
||
| ``` | ||
| ensawards.org/data/ens-best-practices/**/<best-practice-slug>/technicalDetails.tsx | ||
| ``` | ||
|
|
||
| Sort the acceptance tests by `order` (slug as the tiebreaker) before mapping `manual.json` ids to slugs. | ||
|
|
||
| #### Prefer the best practice's shared note builders | ||
|
|
||
| A best practice may ship a `notes.tsx` module next to its `technicalDetails.tsx`: | ||
|
|
||
| ``` | ||
| ensawards.org/data/ens-best-practices/<category>/<best-practice-slug>/notes.tsx | ||
| ``` | ||
|
|
||
| If it exists, **the benchmark notes must be generated with these builders** — | ||
| do **not** hand-write `<div>`/`<p>`/`<img>` markup or import the canonical | ||
| `*Span` constants into the app file. Each acceptance test always exercises the | ||
| same ENS name, expected address, and chain, so those are already baked into the | ||
| builders. The app file only supplies what varies per app: | ||
|
|
||
| - `method` — how the test was run (e.g. `'the "send" flow'`) | ||
| - `proof` — the screenshot(s) (`{ image, alt }`, or an array for multiple) | ||
| - which builder you call (encodes the result + acceptance test) | ||
|
|
||
| Read `notes.tsx` to see the available builders. `deposit-addresses` exports, for | ||
| example: | ||
|
|
||
| - `buildPassNoteForAT1`…`buildPassNoteForAT8` — a name resolves correctly. | ||
| - `buildFailNoteForAT1`…`buildFailNoteForAT8` — a name fails to resolve; each | ||
| takes an optional `extra` follow-up sentence for app-specific detail. | ||
| - `buildEnsNotSupportedNote` — the app doesn't accept ENS names at all (AT1). | ||
| - `buildNotApplicableForNonEvmChain({ chain })` — Bitcoin / Solana not supported. | ||
| - `buildNotApplicableForFailedTest({ testNumber, scope })` — a prerequisite test | ||
| failed (`scope` defaults to `"at all"`; use `"on Base"` for the AT5 cascade). | ||
| - `buildBenchmarkNote({ children, proof })` — escape hatch for an unusual result | ||
| no builder covers. Only here may you import `*Span` constants from | ||
| `technicalDetails` to write custom prose. | ||
|
|
||
| If `notes.tsx` doesn't exist for the target best practice, fall back to writing | ||
| notes inline (see the legacy guidance at the end of Step 4), referencing the | ||
| exported `*Span` constants from `technicalDetails.tsx` (add `export` if missing). | ||
|
|
||
| ### Step 3: Locate manual.json + proof media (per app) | ||
|
|
||
| Search the app's benchmarks dir: | ||
|
|
||
| ``` | ||
| ensawards.org/data/apps/<app>/benchmarks/**/manual.json | ||
| ``` | ||
|
|
||
| Pick the `manual.json` whose directory corresponds to the target best practice. | ||
| Proof media sit in the **same directory**, named with a | ||
| trailing test id: `at-<id>.<ext>` or `ac-<id>.<ext>`. Match each file to a test | ||
| by its id. A test may have no media. Source files can be images (`.png`, `.jpg`, | ||
| `.webp`, ...) or screen recordings (`.mov`, `.mp4`, ...). | ||
|
|
||
| ### Step 3b: Normalize proof media to .png / .gif | ||
|
|
||
| The benchmark file may only reference `.png` or `.gif` assets. Normalize each | ||
| proof file `at-<id>.<ext>` in place, then `git rm` the original if its extension | ||
| changed: | ||
|
|
||
| | Source | Action | | ||
| | ----------------------------------------------------------- | ----------------------- | | ||
| | `.png` | keep as-is | | ||
| | `.gif` | keep as-is | | ||
| | other image (`.jpg`/`.jpeg`/`.webp`/`.heic`/`.tiff`/`.bmp`) | convert → `at-<id>.png` | | ||
| | video (`.mov`/`.mp4`/`.m4v`/`.webm`/`.avi`/`.mkv`) | convert → `at-<id>.gif` | | ||
|
|
||
| Conversions can be finicky; pick commands by OS (default to **macOS**). | ||
|
|
||
| **Image → PNG** | ||
|
|
||
| macOS (built-in `sips`, no install): | ||
|
|
||
| ```bash | ||
| sips -s format png "at-1.jpg" --out "at-1.png" && git rm "at-1.jpg" | ||
| ``` | ||
|
|
||
| Windows (ImageMagick): | ||
|
|
||
| ```powershell | ||
| magick "at-1.jpg" "at-1.png"; git rm "at-1.jpg" | ||
| ``` | ||
|
|
||
| **Video → GIF** (both OSes use `ffmpeg`; install via `brew install ffmpeg` / | ||
| `winget install Gyan.FFmpeg` if missing): | ||
|
|
||
| ```bash | ||
| ffmpeg -y -i "at-5.mov" \ | ||
| -vf "fps=12,scale=800:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \ | ||
| -loop 0 "at-5.gif" && git rm "at-5.mov" | ||
| ``` | ||
|
|
||
| Tune `fps` / `scale` down if the resulting `.gif` is large. After this step every | ||
| proof file is a `.png` or `.gif`, and that final extension is what the import in | ||
| Step 4 must reference. | ||
|
|
||
| `manual.json` schema: | ||
|
|
||
| ```json | ||
| { | ||
| "method": "Default description of how every test was run.", | ||
| "tests": [ | ||
| { "id": 1, "status": "passed", "reason": "Shown correct address" }, | ||
| { "id": 5, "status": "failed", "reason": "...", "method": "Per-test method override" }, | ||
| { "id": 6, "status": "not-applicable", "reason": "app doesn't support Bitcoin" } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| Status → result mapping: | ||
|
|
||
| | `status` | `BenchmarkResults` | | ||
| | ------------------ | -------------------------------- | | ||
| | `passed` | `BenchmarkResults.Pass` | | ||
| | `partially-passed` | `BenchmarkResults.PartialPass` | | ||
| | `failed` | `BenchmarkResults.Fail` | | ||
| | `not-applicable` | `BenchmarkResults.NotApplicable` | | ||
|
|
||
| ### Step 4: Generate `index.tsx` | ||
|
|
||
| Write `index.tsx` in the directory that holds `manual.json` (replace any empty | ||
| `index.ts` there — JSX requires `.tsx`). Export a default | ||
| `AcceptanceTestBenchmarks` keyed by acceptance test slug. Include **one entry per | ||
| test present in `manual.json`** (keyed by the slug at that id's position). | ||
|
|
||
| Notes: call the right note builder per entry (see Step 1). Pick the builder from | ||
| the acceptance test id (its 1-based position) and the recorded `status`, then pass: | ||
|
|
||
| - `method` — derive a short phrase from the per-test `method` override (else the | ||
| global `method`) describing **how** the test was run. Omit the chain context the | ||
| builder already bakes in (e.g. don't repeat "on Base" for the AT5 builder). | ||
| - `proof` — `{ image: <import>, alt: "<app> ..." }` when a matching proof image | ||
| exists; multiple images → pass an array. Omit when there's no proof. | ||
| - `extra` (fail builders) — an optional extra sentence for app-specific detail | ||
| (e.g. `'The app showed an "invalid domain format" error instead.'`). | ||
|
|
||
| The `reason` and `method` strings are **not strict** — they are author notes, not | ||
| literal copy. Use them to choose the builder and to phrase `method`/`extra` | ||
| naturally and accurately; just don't contradict the recorded `status`. | ||
|
|
||
| File template (deposit-addresses; import only the builders + proofs you use): | ||
|
|
||
| ```tsx | ||
| import type { AcceptanceTestBenchmarks } from "data/benchmarks/types"; | ||
| import { BenchmarkResults } from "data/benchmarks/types"; | ||
| import contributors from "data/contributors"; | ||
| import { | ||
| buildPassNoteForAT1, | ||
| // ...one builder per emitted test | ||
| } from "data/ens-best-practices/<category>/<best-practice-slug>/notes"; | ||
|
|
||
| import { parseTimestamp } from "@ensnode/ensnode-sdk"; | ||
|
|
||
| import at1Proof from "./at-1.png"; | ||
| // ...more proof imports | ||
|
|
||
| const <camelCaseBestPracticeSlug> = { | ||
| "<slug-for-id-1>": { | ||
| result: BenchmarkResults.Pass, | ||
| contributions: [{ from: contributors.<contributor>, lastUpdated: parseTimestamp("<datetime>") }], | ||
| notes: buildPassNoteForAT1({ | ||
| method: "<how it was tested>", | ||
| proof: { image: at1Proof, alt: "<app> correctly resolves ..." }, | ||
| }), | ||
| }, | ||
| // ...more entries | ||
| } as const satisfies AcceptanceTestBenchmarks; | ||
|
|
||
| export default <camelCaseBestPracticeSlug>; | ||
| ``` | ||
|
|
||
| The single `} as const satisfies AcceptanceTestBenchmarks;` at the end type-checks | ||
| every entry, so individual entries don't need a per-entry | ||
| `as const satisfies AcceptanceTestBenchmark` (and the `AcceptanceTestBenchmark` | ||
| type import isn't needed in deposit-addresses files). | ||
|
|
||
| Result-specific entry examples — match these renderings: | ||
|
|
||
| **Passed** (proof image present): | ||
|
|
||
| ```tsx | ||
| "at01-resolve-onchain-name": { | ||
| result: BenchmarkResults.Pass, | ||
| contributions: [{ from: contributors.y3drk, lastUpdated: parseTimestamp("2026-06-15T06:40:48Z") }], | ||
| notes: buildPassNoteForAT1({ | ||
| method: "the name lookup search on etherscan.io", | ||
| proof: { image: at1Proof, alt: "Etherscan correctly resolves the deposit address" }, | ||
| }), | ||
| }, | ||
| ``` | ||
|
|
||
| **Failed** (the builder bakes in the ENS name, expected address, and `<i>NOT</i>` | ||
| emphasis; add `extra` for any app-specific detail): | ||
|
|
||
| ```tsx | ||
| "at05-resolve-name-on-other-evm-chain": { | ||
| result: BenchmarkResults.Fail, | ||
| contributions: [{ from: contributors.y3drk, lastUpdated: parseTimestamp("2026-06-09T06:37:10Z") }], | ||
| notes: buildFailNoteForAT5({ | ||
| method: "the name lookup search on basescan.org", | ||
| proof: { image: at5Proof, alt: "Etherscan fails to resolve the Base deposit address" }, | ||
| extra: "The app showed the Ethereum Mainnet address instead.", | ||
| }), | ||
| }, | ||
| ``` | ||
|
|
||
| **Not applicable** (no proof needed — omit `proof`): | ||
|
|
||
| ```tsx | ||
| "at06-resolve-bitcoin-address": { | ||
| result: BenchmarkResults.NotApplicable, | ||
| contributions: [{ from: contributors.y3drk, lastUpdated: parseTimestamp("2026-06-08T16:18:00Z") }], | ||
| notes: buildNotApplicableForNonEvmChain({ chain: "Bitcoin" }), | ||
| }, | ||
| ``` | ||
|
|
||
| **Escape hatch** (result a builder doesn't cover — write custom prose; this is the | ||
| only place you may import `*Span` constants from `technicalDetails`): | ||
|
|
||
| ```tsx | ||
| notes: buildBenchmarkNote({ | ||
| proof: { image: at2Proof, alt: "..." }, | ||
| children: ( | ||
| <> | ||
| Tested using {method}. For {ethereumUnnormalizedEnsNameSpan} the resolved address is correct | ||
| ({ethereumAddressSpan}), but the app behaves inconsistently ... so we count this as a failure. | ||
| </> | ||
| ), | ||
| }), | ||
| ``` | ||
|
|
||
| **Legacy fallback** (only when the best practice has no `notes.tsx`): write the | ||
| note inline as `<div className={cn(acceptanceTestDetailsContainerStyles, "w-full")}>` | ||
| with a `<p>` and optional `<img>`, naming the concrete ENS name and expected | ||
| address via the `*Span` constants imported from `technicalDetails.tsx`. | ||
|
|
||
| ### Step 5: Wire into the app's main benchmarks file | ||
|
|
||
| In `ensawards.org/data/apps/<app>/benchmarks/index.tsx`, replace the target best | ||
| practice's placeholder block (the slug mapping to `undefined` entries) with a | ||
| reference to the generated module, and add the import: | ||
|
|
||
| ```tsx | ||
| import depositAddresses from "./resolution/deposit-addresses"; | ||
| // ... | ||
| "deposit-addresses": depositAddresses, | ||
| ``` | ||
|
|
||
| Use the actual directory where `manual.json` lives for the import path. Remove | ||
| any imports in the main file that become unused as a result. | ||
|
|
||
| ### Step 6: Typecheck | ||
|
|
||
| ```bash | ||
| cd ensawards.org && pnpm astro sync && pnpm exec tsc --noEmit | ||
| ``` | ||
|
|
||
| Fix any errors before finishing. The IDE may show a stale "is not a module" | ||
| error referencing a deleted `index.ts`; `tsc` is the source of truth. | ||
|
|
||
| ## Special overrides | ||
|
|
||
| ### deposit-addresses | ||
|
|
||
| Use the `notes.tsx` builders (see Step 1) for these recurring patterns instead of | ||
| writing markup: | ||
|
|
||
| - **App doesn't accept ENS names at all** (AT1 fails this way): use | ||
| `buildEnsNotSupportedNote({ method, proof })`. The remaining acceptance tests | ||
| then become Not Applicable via `buildNotApplicableForFailedTest({ testNumber: 1 })` | ||
| (default `scope: "at all"`). | ||
| - **`not-applicable` because a dependent test failed** (e.g. `reason` says | ||
| "at#5 is failed"): `buildNotApplicableForFailedTest({ testNumber, scope })`. | ||
| For the AT5 / Base cascade pass `scope: "on Base"`. | ||
| - **Non-EVM chain not supported** (Bitcoin / Solana): | ||
| `buildNotApplicableForNonEvmChain({ chain: "Bitcoin" })` / | ||
| `{ chain: "Solana" }` (pass `proof` if a screenshot exists). | ||
|
|
||
| Never import `*Span` constants or `bestPracticeTechnicalDetailsCodeStyles` / | ||
| `acceptanceTestDetailsContainerStyles` into a deposit-addresses app file — the | ||
| builders own all of that. The only exception is the `buildBenchmarkNote` escape | ||
| hatch, where you write custom prose and may import the spans you need. | ||
|
|
||
| ## Notes | ||
|
|
||
| - Only emit entries for ids present in `manual.json`. Other acceptance tests of | ||
| the best practice stay `undefined` in the main file (or are omitted from the | ||
| generated module and left `undefined` in the main file). | ||
| - Keep import grouping/order consistent with existing benchmark files: `data/*` | ||
| imports, then `@ensnode/*`, then `@/*`, then relative imports. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.