From e78c14cd90cf4c7ab12e9bb783467615a921ba31 Mon Sep 17 00:00:00 2001 From: qedk <1994constant@gmail.com> Date: Tue, 9 Jun 2026 20:56:27 +0100 Subject: [PATCH 1/5] feat: add workflow and script files --- .github/workflows/generate-aggregates.yml | 50 --------- .github/workflows/generate-and-publish.yml | 114 +++++++++++++++++++++ index.d.ts | 34 ++++++ index.js | 30 ++++++ package.json | 57 +++++++++++ scripts/build-npm.mjs | 34 ++++++ 6 files changed, 269 insertions(+), 50 deletions(-) delete mode 100644 .github/workflows/generate-aggregates.yml create mode 100644 .github/workflows/generate-and-publish.yml create mode 100644 index.d.ts create mode 100644 index.js create mode 100644 package.json create mode 100644 scripts/build-npm.mjs diff --git a/.github/workflows/generate-aggregates.yml b/.github/workflows/generate-aggregates.yml deleted file mode 100644 index b196b48..0000000 --- a/.github/workflows/generate-aggregates.yml +++ /dev/null @@ -1,50 +0,0 @@ -# This workflow runs scripts/ProtocolsTable.py and commits protocols.csv to this branch - -name: Generate CSV and JSON files - -on: - push: - branches: [ main, master ] - -jobs: - generate-csv: - runs-on: ubuntu-latest - - permissions: - # Give the default GITHUB_TOKEN write permission to commit and push the - # added or changed files to the repository. - contents: write - - steps: - - name: Checkout code - uses: actions/checkout@v6 - - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: '3.14' - - - name: Install uv - uses: astral-sh/setup-uv@v8.1.0 - with: - enable-cache: true - - - name: Install dependencies - run: uv sync - - - name: Run script (testnet) - run: | - source .venv/bin/activate - python scripts/ProtocolsTable.py --network testnet - - - name: Run script (mainnet) - run: | - source .venv/bin/activate - python scripts/ProtocolsTable.py --network mainnet - - # Commit all changed files back to the repository - - uses: stefanzweifel/git-auto-commit-action@v7 - with: - commit_message: "Update protocols CSV and JSON files" - file_pattern: "protocols-*.csv protocols-*.json" - push_options: "--force" diff --git a/.github/workflows/generate-and-publish.yml b/.github/workflows/generate-and-publish.yml new file mode 100644 index 0000000..df4996e --- /dev/null +++ b/.github/workflows/generate-and-publish.yml @@ -0,0 +1,114 @@ +# This workflow regenerates the protocol aggregates (protocols-*.csv/json), +# commits them back to the branch, and then publishes the data as an npm package. + +name: generate and publish + +on: + push: + branches: [ main, master ] + +# Serialize runs so two pushes can't race on the registry +concurrency: + group: generate-and-publish + cancel-in-progress: false + +jobs: + generate-and-publish: + runs-on: ubuntu-latest + + permissions: + # Give the default GITHUB_TOKEN write permission to commit and push the + # added or changed files to the repository. + contents: write + # Required for npm provenance: lets the job mint a short-lived OIDC token + # that Sigstore uses to sign the release. + id-token: write + + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: '3.14' + + - name: Install uv + uses: astral-sh/setup-uv@v8.1.0 + with: + enable-cache: true + + - name: Install dependencies + run: uv sync + + - name: Run script (testnet) + run: | + source .venv/bin/activate + python scripts/ProtocolsTable.py --network testnet + + - name: Run script (mainnet) + run: | + source .venv/bin/activate + python scripts/ProtocolsTable.py --network mainnet + + # Commit all changed files back to the repository + - uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "Update protocols CSV and JSON files" + file_pattern: "protocols-*.csv protocols-*.json" + push_options: "--force" + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + registry-url: 'https://registry.npmjs.org' + + - name: Set version from run number + run: | + # Keep major.minor from package.json, use the run number as the patch + # so every push to main produces a unique, monotonic release. + BASE=$(node -p "require('./package.json').version.split('.').slice(0, 2).join('.')") + VERSION="${BASE}.${GITHUB_RUN_NUMBER}" + echo "Publishing version ${VERSION}" + npm version "${VERSION}" --no-git-tag-version --allow-same-version + + - name: Build npm package + run: npm run build + + - name: Verify bundled files and casing + working-directory: dist + run: | + npm pack --dry-run --json > pack.json + node -e ' + const fs = require("fs"), path = require("path"); + const packed = new Set(JSON.parse(fs.readFileSync("pack.json", "utf8"))[0].files.map(f => f.path)); + const walk = d => fs.readdirSync(d, { withFileTypes: true }) + .flatMap(e => e.isDirectory() ? walk(path.join(d, e.name)) : [path.join(d, e.name)]); + const expected = [ + "mainnet.json", "testnet.json", "mainnet.csv", "testnet.csv", "categories.json", + ...walk("mainnet"), ...walk("testnet"), + ].filter(p => /\.(json|jsonc|csv)$/.test(p)); + const missing = expected.filter(p => !packed.has(p)); + if (missing.length) { + console.error("Files missing from the package (often a casing mismatch):\n " + missing.join("\n ")); + process.exit(1); + } + // Guard against case-only collisions that break case-insensitive filesystems. + const seen = new Map(); + for (const p of packed) { + const key = p.toLowerCase(); + if (seen.has(key) && seen.get(key) !== p) { + console.error(`Case collision: "${seen.get(key)}" vs "${p}"`); + process.exit(1); + } + seen.set(key, p); + } + console.log(`Verified ${expected.length} data files bundled with correct casing.`); + ' + + - name: Publish to npm with provenance + working-directory: dist + run: npm publish --provenance --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..78704bd --- /dev/null +++ b/index.d.ts @@ -0,0 +1,34 @@ +/** + * A category::sub-category pair, e.g. "DeFi::DEX". + */ +export type Category = string; + +/** + * A single protocol entry, keyed by its slug in the aggregate maps. + */ +export interface Protocol { + /** Display name of the protocol. */ + name: string; + /** Human-readable description. */ + description: string; + /** Whether the protocol is live, when present in the source data. */ + live?: boolean; + /** Category::sub-category pairs, primary first. */ + categories: Category[]; + /** Mapping of contract name -> address. */ + addresses: Record; + /** Mapping of link type (project, twitter, github, docs, ...) -> URL. */ + links: Record; +} + +/** Map of protocol slug -> protocol entry. */ +export type ProtocolMap = Record; + +/** Mainnet protocols, keyed by slug. */ +export declare const mainnet: ProtocolMap; + +/** Testnet protocols, keyed by slug. */ +export declare const testnet: ProtocolMap; + +/** The full list of valid category::sub-category values. */ +export declare const categories: { categories: Category[] }; diff --git a/index.js b/index.js new file mode 100644 index 0000000..2ebc4ef --- /dev/null +++ b/index.js @@ -0,0 +1,30 @@ +'use strict'; + +/** + * Aggregated Monad protocol data, bundled from the monad-crypto/protocols + * repository. The individual per-protocol files are also shipped in this + * package and can be imported directly, e.g.: + * + * const protocols = require('@monad-crypto/protocols'); + * protocols.mainnet['0x']; + * + * // or a single raw file (per-protocol casing is preserved): + * const kuru = require('@monad-crypto/protocols/mainnet/kuru.jsonc'); + * + * CSV summaries are shipped as files too and can be resolved via: + * require.resolve('@monad-crypto/protocols/mainnet.csv'); + * + * NOTE: the aggregate files referenced below are produced at bundle time by + * scripts/build-npm.mjs (the in-repo sources are protocols-{mainnet,testnet}.json). + * Run `npm run build` before requiring this file outside the published package. + */ + +const mainnet = require('./mainnet.json'); +const testnet = require('./testnet.json'); +const categories = require('./categories.json'); + +module.exports = { + mainnet, + testnet, + categories, +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..8563ca5 --- /dev/null +++ b/package.json @@ -0,0 +1,57 @@ +{ + "name": "@monad-crypto/protocols", + "version": "0.1.0", + "description": "Contract addresses, links, categories, and metadata for protocols on the Monad network.", + "homepage": "https://github.com/monad-crypto/protocols#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/monad-crypto/protocols.git" + }, + "bugs": { + "url": "https://github.com/monad-crypto/protocols/issues" + }, + "main": "index.js", + "types": "index.d.ts", + "exports": { + ".": { + "types": "./index.d.ts", + "default": "./index.js" + }, + "./categories.json": "./categories.json", + "./mainnet.json": "./mainnet.json", + "./testnet.json": "./testnet.json", + "./mainnet.csv": "./mainnet.csv", + "./testnet.csv": "./testnet.csv", + "./mainnet/*": "./mainnet/*", + "./testnet/*": "./testnet/*", + "./package.json": "./package.json" + }, + "files": [ + "index.js", + "index.d.ts", + "categories.json", + "mainnet.json", + "testnet.json", + "mainnet.csv", + "testnet.csv", + "mainnet", + "testnet" + ], + "scripts": { + "build": "node scripts/build-npm.mjs" + }, + "keywords": [ + "monad", + "protocols", + "contracts", + "addresses", + "web3", + "defi", + "token-list" + ], + "publishConfig": { + "access": "public", + "provenance": true, + "registry": "https://registry.npmjs.org" + } +} diff --git a/scripts/build-npm.mjs b/scripts/build-npm.mjs new file mode 100644 index 0000000..172ef85 --- /dev/null +++ b/scripts/build-npm.mjs @@ -0,0 +1,34 @@ +// Assembles the publishable npm package under dist/. + +import { cpSync, mkdirSync, rmSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = join(dirname(fileURLToPath(import.meta.url)), '..'); +const dist = join(root, 'dist'); + +rmSync(dist, { recursive: true, force: true }); +mkdirSync(dist, { recursive: true }); + +// Generated aggregates -> JS-package-friendly names +const aggregates = { + 'protocols-mainnet.json': 'mainnet.json', + 'protocols-testnet.json': 'testnet.json', + 'protocols-mainnet.csv': 'mainnet.csv', + 'protocols-testnet.csv': 'testnet.csv', +}; +for (const [src, dest] of Object.entries(aggregates)) { + cpSync(join(root, src), join(dist, dest)); +} + +// Files shipped under their existing names. +for (const f of ['categories.json', 'index.js', 'index.d.ts', 'package.json', 'README.md']) { + cpSync(join(root, f), join(dist, f)); +} + +// Per-protocol directories, copied verbatim (casing preserved). +for (const dir of ['mainnet', 'testnet']) { + cpSync(join(root, dir), join(dist, dir), { recursive: true }); +} + +console.log('Built dist/ npm package'); From f21cdc2678e92a6abd2e90da2298ef1ad298ba5c Mon Sep 17 00:00:00 2001 From: qedk <1994constant@gmail.com> Date: Wed, 10 Jun 2026 14:52:22 +0100 Subject: [PATCH 2/5] feat: publish as npm package --- .github/workflows/generate-and-publish.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/generate-and-publish.yml b/.github/workflows/generate-and-publish.yml index df4996e..a54a2e9 100644 --- a/.github/workflows/generate-and-publish.yml +++ b/.github/workflows/generate-and-publish.yml @@ -1,5 +1,10 @@ # This workflow regenerates the protocol aggregates (protocols-*.csv/json), # commits them back to the branch, and then publishes the data as an npm package. +# +# Publishing uses npm Trusted Publishing (OIDC) — there is NO NPM_TOKEN secret. +# A one-time setup is required on npmjs.com (package Settings -> Trusted +# Publisher): org monad-crypto, repo protocols, workflow generate-and-publish.yml. +# Renaming this workflow file breaks the trust relationship. name: generate and publish @@ -104,11 +109,9 @@ jobs: } seen.set(key, p); } - console.log(`Verified ${expected.length} data files bundled with correct casing.`); + console.log(`Verified ${expected.length} data files bundled.`); ' - - name: Publish to npm with provenance + - name: Publish to npm working-directory: dist - run: npm publish --provenance --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: npm publish From 1f61f77f503c7baf6b8318d744deac9b67cf2c51 Mon Sep 17 00:00:00 2001 From: qedk <1994constant@gmail.com> Date: Wed, 10 Jun 2026 14:52:59 +0100 Subject: [PATCH 3/5] fix: fix comment --- .github/workflows/generate-and-publish.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/generate-and-publish.yml b/.github/workflows/generate-and-publish.yml index a54a2e9..b39de49 100644 --- a/.github/workflows/generate-and-publish.yml +++ b/.github/workflows/generate-and-publish.yml @@ -1,10 +1,8 @@ # This workflow regenerates the protocol aggregates (protocols-*.csv/json), # commits them back to the branch, and then publishes the data as an npm package. # -# Publishing uses npm Trusted Publishing (OIDC) — there is NO NPM_TOKEN secret. -# A one-time setup is required on npmjs.com (package Settings -> Trusted -# Publisher): org monad-crypto, repo protocols, workflow generate-and-publish.yml. -# Renaming this workflow file breaks the trust relationship. +# Publishing uses npm Trusted Publishing (OIDC), renaming this workflow file +# breaks the trust relationship. name: generate and publish From 2849bcfbde6f017be20eb27c98f6469ae910c3c6 Mon Sep 17 00:00:00 2001 From: qedk <1994constant@gmail.com> Date: Wed, 10 Jun 2026 15:24:04 +0100 Subject: [PATCH 4/5] fix: strip script, add deploy guard --- .github/workflows/generate-and-publish.yml | 10 ++++++++-- scripts/build-npm.mjs | 12 +++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/workflows/generate-and-publish.yml b/.github/workflows/generate-and-publish.yml index b39de49..abe0bbc 100644 --- a/.github/workflows/generate-and-publish.yml +++ b/.github/workflows/generate-and-publish.yml @@ -19,6 +19,10 @@ jobs: generate-and-publish: runs-on: ubuntu-latest + # Pushes made with the default GITHUB_TOKEN (like our auto-commit below) + # don't re-trigger workflows + if: github.event.head_commit.message != 'Update protocols CSV and JSON files' + permissions: # Give the default GITHUB_TOKEN write permission to commit and push the # added or changed files to the repository. @@ -54,12 +58,14 @@ jobs: source .venv/bin/activate python scripts/ProtocolsTable.py --network mainnet - # Commit all changed files back to the repository + # Commit all changed files back to the repository. The commit sits on + # top of the SHA that triggered this run, so a plain push fast-forwards; + # if main advanced mid-run the push fails instead of clobbering it, and + # the next push regenerates the aggregates anyway. - uses: stefanzweifel/git-auto-commit-action@v7 with: commit_message: "Update protocols CSV and JSON files" file_pattern: "protocols-*.csv protocols-*.json" - push_options: "--force" - name: Set up Node.js uses: actions/setup-node@v6 diff --git a/scripts/build-npm.mjs b/scripts/build-npm.mjs index 172ef85..087055e 100644 --- a/scripts/build-npm.mjs +++ b/scripts/build-npm.mjs @@ -1,6 +1,6 @@ // Assembles the publishable npm package under dist/. -import { cpSync, mkdirSync, rmSync } from 'node:fs'; +import { cpSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -22,11 +22,17 @@ for (const [src, dest] of Object.entries(aggregates)) { } // Files shipped under their existing names. -for (const f of ['categories.json', 'index.js', 'index.d.ts', 'package.json', 'README.md']) { +for (const f of ['categories.json', 'index.js', 'index.d.ts', 'README.md']) { cpSync(join(root, f), join(dist, f)); } -// Per-protocol directories, copied verbatim (casing preserved). +// The published package is pre-built data: drop the scripts field so its +// metadata doesn't reference build tooling that isn't shipped. +const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')); +delete pkg.scripts; +writeFileSync(join(dist, 'package.json'), JSON.stringify(pkg, null, 2) + '\n'); + +// Per-protocol directories, copied verbatim for (const dir of ['mainnet', 'testnet']) { cpSync(join(root, dir), join(dist, dir), { recursive: true }); } From f64e8bab52b056ee743c5649e15aa04d8356ed16 Mon Sep 17 00:00:00 2001 From: qedk <1994constant@gmail.com> Date: Wed, 10 Jun 2026 15:26:03 +0100 Subject: [PATCH 5/5] fix: fix comment --- .github/workflows/generate-and-publish.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/generate-and-publish.yml b/.github/workflows/generate-and-publish.yml index abe0bbc..07d4dc2 100644 --- a/.github/workflows/generate-and-publish.yml +++ b/.github/workflows/generate-and-publish.yml @@ -59,9 +59,7 @@ jobs: python scripts/ProtocolsTable.py --network mainnet # Commit all changed files back to the repository. The commit sits on - # top of the SHA that triggered this run, so a plain push fast-forwards; - # if main advanced mid-run the push fails instead of clobbering it, and - # the next push regenerates the aggregates anyway. + # top of the SHA that triggered this run - uses: stefanzweifel/git-auto-commit-action@v7 with: commit_message: "Update protocols CSV and JSON files"