Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
39 changes: 27 additions & 12 deletions .github/workflows/addressChecks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ name: Address Checks
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * *' # Runs daily at midnight
- cron: '0 0 * * *' # Runs daily at midnight UTC

jobs:
update-data:
runs-on: ubuntu-latest
permissions:
contents: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
Expand All @@ -19,21 +19,36 @@ jobs:
curl -fsSL https://bun.sh/install | bash -s "bun-v1.4.4"
echo "$HOME/.bun/bin" >> $GITHUB_PATH

- name: Install dependencies and build
run: |
bun install --frozen-lockfile
bun run build
- name: Install dependencies
run: bun install --frozen-lockfile

- name: Run checks and update data
- name: Run address checks
id: checks
continue-on-error: true
run: |
rm -f scripts/fetchedAddressData.json all_checks_passed.txt issue_body.md
bun run runAddressCheck
echo "ALL_CHECKS_PASSED=$(cat all_checks_passed.txt)" >> $GITHUB_ENV

- name: Create an issue if checks failed
if: env.ALL_CHECKS_PASSED != 'true'
- name: Save address-check report
if: ${{ !cancelled() && (steps.checks.outcome == 'success' || steps.checks.outcome == 'failure') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: address-check-report
path: |
scripts/fetchedAddressData.json
all_checks_passed.txt
issue_body.md
retention-days: 30
if-no-files-found: warn

- name: Create an issue if completed checks found mismatches
if: ${{ !cancelled() && steps.checks.outcome == 'failure' && hashFiles('issue_body.md') != '' }}
run: |
curl -H "Authorization: Bearer ${{ secrets.ADDRESS_CHECKS }}" \
curl --fail-with-body -H "Authorization: Bearer ${{ secrets.ADDRESS_CHECKS }}" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg title ':robot: Address Checks Failed' --arg body "$(cat issue_body.md)" '{title: $title, body: $body}')" \
https://api.github.com/repos/yearn/yearn-devdocs/issues
echo "Issue created"

- name: Fail the job if checks failed or could not complete
if: ${{ !cancelled() && steps.checks.outcome == 'failure' }}
run: exit 1
52 changes: 27 additions & 25 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,42 @@ jobs:
build-and-release:
runs-on: ubuntu-latest
permissions:
contents: write
contents: read
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Install Bun
run: |
curl -fsSL https://bun.sh/install | bash -s "bun-v1.4.4"
echo "$HOME/.bun/bin" >> $GITHUB_PATH
- name: Install dependencies and build
- name: Install dependencies
run: bun install --frozen-lockfile

- name: Test address-check pipeline
run: bun run test:addresses

- name: Check addresses and build
id: build
run: |
bun install --frozen-lockfile
rm -f scripts/fetchedAddressData.json all_checks_passed.txt issue_body.md
bun run build

- name: Check if PR is from a fork
id: check_fork
- name: Report address-check result
if: steps.build.outcome == 'success'
run: |
if [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then
echo "PR is from a fork"
echo "is_fork=true" >> $GITHUB_ENV
else
echo "PR is not from a fork"
echo "is_fork=false" >> $GITHUB_ENV
if [ "$(cat all_checks_passed.txt)" != 'true' ]; then
cat issue_body.md
echo '::error::Address validation found mismatches. The site contains the fresh warning report.'
exit 1
fi

- name: Run address checks
if: env.is_fork != 'true'
run: |
bun run runAddressCheck
echo "ALL_CHECKS_PASSED=$(cat all_checks_passed.txt)" >> $GITHUB_ENV

- name: Create an issue if address checks fail
if: env.is_fork != 'true' && env.ALL_CHECKS_PASSED != 'true'
run: |
curl -H "Authorization: Bearer ${{ secrets.ADDRESS_CHECKS }}" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg title ':robot: Address Checks Failed' --arg body "$(cat issue_body.md)" '{title: $title, body: $body}')" \
https://api.github.com/repos/yearn/yearn-devdocs/issues
echo "Issue created"
- name: Save address-check report
if: ${{ !cancelled() && (steps.build.outcome == 'success' || steps.build.outcome == 'failure') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: address-check-report
path: |
scripts/fetchedAddressData.json
all_checks_passed.txt
issue_body.md
retention-days: 30
if-no-files-found: warn
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ bun.lockb
.branch-name
.github/copilot-instructions.md
all_checks_passed.txt
issue_body.md

# Lockfiles managed by package managers (we use Bun)
package-lock.json
Expand Down
48 changes: 46 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,60 @@ This command starts a local development server and opens up a browser window. Mo
bun run start
```

Running `bun run start` will also run a script that checks the validity of smart contract addresses in the docs against a local constants file. This runs on every dev run as a way to keep the checks updated and the information about them on the site current (all PRs will include an update to last check time). If you want to start without the `runAddressChecks` script you can run `bun run start-no-check`.
`bun run start` collects a fresh mainnet address-check report before starting the
server. Completed checks with mismatches still start the site with warning banners.
An incomplete check, such as an RPC failure, stops startup. Use `bun run start-no-check`
only when you intentionally want to use the existing report without refreshing it.

## Build

This command generates static content into the `build` directory and can be served using any static content hosting service.
This command collects a fresh mainnet address-check report, then generates static
content in `build`. Vercel runs the same command, so the report included in a
deployment comes from that deployment's build.

```bash
bun run build
```

A completed check with mismatched addresses builds successfully and publishes the
fresh warning banner and check time. The independent address-check CI step fails
for those mismatches. RPC errors, missing reports, and incomplete checks stop the
build; the script removes the previous report before fetching, so an older green
snapshot cannot be reused after a failed check.

`bun run build:site` only renders the existing snapshot and generates AI exports.
Use it for an intentional offline build; it does not run address checks. Normal
builds and deployments use `bun run build`.

### Address-check results

Run the standalone checker with `bun run runAddressCheck`. Its exit codes are:

| Exit code | Meaning | Report |
| --- | --- | --- |
| 0 | All checks passed | Fresh complete report |
| 1 | Checks completed and found mismatches | Fresh complete report and failure details |
| 2 | Checks could not complete | No report available for deployment |

Results are written to `scripts/fetchedAddressData.json`, with the summary in
`all_checks_passed.txt` and mismatch details in `issue_body.md`. The banner imports
the JSON snapshot; it is not a live browser check. A V3 mismatch includes a latest
release disagreement, a documented release absent from the mainnet registry, or
an address disagreement. Gauge-count discrepancies also fail their group's result.

PR CI and the daily Address Checks workflow retain the JSON, summary, and available
failure details in the `address-check-report` artifact for 30 days. The daily job
checks addresses without rebuilding the website, keeps its existing mismatch issue
reporting, and fails on both mismatches and incomplete checks. It does not commit
generated data or deploy the website. Each later deployment collects its own fresh
report before rendering.

Address checks use the public mainnet RPC by default and also run for fork PRs;
there is no dependency on privileged credentials for validation. Configure
`RPC_URI_FOR_1` to use another mainnet RPC.

Run the focused regression checks with `bun run test:addresses`.

## AI / agent exports

The build also generates AI-friendly artifacts that will be served by the hosted site:
Expand Down
1 change: 1 addition & 0 deletions docs/developers/wallet-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Below are the relevant factories and registries for V3 contracts. The most recen

| Registry / ENS | Contract Address |
| ---------------------- | ---------------------- |
| **VaultFactory 3.1.1** | <ContractAddress contractName = {['releaseRegistry', '3.1.1', 'factory']} /> |
| **VaultFactory 3.1.0** | <ContractAddress contractName = {['releaseRegistry', '3.1.0', 'factory']} /> |
| **VaultFactory 3.0.4** | <ContractAddress contractName = {['releaseRegistry', '3.0.4', 'factory']} /> |
| **VaultFactory 3.0.3** | <ContractAddress contractName = {['releaseRegistry', '3.0.3', 'factory']} /> |
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
"scripts": {
"docusaurus": "docusaurus",
"get-branch-name": "git rev-parse --abbrev-ref HEAD > .branch-name",
"start": "bun run runAddressCheck && bun run get-branch-name && BRANCH_NAME=$(cat .branch-name) IS_DEV=true docusaurus start",
"start": "bun scripts/withAddressChecks.ts start-no-check",
"start-no-check": "bun run get-branch-name && BRANCH_NAME=$(cat .branch-name) IS_DEV=true docusaurus start",
"build": "docusaurus build && bun scripts/generateAiArtifacts.ts",
"build": "bun scripts/withAddressChecks.ts build:site",
"build:site": "docusaurus build && bun scripts/generateAiArtifacts.ts",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
Expand All @@ -20,6 +21,7 @@
"vydoc": "bunx vydoc -i ../yearn-vaults-v3/contracts/ -o ./templates/temp -t ./templates/contract.ejs -c $(which vyper)",
"v3-docs": "node natspec/scripts/natspec-generate.mjs",
"test:natspec": "node natspec/scripts/helpers/copyDocs.test.mjs",
"test:addresses": "bun test scripts/addressChecks.test.ts",
"runAddressCheck": "bun scripts/runAddressChecks.ts"
},
"packageManager": "bun@1.4.4",
Expand Down
50 changes: 50 additions & 0 deletions scripts/addressCheckReport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import fs from 'node:fs'
import path from 'node:path'
import type { AddressChecks, ContractAddresses } from '../src/ethereum/types'

export type AddressCheckReport = {
status: 'complete'
timeLastChecked: number
addressesData: ContractAddresses
addressChecks: AddressChecks
}

export function didAddressChecksPass(checks: AddressChecks): boolean {
return checks.allV3ChecksPassed === true &&
checks.allVeYfiChecksPassed === true &&
checks.allMultisigChecksPassed === true &&
checks.failedChecks.length === 0
}

export function isCompleteAddressCheckReport(value: unknown): value is AddressCheckReport {
if (!value || typeof value !== 'object') return false
const report = value as AddressCheckReport
const checks = report.addressChecks
return report.status === 'complete' &&
Number.isInteger(report.timeLastChecked) && report.timeLastChecked > 0 &&
!!report.addressesData?.v3ContractAddresses &&
typeof checks?.allV3ChecksPassed === 'boolean' &&
typeof checks.allVeYfiChecksPassed === 'boolean' &&
typeof checks.allMultisigChecksPassed === 'boolean' &&
Array.isArray(checks.failedChecks) && checks.failedChecks.every(item => typeof item === 'string')
}

export function writeAddressCheckReport(report: AddressCheckReport, directory: string): boolean {
if (!isCompleteAddressCheckReport(report)) throw new Error('Address checks returned an incomplete report')
const passed = didAddressChecksPass(report.addressChecks)
const reportPath = path.join(directory, 'scripts/fetchedAddressData.json')
const temporaryPath = `${reportPath}.${process.pid}.tmp`
fs.writeFileSync(temporaryPath, JSON.stringify(report, null, 2) + '\n')
fs.renameSync(temporaryPath, reportPath)
fs.writeFileSync(path.join(directory, 'all_checks_passed.txt'), String(passed))
if (!passed) {
const failures = report.addressChecks.failedChecks.length
? report.addressChecks.failedChecks
: ['One or more address-check groups did not pass.']
fs.writeFileSync(path.join(directory, 'issue_body.md'),
':robot::warning: **Automatic Address Checks have failed!**\n\n' +
failures.map(failure => `- ${failure}`).join('\n') +
'\n\nReview the on-chain observations against `src/ethereum/constants.ts` and the relevant documentation.\n')
}
return passed
}
Loading
Loading