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
50 changes: 0 additions & 50 deletions .github/workflows/generate-aggregates.yml

This file was deleted.

119 changes: 119 additions & 0 deletions .github/workflows/generate-and-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# 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), renaming this workflow file
# breaks the trust relationship.

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

# 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.
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. The commit sits on
# top of the SHA that triggered this run
- uses: stefanzweifel/git-auto-commit-action@v7
with:
commit_message: "Update protocols CSV and JSON files"
file_pattern: "protocols-*.csv protocols-*.json"

- 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.`);
'

- name: Publish to npm
working-directory: dist
run: npm publish
34 changes: 34 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -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<string, string>;
/** Mapping of link type (project, twitter, github, docs, ...) -> URL. */
links: Record<string, string>;
}

/** Map of protocol slug -> protocol entry. */
export type ProtocolMap = Record<string, Protocol>;

/** 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[] };
30 changes: 30 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -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,
};
57 changes: 57 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -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"
},
Comment thread
QEDK marked this conversation as resolved.
"keywords": [
"monad",
"protocols",
"contracts",
"addresses",
"web3",
"defi",
"token-list"
],
"publishConfig": {
"access": "public",
"provenance": true,
"registry": "https://registry.npmjs.org"
}
}
40 changes: 40 additions & 0 deletions scripts/build-npm.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Assembles the publishable npm package under dist/.

import { cpSync, mkdirSync, readFileSync, rmSync, writeFileSync } 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', 'README.md']) {
cpSync(join(root, f), join(dist, f));
}

// 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 });
}

console.log('Built dist/ npm package');