Skip to content
Merged
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
12 changes: 7 additions & 5 deletions .cursor/rules/modernizer.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ The `src/` folder is nearly drained — only `src/components/IndividualPageConte
- [ ] Create backup branch

### During Upgrade
- [ ] Update package.json
- [ ] Run `pnpm install`
- [ ] Update package.json to versions that satisfy **both** Aikido Safe Chain (48h minimum package age; only `@aptos-labs/*` is excluded) **and** pnpm `minimumReleaseAge` (5 days)
- [ ] Run `pnpm install` through Safe Chain-wrapped pnpm (CI does this automatically). Never `--safe-chain-skip-minimum-package-age`, never `SAFE_CHAIN_MINIMUM_PACKAGE_AGE_HOURS=0`, never widen `SAFE_CHAIN_MINIMUM_PACKAGE_AGE_EXCLUSIONS`
- [ ] Fix TypeScript errors
- [ ] Update deprecated API usage
- [ ] Run full test suite
Expand Down Expand Up @@ -87,9 +87,11 @@ The `src/` folder is nearly drained — only `src/components/IndividualPageConte
## Commands

```bash
pnpm outdated # Check for updates
pnpm update # Update within ranges
pnpm add pkg@latest # Update to latest
pnpm outdated # Check for updates (does not install)
# Installs must go through Aikido Safe Chain. Pin exact versions that are
# at least 5 days old (pnpm minimumReleaseAge) and 48h old (Safe Chain).
# Do not `pnpm add pkg@latest` — that ignores the age gates.
pnpm add pkg@<age-eligible-version>
```

## Refactoring Principles
Expand Down
1 change: 1 addition & 0 deletions .cursor/rules/reviewer.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ You are acting as the **Reviewer** for the Aptos Explorer project.
- [ ] No secrets or API keys in code
- [ ] User input sanitized
- [ ] External links use `rel="noopener noreferrer"`
- [ ] Lockfile / dependency changes: Aikido Safe Chain was not bypassed (48h age floor, no skip flag, only `@aptos-labs/*` excluded)

### Accessibility
- [ ] Proper semantic HTML
Expand Down
12 changes: 9 additions & 3 deletions .github/actions/setup-node-pnpm/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ runs:
using: composite
steps:
- name: Setup pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10

- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
Expand All @@ -16,9 +16,15 @@ runs:
cache: pnpm
registry-url: "https://registry.npmjs.org"

- name: Allow first-party @aptos-labs/* packages through safe-chain age check
- name: Aikido Safe Chain age policy
shell: bash
run: echo "SAFE_CHAIN_MINIMUM_PACKAGE_AGE_EXCLUSIONS=@aptos-labs/*" >> "${GITHUB_ENV}"
run: |
# 48h is Safe Chain's default minimum package age. Set it explicitly so
# agents and future action edits cannot silently drop the floor to 0.
# The only age-check exclusion is first-party @aptos-labs/* packages.
# Do not add other names, and do not pass --safe-chain-skip-minimum-package-age.
echo "SAFE_CHAIN_MINIMUM_PACKAGE_AGE_HOURS=48" >> "${GITHUB_ENV}"
echo "SAFE_CHAIN_MINIMUM_PACKAGE_AGE_EXCLUSIONS=@aptos-labs/*" >> "${GITHUB_ENV}"

- name: Setup AikidoSec Safe Chain
uses: aptos-labs/actions/aikidosec-safe-chain@fdd3a3a628c840d5c35086a87e9cc3141b635505 # main (2026-05-20)
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
APTOS_DEVNET_API_KEY: ${{ secrets.APTOS_DEVNET_API_KEY }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: ./.github/actions/setup-node-pnpm

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/lint-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: ./.github/actions/setup-node-pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
Expand All @@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: ./.github/actions/setup-node-pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
Expand Down
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pnpm 11.9.0
pnpm 11.20.0
nodejs 24.14.1

16 changes: 16 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ This document serves as the canonical source of truth for AI coding assistants w

```bash
# Node: see `.node-version` (matches CI via actions/setup-node node-version-file)
# Installs must run through Aikido Safe Chain (CI wraps `pnpm` automatically).
# Never `--safe-chain-skip-minimum-package-age`. Only `@aptos-labs/*` is age-excluded.
pnpm install # Install dependencies
pnpm routes:generate # TanStack route tree (also runs before dev/build/lint/test via pre* scripts)
pnpm dev # Dev server on port 3030
Expand Down Expand Up @@ -252,6 +254,7 @@ This repository uses a multi-agent workflow with 7 specialized roles. Each role
- [ ] If the PR is user-visible or release-worthy: `CHANGELOG.md` updated under **[Unreleased]**
- [ ] If a feature was added, changed, or removed: `docs/FEATURES_SPECIFICATION.md` updated with the corresponding `FEAT-*` entry
- [ ] If Netlify deployment files changed: no new Netlify Edge Functions without explicit approval (see [Netlify and Edge Functions](#netlify-and-edge-functions))
- [ ] If dependencies or the lockfile changed: installs went through Aikido Safe Chain (48h age floor, no skip flag, only `@aptos-labs/*` excluded)

**Outputs**: Review feedback, approval or change requests

Expand Down Expand Up @@ -472,6 +475,19 @@ Environment variables in this repository are a **contract with deployments** (Ne
- **When adding a new env var**: document it in `.env.example` with a comment explaining what it controls, declare its TypeScript type in `app/types/declarations.d.ts` if it is `VITE_`-prefixed, and add it to `.github/workflows/ci.yml` if CI needs it.
- **When you must rename or remove an env var**: search the entire repo (`rg VITE_FOO`, `rg APTOS_FOO`), update `.env.example`, `app/types/declarations.d.ts`, `.github/workflows/*.yml`, `netlify.toml`, and any docs in the same PR, **and** call it out under `CHANGELOG.md` → `[Unreleased]` so operators know to update their dashboards.

### Aikido Safe Chain (required for every install)

CI wraps **every** `pnpm` invocation with [Aikido Safe Chain](https://github.com/AikidoSec/safe-chain) via `.github/actions/setup-node-pnpm` (`aptos-labs/actions/aikidosec-safe-chain`). Safe Chain MITM-proxies the npm registry, blocks malware against Aikido Intel, and **suppresses package versions newer than 48 hours**. That is in addition to pnpm's own `minimumReleaseAge: 7200` (5 days) in `pnpm-workspace.yaml`.

**Rules for all agents (no exceptions without explicit human approval):**

- **Do not bypass Safe Chain.** Never run `pnpm install` / `pnpm add` / `pnpm update` / `pnpm fetch` through a raw pnpm binary that is not Safe Chain-wrapped. In this repo's CI the wrapper is automatic; locally, put Safe Chain shims first on `PATH` (or use the same `setup-ci` install CI uses) before mutating the lockfile.
- **Do not skip the age gate.** Never pass `--safe-chain-skip-minimum-package-age`, never set `SAFE_CHAIN_MINIMUM_PACKAGE_AGE_HOURS=0`, and never lower the 48-hour floor.
- **Do not widen the exclusion list.** The only Safe Chain age-check exclusion is `@aptos-labs/*` (`SAFE_CHAIN_MINIMUM_PACKAGE_AGE_EXCLUSIONS`). Do not add other packages or scopes.
- **Do not use `minimumReleaseAgeExclude` as a bypass.** pnpm's 5-day gate is stricter than Safe Chain's 48 hours. Version-pinned exclude entries are allowed only as a temporary, audited exception for a package that still sits inside the 5-day window; remove the entry once that version ages out. An empty exclude list is the default.
- **When bumping dependencies:** pick versions that already satisfy **both** gates (Safe Chain 48h **and** pnpm 5 days). First-party `@aptos-labs/*` may be newer than 48 hours; they still must pass pnpm's 5-day gate unless an audited `minimumReleaseAgeExclude` entry exists.
- **After a lockfile change:** re-run install through Safe Chain so tarballs are scanned (`✔ Safe-chain: Scanned N packages, no malware found`). A frozen-lockfile install that only reuses the local store is not a scan.

---

## Tool-Specific Rules
Expand Down
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Security

- **pnpm audit — dependency security updates**: Bumped direct `vite` from `8.0.16` to `8.2.0`. Updated `pnpm-workspace.yaml` overrides for transitive packages flagged by `pnpm audit`: `tar@7.5.22`, `brace-expansion@5.0.9`, `undici@7.29.0`, `fast-uri@3.1.5`, `postcss@8.5.23`, `@opentelemetry/core@2.10.0`, `@opentelemetry/propagator-jaeger@2.10.0`, `svgo@4.0.2`, and `sharp@0.35.3`. `pnpm audit` now reports no known vulnerabilities. `minimumReleaseAgeExclude` was refreshed so only in-window packages (`fast-uri@3.1.5`, plus existing Renovate pins) remain excepted from the 5-day release-age gate.
- **pnpm audit — nanoid override**: Added a `nanoid@3.3.18` override so the PostCSS 3.x line picks up the GHSA-2v37-7h3g-55p8 fix (`customAlphabet` / `customRandom` infinite loop when `size` is 0). `image-size` (GHSA-w3rx-r6r6-pgpr / GHSA-5p2g-fcmc-qvqq) and `extract-zip` (GHSA-jmr9-qjv8-65gv) remain reported: no patched npm releases exist (`image-size@2.0.3` and `extract-zip@2.0.2` are unpublished). Both are transitive through `@netlify/vite-plugin-tanstack-start` (dev-only Netlify local tooling). Stale `minimumReleaseAgeExclude` entries were cleared now that locked versions are older than the 5-day gate. The updated lockfile was re-fetched through Aikido Safe Chain (malware scan of 1057 packages, 48h minimum package age, `@aptos-labs/*` the only age exclusion).

### Changed

- **Non-major dependency refresh**: Bumped age-eligible direct dependencies (5-day `minimumReleaseAge`) including MUI `9.0.1` → `9.3.1`, React `19.2.5` → `19.2.8`, Vite `8.2.0` → `8.2.1`, Biome `2.4.9` → `2.5.7` (`recommended: true` migrated to `preset: "recommended"`), Vitest / `@vitest/coverage-v8` `4.1.4` / `^4.1.6` → pinned `4.1.10`, TanStack Query `5.100.11` → `5.101.4`, TanStack Router/Start patch line (`react-router` `1.170.23`, `react-start` `1.168.40`, `router-plugin` `1.168.27`, `router-cli` `1.167.25`), Playwright `1.59.1` → `1.62.1`, `react-hook-form` `7.74.0` → `7.84.0`, `date-fns` `4.4.0`, `es-toolkit` `1.50.0`, `@tanstack/react-virtual` `3.14.9`, `@netlify/vite-plugin-tanstack-start` `1.3.17`, and matching type packages. Transitive overrides: `axios@1.19.0`, `js-yaml@5.2.3`, `postcss@8.5.26`, `h3@2.0.1-rc.26`. pnpm `11.9.0` → `11.20.0`. GitHub Actions: `actions/checkout` `v7.0.1`, `pnpm/action-setup` `v6.0.10`. The `@netlify/edge-functions-dev` `duplex: "half"` fetch patch was retargeted from `1.0.16` to `1.0.23` to match the new plugin tree. Majors and packages newer than five days (including `@aptos-labs/ts-sdk` `7.3.0` and Biome `2.5.8`) were left for a later bump.

- **TypeScript 6 → 7 upgrade**: Bumped `typescript` from `6.0.3` to `7.0.2`, the native Go-based compiler. `pnpm lint` / `tsc --noEmit` now use the TypeScript 7 `tsc` binary (typically much faster type-checking). No `tsconfig.json` or source changes were required; Vite 8 / Biome do not depend on the legacy TypeScript programmatic API, so the side-by-side `@typescript/typescript6` compatibility package is not needed for this repo.
- **Move decompiler WASM — bytecode v10 support**: Upgraded the bundled Move decompiler/disassembler WASM (`app/wasm/move_decompiler_wasm*`) to a build that supports **bytecode format version 10**. The rebuilt module reports `max_bytecode_version: 10` via `get_version_info()`, and `verify_module` / `decompile_module` / `disassemble_module` (and their `_script` counterparts) now correctly handle v10 modules and scripts across the decompiler views (Account **Code** tab, module diff view, and the transaction script-bytecode decompiler). The regenerated wasm-bindgen glue (`move_decompiler_wasm.js`) also gains the `console_error_panic_hook` host imports for clearer error messages.

Expand Down
16 changes: 6 additions & 10 deletions app/components/webMcpTools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,12 @@ describe("isAddressLike", () => {
])("accepts %s", (v) => {
expect(isAddressLike(v)).toBe(true);
});
it.each([
"",
"bob",
"0x",
"xyz.apt.foo",
"0xGHIJ",
"foo",
])("rejects %s", (v) => {
expect(isAddressLike(v)).toBe(false);
});
it.each(["", "bob", "0x", "xyz.apt.foo", "0xGHIJ", "foo"])(
"rejects %s",
(v) => {
expect(isAddressLike(v)).toBe(false);
},
);
});

describe("buildWebMcpTools", () => {
Expand Down
11 changes: 7 additions & 4 deletions app/pages/Account/Tabs/ModulesTab/Contract.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ import {
useTheme,
} from "@mui/material";
import React, {type ReactNode, useEffect, useMemo, useState} from "react";
import {Controller, type SubmitHandler, useForm} from "react-hook-form";
import {
Controller,
type SubmitHandler,
type UseFormHandleSubmit,
useForm,
} from "react-hook-form";
import type {Types} from "~/types/aptos";
import {view} from "../../../../api";
import {ResponseErrorType} from "../../../../api/client";
Expand Down Expand Up @@ -190,9 +195,7 @@ type ContractFormType = {
ledgerVersion?: string;
};

type FormTriggerSubmit = (
handler: SubmitHandler<ContractFormType>,
) => (e?: React.BaseSyntheticEvent) => Promise<void>;
type FormTriggerSubmit = UseFormHandleSubmit<ContractFormType>;

function getErrorMessage(error: unknown): string {
if (error instanceof Error) {
Expand Down
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"preset": "recommended",
"correctness": {
"noUnusedVariables": "warn",
"noUnusedImports": "warn",
Expand Down
60 changes: 30 additions & 30 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "aptos-explorer",
"version": "2.0.0",
"private": true,
"packageManager": "pnpm@11.9.0",
"packageManager": "pnpm@11.20.0",
"type": "module",
"engines": {
"node": "^24 || ^25"
Expand All @@ -13,30 +13,30 @@
"@download/blockies": "1.0.3",
"@emotion/react": "11.14.0",
"@emotion/styled": "11.14.1",
"@mui/icons-material": "9.0.1",
"@mui/material": "9.0.1",
"@noble/hashes": "2.2.0",
"@mui/icons-material": "9.3.1",
"@mui/material": "9.3.1",
"@noble/hashes": "2.3.0",
"@scure/base": "2.2.0",
"@tanstack/react-query": "5.100.11",
"@tanstack/react-router": "1.170.7",
"@tanstack/react-start": "1.168.10",
"@tanstack/react-virtual": "3.13.25",
"@uiw/react-json-view": "2.0.0-alpha.42",
"@tanstack/react-query": "5.101.4",
"@tanstack/react-router": "1.170.23",
"@tanstack/react-start": "1.168.40",
"@tanstack/react-virtual": "3.14.9",
"@uiw/react-json-view": "2.0.0-alpha.43",
"chart.js": "4.5.1",
"d3-geo": "3.1.1",
"d3-selection": "3.0.0",
"d3-zoom": "3.0.0",
"date-fns": "4.1.0",
"date-fns": "4.4.0",
"diff": "9.0.0",
"es-toolkit": "1.45.1",
"es-toolkit": "1.50.0",
"highlightjs-move": "0.3.0",
"js-cookie": "3.0.7",
"js-cookie": "3.0.8",
"pako": "2.1.0",
"react": "19.2.5",
"react": "19.2.8",
"react-chartjs-2": "5.3.1",
"react-dom": "19.2.5",
"react-dom": "19.2.8",
"react-helmet-async": "3.0.0",
"react-hook-form": "7.74.0",
"react-hook-form": "7.84.0",
"react-simple-maps": "3.0.0",
"react-syntax-highlighter": "16.1.1"
},
Expand Down Expand Up @@ -83,32 +83,32 @@
]
},
"devDependencies": {
"@biomejs/biome": "2.4.9",
"@biomejs/biome": "2.5.7",
"@codecov/vite-plugin": "2.0.1",
"@netlify/vite-plugin-tanstack-start": "1.3.5",
"@playwright/test": "1.59.1",
"@tanstack/react-query-devtools": "5.100.11",
"@tanstack/react-router-devtools": "1.167.0",
"@tanstack/router-cli": "1.167.9",
"@tanstack/router-plugin": "1.168.10",
"@netlify/vite-plugin-tanstack-start": "1.3.17",
"@playwright/test": "1.62.1",
"@tanstack/react-query-devtools": "5.101.4",
"@tanstack/react-router-devtools": "1.167.1",
"@tanstack/router-cli": "1.167.25",
"@tanstack/router-plugin": "1.168.27",
"@testing-library/react": "16.3.2",
"@types/js-cookie": "3.0.6",
"@types/jsdom": "28.0.1",
"@types/jsdom": "28.0.3",
"@types/pako": "2.0.4",
"@types/react": "19.2.14",
"@types/react-dom": "19.2.3",
"@types/react": "19.2.18",
"@types/react-dom": "19.2.4",
"@types/react-simple-maps": "3.0.6",
"@types/react-syntax-highlighter": "15.5.13",
"@vitejs/plugin-react-swc": "4.3.0",
"@vitest/coverage-v8": "^4.1.6",
"@vitejs/plugin-react-swc": "4.3.3",
"@vitest/coverage-v8": "4.1.10",
"husky": "9.1.7",
"jsdom": "30.0.1",
"lint-staged": "17.0.2",
"lint-staged": "17.3.0",
"rollup-plugin-visualizer": "7.0.1",
"typescript": "7.0.2",
"vite": "8.2.0",
"vite": "8.2.1",
"vite-plugin-compression": "0.5.1",
"vite-plugin-svgr": "5.2.0",
"vitest": "4.1.4"
"vitest": "4.1.10"
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
diff --git a/dist/node/main.js b/dist/node/main.js
index 4d66fb56936f7c8148b46ffeb8f398d2a16e06f0..c3b3b8dd2e344bd5212d82e7fd59fafb2114f780 100644
--- a/dist/node/main.js
+++ b/dist/node/main.js
@@ -230,7 +230,12 @@ var EdgeFunctionsHandler = class {
Expand Down
Loading