diff --git a/README.md b/README.md index 9692c78..0af9be2 100644 --- a/README.md +++ b/README.md @@ -6,24 +6,28 @@ ## 🌐 Supported Networks -| Network | Chain ID | Status | Endpoint | -|---------|----------|--------|----------| -| **Ethereum Mainnet** | 1 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/FV6RR6y13rsnCxBAicKuQEwDp8ioEGiNaWaZUmvr1F8k) | -| **Base Mainnet** | 8453 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/43s9hQRurMGjuYnC1r2ZwS6xSQktbFyXMPMqGKUFJojb) | -| **BSC Mainnet** | 56 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/D6aWqowLkWqBgcqmpNKXuNikPkob24ADXCciiP8Hvn1K) | -| **Polygon Mainnet** | 137 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/9q16PZv1JudvtnCAf44cBoxg82yK9SSsFvrjCY9xnneF) | -| **Monad** | 143 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/4tvLxkczjhSaMiqRrCV1EyheYHyJ7Ad8jub1UUyukBjg) | -| **Ethereum Sepolia** | 11155111 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/6wQRC7geo9XYAhckfmfo8kbMRLeWU8KQd3XsJqFKmZLT) | -| **Base Sepolia** | 84532 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/4yYAvQLFjBhBtdRCY7eUWo181VNoTSLLFd5M7FXQAi6u) | -| **BSC Chapel** | 97 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/BTjind17gmRZ6YhT9peaCM13SvWuqztsmqyfjpntbg3Z) | -| **Monad Testnet** | 10143 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/8iiMH9sj471jbp7AwUuuyBXvPJqCEsobuHBeUEKQSxhU) | -| **Polygon Amoy** | 80002 | ⛔️ Contracts not deployed | - | -| **Linea Sepolia** | 59141 | ⛔️ Contracts not deployed | - | -| **Hedera Testnet** | 296 | ⛔️ Contracts not deployed | - | -| **HyperEVM Testnet** | 998 | ⛔️ Contracts not deployed | - | -| **SKALE Base Sepolia** | 1351057110 | ⛔️ Contracts not deployed | - | - -Note: The Graph Gateway endpoints require authentication (API key / authorization header). If you see an β€œauth error”, use the gateway form `https://gateway.thegraph.com/api//subgraphs/id/`. +| Network | Chain ID | Status | The Graph | Goldsky | +|---------|----------|--------|-----------|---------| +| **Ethereum Mainnet** | 1 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/FV6RR6y13rsnCxBAicKuQEwDp8ioEGiNaWaZUmvr1F8k) | [Endpoint](https://api.goldsky.com/api/public/project_cmm2akwq17hxv01u972qzekpv/subgraphs/erc-8004-eth-mainnet/1.0.1/gn) | +| **Base Mainnet** | 8453 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/43s9hQRurMGjuYnC1r2ZwS6xSQktbFyXMPMqGKUFJojb) | [Endpoint](https://api.goldsky.com/api/public/project_cmm2akwq17hxv01u972qzekpv/subgraphs/erc-8004-base-mainnet/1.0.2/gn) | +| **BSC Mainnet** | 56 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/D6aWqowLkWqBgcqmpNKXuNikPkob24ADXCciiP8Hvn1K) | [Endpoint](https://api.goldsky.com/api/public/project_cmm2akwq17hxv01u972qzekpv/subgraphs/erc-8004-bsc-mainnet/1.0.1/gn) | +| **Polygon Mainnet** | 137 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/9q16PZv1JudvtnCAf44cBoxg82yK9SSsFvrjCY9xnneF) | [Endpoint](https://api.goldsky.com/api/public/project_cmm2akwq17hxv01u972qzekpv/subgraphs/erc-8004-polygon-mainnet/1.0.1/gn) | +| **Monad** | 143 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/4tvLxkczjhSaMiqRrCV1EyheYHyJ7Ad8jub1UUyukBjg) | [Endpoint](https://api.goldsky.com/api/public/project_cmm2akwq17hxv01u972qzekpv/subgraphs/erc-8004-monad-mainnet/1.0.1/gn) | +| **Ethereum Sepolia** | 11155111 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/6wQRC7geo9XYAhckfmfo8kbMRLeWU8KQd3XsJqFKmZLT) | [Endpoint](https://api.goldsky.com/api/public/project_cmm2akwq17hxv01u972qzekpv/subgraphs/erc-8004-eth-sepolia/1.0.1/gn) | +| **Base Sepolia** | 84532 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/4yYAvQLFjBhBtdRCY7eUWo181VNoTSLLFd5M7FXQAi6u) | [Endpoint](https://api.goldsky.com/api/public/project_cmm2akwq17hxv01u972qzekpv/subgraphs/erc-8004-base-sepolia/1.0.1/gn) | +| **BSC Chapel** | 97 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/BTjind17gmRZ6YhT9peaCM13SvWuqztsmqyfjpntbg3Z) | [Endpoint](https://api.goldsky.com/api/public/project_cmm2akwq17hxv01u972qzekpv/subgraphs/erc-8004-bsc-testnet/1.0.1/gn) | +| **Monad Testnet** | 10143 | βœ… Deployed | [Endpoint](https://gateway.thegraph.com/api/subgraphs/id/8iiMH9sj471jbp7AwUuuyBXvPJqCEsobuHBeUEKQSxhU) | [Endpoint](https://api.goldsky.com/api/public/project_cmm2akwq17hxv01u972qzekpv/subgraphs/erc-8004-monad-testnet/1.0.1/gn) | +| **Polygon Amoy** | 80002 | ⛔️ Contracts not deployed | - | - | +| **Linea Sepolia** | 59141 | ⛔️ Contracts not deployed | - | - | +| **Hedera Testnet** | 296 | ⛔️ Contracts not deployed | - | - | +| **HyperEVM Testnet** | 998 | ⛔️ Contracts not deployed | - | - | +| **SKALE Base Sepolia** | 1351057110 | ⛔️ Contracts not deployed | - | - | + +**The Graph:** Endpoints require authentication (API key / authorization header). Use the form `https://gateway.thegraph.com/api//subgraphs/id/`. + +**Goldsky:** Endpoints are public with 50/10s per-IP rate limits β€” no authentication required for queries. + +Provider support per network is configured via `graphNode.supported` and `goldsky.supported` in each network config file. ## πŸš€ Quick Start @@ -111,6 +115,11 @@ DEPLOYMENT=erc-8004-base-sepolia npm run build:single "startBlock": 1 }, "graphNode": { + "supported": true, + "network": "new-network" + }, + "goldsky": { + "supported": true, "network": "new-network" } } @@ -159,8 +168,9 @@ if (chainId.equals(BigInt.fromI32(123456))) { ### Deployment +#### The Graph Studio + ```bash -# Deploy to The Graph Studio (requires auth token) # Set your deployment key first: # npx graph auth --studio @@ -168,12 +178,48 @@ if (chainId.equals(BigInt.fromI32(123456))) { DEPLOYMENT=erc-8004-eth-sepolia npm run deploy # Deploy to Graph Studio (recommended; lets you set a release label) -# STUDIO_SLUG= DEPLOYMENT=erc-8004-eth-sepolia VERSION_LABEL= npm run deploy:studio +STUDIO_SLUG= \ +DEPLOYMENT=erc-8004-eth-sepolia \ +VERSION_LABEL= \ +npm run deploy:studio # Or deploy locally for testing npm run create-local && npm run deploy-local ``` +#### Goldsky + +Goldsky provides backwards-compatible subgraph hosting with improved performance and reliability. Goldsky also has public-query endpoints, allowing you to query the subgraph without authentication, making Goldsky endpoints ideal for agentic applications. + +**Prerequisites:** +- Install Goldsky CLI: `curl https://goldsky.com | sh` +- Obtain an API token from your [Goldsky Project Settings](https://app.goldsky.com) page + +**Important:** Unlike The Graph, Goldsky requires you to build before deploying. The deploy scripts handle this automatically. Also, Goldsky uses unique `name/version` pairs β€” redeploying the same version will error. Bump the `VERSION` or use tags to manage releases. + +```bash +# Deploy a single network to Goldsky +GOLDSKY_TOKEN= \ +DEPLOYMENT=erc-8004-eth-sepolia \ +VERSION=1.0.0 \ +npm run deploy:goldsky + +# Deploy with a tag (e.g., "prod") for stable endpoints +GOLDSKY_TOKEN= \ +DEPLOYMENT=erc-8004-eth-sepolia \ +VERSION=1.0.0 \ +TAG=prod \ +npm run deploy:goldsky + +# Deploy ALL supported networks to Goldsky +GOLDSKY_TOKEN= \ +VERSION=1.0.0 \ +npm run deploy:goldsky:all + +# Update a tag to point to a new version (zero-downtime swap) +goldsky subgraph tag create erc-8004-eth-sepolia/2.0.0 --tag prod --token $GOLDSKY_TOKEN +``` + ## πŸ“Š Overview This subgraph indexes data from three core smart contracts implementing the ERC-8004 standard: diff --git a/config/networks/base-mainnet.json b/config/networks/base-mainnet.json index 2dde5e5..5a7d32d 100644 --- a/config/networks/base-mainnet.json +++ b/config/networks/base-mainnet.json @@ -12,6 +12,11 @@ }, "validationRegistry": {}, "graphNode": { + "supported": true, + "network": "base" + }, + "goldsky": { + "supported": true, "network": "base" } } diff --git a/config/networks/base-sepolia.json b/config/networks/base-sepolia.json index 02d5a87..e46d8fc 100644 --- a/config/networks/base-sepolia.json +++ b/config/networks/base-sepolia.json @@ -12,6 +12,11 @@ }, "validationRegistry": {}, "graphNode": { + "supported": true, + "network": "base-sepolia" + }, + "goldsky": { + "supported": true, "network": "base-sepolia" } } diff --git a/config/networks/bsc-mainnet.json b/config/networks/bsc-mainnet.json index d3509bc..8c35810 100644 --- a/config/networks/bsc-mainnet.json +++ b/config/networks/bsc-mainnet.json @@ -12,6 +12,11 @@ }, "validationRegistry": {}, "graphNode": { + "supported": true, + "network": "bsc" + }, + "goldsky": { + "supported": true, "network": "bsc" } } diff --git a/config/networks/bsc-testnet.json b/config/networks/bsc-testnet.json index 14dfb1b..7270ea7 100644 --- a/config/networks/bsc-testnet.json +++ b/config/networks/bsc-testnet.json @@ -12,6 +12,11 @@ }, "validationRegistry": {}, "graphNode": { + "supported": true, + "network": "chapel" + }, + "goldsky": { + "supported": true, "network": "chapel" } } diff --git a/config/networks/eth-mainnet.json b/config/networks/eth-mainnet.json index 5b17b54..468e102 100644 --- a/config/networks/eth-mainnet.json +++ b/config/networks/eth-mainnet.json @@ -12,6 +12,11 @@ }, "validationRegistry": {}, "graphNode": { + "supported": true, + "network": "mainnet" + }, + "goldsky": { + "supported": true, "network": "mainnet" } } diff --git a/config/networks/eth-sepolia.json b/config/networks/eth-sepolia.json index 0328fa0..e951585 100644 --- a/config/networks/eth-sepolia.json +++ b/config/networks/eth-sepolia.json @@ -12,6 +12,11 @@ }, "validationRegistry": {}, "graphNode": { + "supported": true, + "network": "sepolia" + }, + "goldsky": { + "supported": true, "network": "sepolia" } } diff --git a/config/networks/hedera-testnet.json b/config/networks/hedera-testnet.json index 092eb4e..5b98236 100644 --- a/config/networks/hedera-testnet.json +++ b/config/networks/hedera-testnet.json @@ -6,6 +6,10 @@ "reputationRegistry": {}, "validationRegistry": {}, "graphNode": { + "supported": false, "network": "hedera-testnet" + }, + "goldsky": { + "supported": false } } diff --git a/config/networks/hyperevm-testnet.json b/config/networks/hyperevm-testnet.json index 1fe5a98..48384e1 100644 --- a/config/networks/hyperevm-testnet.json +++ b/config/networks/hyperevm-testnet.json @@ -6,6 +6,11 @@ "reputationRegistry": {}, "validationRegistry": {}, "graphNode": { + "supported": true, + "network": "hyperevm-testnet" + }, + "goldsky": { + "supported": true, "network": "hyperevm-testnet" } } diff --git a/config/networks/linea-sepolia.json b/config/networks/linea-sepolia.json index 7d5d02f..78bf4a5 100644 --- a/config/networks/linea-sepolia.json +++ b/config/networks/linea-sepolia.json @@ -6,6 +6,11 @@ "reputationRegistry": {}, "validationRegistry": {}, "graphNode": { + "supported": true, + "network": "linea-sepolia" + }, + "goldsky": { + "supported": true, "network": "linea-sepolia" } } diff --git a/config/networks/monad-mainnet.json b/config/networks/monad-mainnet.json index 10d6dc9..939a564 100644 --- a/config/networks/monad-mainnet.json +++ b/config/networks/monad-mainnet.json @@ -12,6 +12,11 @@ }, "validationRegistry": {}, "graphNode": { + "supported": true, + "network": "monad" + }, + "goldsky": { + "supported": true, "network": "monad" } } diff --git a/config/networks/monad-testnet.json b/config/networks/monad-testnet.json index 78c05f6..afdc50e 100644 --- a/config/networks/monad-testnet.json +++ b/config/networks/monad-testnet.json @@ -12,6 +12,11 @@ }, "validationRegistry": {}, "graphNode": { + "supported": true, + "network": "monad-testnet" + }, + "goldsky": { + "supported": true, "network": "monad-testnet" } } diff --git a/config/networks/polygon-amoy.json b/config/networks/polygon-amoy.json index 6b4a48e..9941ea9 100644 --- a/config/networks/polygon-amoy.json +++ b/config/networks/polygon-amoy.json @@ -6,6 +6,11 @@ "reputationRegistry": {}, "validationRegistry": {}, "graphNode": { + "supported": true, + "network": "polygon-amoy" + }, + "goldsky": { + "supported": true, "network": "polygon-amoy" } } diff --git a/config/networks/polygon-mainnet.json b/config/networks/polygon-mainnet.json index ea52792..1df6d9e 100644 --- a/config/networks/polygon-mainnet.json +++ b/config/networks/polygon-mainnet.json @@ -12,6 +12,11 @@ }, "validationRegistry": {}, "graphNode": { + "supported": true, + "network": "matic" + }, + "goldsky": { + "supported": true, "network": "matic" } } diff --git a/config/networks/skale-base-mainnet.json b/config/networks/skale-base-mainnet.json new file mode 100644 index 0000000..6c0e397 --- /dev/null +++ b/config/networks/skale-base-mainnet.json @@ -0,0 +1,22 @@ +{ + "network": "skale-base-mainnet", + "chainId": "1187947933", + "displayName": "SKALE Base Mainnet", + "identityRegistry": { + "address": "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432", + "startBlock": 689195 + }, + "reputationRegistry": { + "address": "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63", + "startBlock": 689195 + }, + "validationRegistry": {}, + "graphNode": { + "supported": false, + "network": "skale-base" + }, + "goldsky": { + "supported": true, + "netowrk": "skale-base" + } +} diff --git a/config/networks/skale-base-sepolia.json b/config/networks/skale-base-sepolia.json new file mode 100644 index 0000000..de81369 --- /dev/null +++ b/config/networks/skale-base-sepolia.json @@ -0,0 +1,22 @@ +{ + "network": "skale-base-sepolia", + "chainId": "1351057110", + "displayName": "SKALE Base Sepolia", + "identityRegistry": { + "address": "0x8004A818BFB912233c491871b3d84c89A494BD9e", + "startBlock": 923614 + }, + "reputationRegistry": { + "address": "0x8004B663056A597Dffe9eCcC1965A193B7388713", + "startBlock": 923615 + }, + "validationRegistry": {}, + "graphNode": { + "supported": false, + "network": "skale-base-sepolia-testnet" + }, + "goldsky": { + "supported": true, + "network": "skale-base-sepolia-testnet" + } +} diff --git a/config/networks/skale-sepolia.json b/config/networks/skale-sepolia.json deleted file mode 100644 index 84b0fe9..0000000 --- a/config/networks/skale-sepolia.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "network": "skale-base-sepolia-testnet", - "chainId": "1351057110", - "displayName": "SKALE Base Sepolia Testnet", - "identityRegistry": {}, - "reputationRegistry": {}, - "validationRegistry": {}, - "graphNode": { - "network": "skale-base-sepolia-testnet" - } -} diff --git a/deployments/deployment.json b/deployments/deployment.json index 712cedc..88d910a 100644 --- a/deployments/deployment.json +++ b/deployments/deployment.json @@ -10,6 +10,7 @@ "chainId": "11155111", "status": "prod", "configFile": "config/networks/eth-sepolia.json", + "goldskyName": "erc-8004-eth-sepolia", "versions": { "schema": "1.0.0", "subgraph": "1.0.0" @@ -21,6 +22,7 @@ "chainId": "1", "status": "prod", "configFile": "config/networks/eth-mainnet.json", + "goldskyName": "erc-8004-eth-mainnet", "versions": { "schema": "1.0.0", "subgraph": "1.0.0" @@ -32,28 +34,43 @@ "chainId": "84532", "status": "prod", "configFile": "config/networks/base-sepolia.json", + "goldskyName": "erc-8004-base-sepolia", "versions": { "schema": "1.0.0", "subgraph": "1.0.0" } }, - "erc-8004-linea-sepolia": { - "network": "linea-sepolia", - "displayName": "Linea Sepolia", - "chainId": "59141", + "erc-8004-base-mainnet": { + "network": "base-mainnet", + "displayName": "Base Mainnet", + "chainId": "8453", "status": "prod", - "configFile": "config/networks/linea-sepolia.json", + "configFile": "config/networks/base-mainnet.json", + "goldskyName": "erc-8004-base-mainnet", "versions": { "schema": "1.0.0", "subgraph": "1.0.0" } }, - "erc-8004-polygon-amoy": { - "network": "polygon-amoy", - "displayName": "Polygon Amoy", - "chainId": "80002", + "erc-8004-bsc-mainnet": { + "network": "bsc-mainnet", + "displayName": "BSC Mainnet", + "chainId": "56", "status": "prod", - "configFile": "config/networks/polygon-amoy.json", + "configFile": "config/networks/bsc-mainnet.json", + "goldskyName": "erc-8004-bsc-mainnet", + "versions": { + "schema": "1.0.0", + "subgraph": "1.0.0" + } + }, + "erc-8004-bsc-testnet": { + "network": "bsc-testnet", + "displayName": "BSC Testnet", + "chainId": "97", + "status": "prod", + "configFile": "config/networks/bsc-testnet.json", + "goldskyName": "erc-8004-bsc-testnet", "versions": { "schema": "1.0.0", "subgraph": "1.0.0" @@ -65,6 +82,55 @@ "chainId": "137", "status": "prod", "configFile": "config/networks/polygon-mainnet.json", + "goldskyName": "erc-8004-polygon-mainnet", + "versions": { + "schema": "1.0.0", + "subgraph": "1.0.0" + } + }, + "erc-8004-monad-mainnet": { + "network": "monad-mainnet", + "displayName": "Monad Mainnet", + "chainId": "143", + "status": "prod", + "configFile": "config/networks/monad-mainnet.json", + "goldskyName": "erc-8004-monad-mainnet", + "versions": { + "schema": "1.0.0", + "subgraph": "1.0.0" + } + }, + "erc-8004-monad-testnet": { + "network": "monad-testnet", + "displayName": "Monad Testnet", + "chainId": "10143", + "status": "prod", + "configFile": "config/networks/monad-testnet.json", + "goldskyName": "erc-8004-monad-testnet", + "versions": { + "schema": "1.0.0", + "subgraph": "1.0.0" + } + }, + "erc-8004-linea-sepolia": { + "network": "linea-sepolia", + "displayName": "Linea Sepolia", + "chainId": "59141", + "status": "prod", + "configFile": "config/networks/linea-sepolia.json", + "goldskyName": "erc-8004-linea-sepolia", + "versions": { + "schema": "1.0.0", + "subgraph": "1.0.0" + } + }, + "erc-8004-polygon-amoy": { + "network": "polygon-amoy", + "displayName": "Polygon Amoy", + "chainId": "80002", + "status": "prod", + "configFile": "config/networks/polygon-amoy.json", + "goldskyName": "erc-8004-polygon-amoy", "versions": { "schema": "1.0.0", "subgraph": "1.0.0" @@ -87,6 +153,18 @@ "chainId": "998", "status": "prod", "configFile": "config/networks/hyperevm-testnet.json", + "goldskyName": "erc-8004-hyperevm-testnet", + "versions": { + "schema": "1.0.0", + "subgraph": "1.0.0" + } + }, + "erc-8004-skale-base-mainnet": { + "network": "skale-base-mainnet", + "displayName": "SKALE Base Mainnet", + "chainId": "1187947933", + "status": "prod", + "configFile": "config/networks/skale-base-mainnet.json", "versions": { "schema": "1.0.0", "subgraph": "1.0.0" @@ -97,7 +175,7 @@ "displayName": "SKALE Base Sepolia Testnet", "chainId": "1351057110", "status": "prod", - "configFile": "config/networks/skale-sepolia.json", + "configFile": "config/networks/skale-base-sepolia.json", "versions": { "schema": "1.0.0", "subgraph": "1.0.0" diff --git a/package-lock.json b/package-lock.json index 675da83..fb6f534 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1113,10 +1113,14 @@ } }, "node_modules/@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", - "license": "MIT" + "version": "25.4.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.4.0.tgz", + "integrity": "sha512-9wLpoeWuBlcbBpOY3XmzSTG3oscB6xjBEEtn+pYXTfhyXhIxC5FsBer2KTopBlvKEiW9l13po9fq+SJY/5lkhw==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~7.18.0" + } }, "node_modules/@types/parse-json": { "version": "4.0.2", @@ -3455,6 +3459,12 @@ "node": ">=8" } }, + "node_modules/jayson/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4947,6 +4957,12 @@ "node": ">=20.18.1" } }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", diff --git a/package.json b/package.json index 62a4ef0..31045ed 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "build:single": "graph build deployments/generated/$DEPLOYMENT/subgraph.yaml", "deploy": "graph deploy --node https://api.thegraph.com/deploy/ agent0-sdk/agent0-sdk", "deploy:studio": "node scripts/deploy-studio.js", + "deploy:goldsky": "node scripts/deploy-goldsky.js", + "deploy:goldsky:all": "npm run generate && node scripts/deploy-goldsky-all.js", "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 agent0-sdk/agent0-sdk", "create-local": "graph create --node http://localhost:8020/ agent0-sdk/agent0-sdk", "remove-local": "graph remove --node http://localhost:8020/ agent0-sdk/agent0-sdk", diff --git a/scripts/deploy-goldsky-all.js b/scripts/deploy-goldsky-all.js new file mode 100644 index 0000000..2a8362f --- /dev/null +++ b/scripts/deploy-goldsky-all.js @@ -0,0 +1,216 @@ +#!/usr/bin/env node + +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +const ROOT_DIR = path.join(__dirname, '..'); +const NETWORKS_DIR = path.join(ROOT_DIR, 'config', 'networks'); +const OUTPUT_DIR = path.join(ROOT_DIR, 'deployments', 'generated'); +const DEFAULT_VERSION = '1.0.0'; + +// Colors +const colors = { + reset: '\x1b[0m', + green: '\x1b[32m', + yellow: '\x1b[33m', + red: '\x1b[31m', + cyan: '\x1b[36m', + bold: '\x1b[1m', +}; + +function log(message, color = 'reset') { + console.log(`${colors[color]}${message}${colors.reset}`); +} + +function prepareBuildForGoldsky() { + const buildDir = path.join(ROOT_DIR, 'build'); + const abisDir = path.join(ROOT_DIR, 'abis'); + const buildAbisDir = path.join(buildDir, 'abis'); + + if (!fs.existsSync(buildAbisDir)) { + fs.mkdirSync(buildAbisDir, { recursive: true }); + } + for (const file of fs.readdirSync(abisDir)) { + fs.copyFileSync(path.join(abisDir, file), path.join(buildAbisDir, file)); + } + + const manifestPath = path.join(buildDir, 'subgraph.yaml'); + let manifest = fs.readFileSync(manifestPath, 'utf8'); + manifest = manifest.replace(/\.\.\/\.\.\/abis\//g, 'abis/'); + fs.writeFileSync(manifestPath, manifest); +} + +function loadNetworkConfigs() { + const networkFiles = fs.readdirSync(NETWORKS_DIR).filter(f => f.endsWith('.json')); + const configs = {}; + + for (const file of networkFiles) { + const networkName = file.replace('.json', ''); + const configPath = path.join(NETWORKS_DIR, file); + const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); + configs[networkName] = config; + } + + return configs; +} + +function isDeployable(config) { + // Must be Goldsky-supported + if (!config.goldsky || !config.goldsky.supported) { + return false; + } + + // Must have contracts deployed (non-empty identityRegistry) + if (!config.identityRegistry || !config.identityRegistry.address) { + return false; + } + + return true; +} + +function main() { + log('\nπŸš€ Deploying all subgraphs to Goldsky\n', 'cyan'); + + const goldskyToken = process.env.GOLDSKY_TOKEN; + const version = process.env.VERSION || DEFAULT_VERSION; + const tag = process.env.TAG; + + if (!goldskyToken) { + log('❌ Error: GOLDSKY_TOKEN environment variable not set', 'red'); + log('\nGet your API token from your Goldsky Project Settings page.', 'cyan'); + log('\nUsage:', 'cyan'); + log(' GOLDSKY_TOKEN= VERSION=1.0.0 npm run deploy:goldsky:all\n', 'yellow'); + process.exit(1); + } + + // Load all network configs + const networkConfigs = loadNetworkConfigs(); + const networkNames = Object.keys(networkConfigs).sort(); + + // Filter to deployable networks + const deployable = []; + const skipped = []; + + for (const name of networkNames) { + const config = networkConfigs[name]; + if (isDeployable(config)) { + deployable.push({ name, config }); + } else { + const reason = !config.goldsky?.supported + ? 'not supported on Goldsky' + : 'no contracts deployed'; + skipped.push({ name, config, reason }); + } + } + + log(`πŸ“Š Found ${networkNames.length} networks:`, 'cyan'); + log(` βœ… Deployable: ${deployable.length}`, 'green'); + log(` ⏭️ Skipped: ${skipped.length}`, 'yellow'); + log(''); + + if (skipped.length > 0) { + log('Skipping:', 'yellow'); + skipped.forEach(({ name, config, reason }) => { + log(` - ${config.displayName || name}: ${reason}`, 'yellow'); + }); + log(''); + } + + if (deployable.length === 0) { + log('❌ No deployable networks found.', 'red'); + process.exit(1); + } + + // Deploy each network + const results = []; + + for (const { name, config } of deployable) { + const deployment = `erc-8004-${name}`; + const manifestPath = path.join(OUTPUT_DIR, deployment, 'subgraph.yaml'); + const goldskyName = deployment; + + if (!fs.existsSync(manifestPath)) { + log(`⚠️ Skipping ${config.displayName}: manifest not found (run "npm run generate" first)`, 'yellow'); + results.push({ name, displayName: config.displayName, success: false, error: 'manifest not found' }); + continue; + } + + try { + log(`\n${'─'.repeat(70)}`, 'cyan'); + log(`πŸ”¨ Building & deploying ${config.displayName} (${goldskyName}/${version})...`, 'cyan'); + log(`${'─'.repeat(70)}`, 'cyan'); + + // Codegen + execSync(`graph codegen ${manifestPath}`, { + cwd: ROOT_DIR, + stdio: 'inherit', + }); + + // Build + execSync(`graph build ${manifestPath}`, { + cwd: ROOT_DIR, + stdio: 'inherit', + }); + + // Prepare build dir for Goldsky (copy ABIs, fix paths) + prepareBuildForGoldsky(); + + // Deploy + const buildDir = path.join(ROOT_DIR, 'build'); + let deployCmd = `goldsky subgraph deploy ${goldskyName}/${version} --path ${buildDir} --token ${goldskyToken}`; + + if (tag) { + deployCmd += ` --tag ${tag}`; + } + + execSync(deployCmd, { + cwd: ROOT_DIR, + stdio: 'inherit', + }); + + log(` βœ… ${config.displayName} deployed successfully`, 'green'); + results.push({ name, displayName: config.displayName, success: true }); + + } catch (error) { + log(` ❌ ${config.displayName} failed to deploy`, 'red'); + results.push({ name, displayName: config.displayName, success: false, error: error.message }); + } + } + + // Summary + log('\n' + '='.repeat(70), 'cyan'); + log('πŸ“Š Goldsky Deployment Summary\n', 'cyan'); + + const successful = results.filter(r => r.success); + const failed = results.filter(r => !r.success); + + log(`βœ… Successful: ${successful.length}/${results.length}`, 'green'); + successful.forEach(r => { + log(` - ${r.displayName}`, 'green'); + }); + + if (failed.length > 0) { + log(`\n❌ Failed: ${failed.length}`, 'red'); + failed.forEach(r => { + log(` - ${r.displayName}: ${r.error}`, 'red'); + }); + } + + if (skipped.length > 0) { + log(`\n⏭️ Skipped: ${skipped.length}`, 'yellow'); + skipped.forEach(({ config, reason }) => { + log(` - ${config.displayName}: ${reason}`, 'yellow'); + }); + } + + log('\n' + '='.repeat(70) + '\n', 'cyan'); + + if (failed.length > 0) { + process.exit(1); + } +} + +if (require.main === module) { + main(); +} diff --git a/scripts/deploy-goldsky.js b/scripts/deploy-goldsky.js new file mode 100644 index 0000000..cacc556 --- /dev/null +++ b/scripts/deploy-goldsky.js @@ -0,0 +1,207 @@ +#!/usr/bin/env node + +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +const ROOT_DIR = path.join(__dirname, '..'); +const NETWORKS_DIR = path.join(ROOT_DIR, 'config', 'networks'); +const OUTPUT_DIR = path.join(ROOT_DIR, 'deployments', 'generated'); +const DEFAULT_VERSION = '1.0.0'; + +// Colors +const colors = { + reset: '\x1b[0m', + green: '\x1b[32m', + yellow: '\x1b[33m', + red: '\x1b[31m', + cyan: '\x1b[36m', + bold: '\x1b[1m', +}; + +function log(message, color = 'reset') { + console.log(`${colors[color]}${message}${colors.reset}`); +} + +function prepareBuildForGoldsky() { + // graph build outputs to build/ but subgraph.yaml still has relative ABI paths + // (e.g., ../../abis/X.json). Goldsky needs ABIs co-located in the build dir. + const buildDir = path.join(ROOT_DIR, 'build'); + const abisDir = path.join(ROOT_DIR, 'abis'); + const buildAbisDir = path.join(buildDir, 'abis'); + + // Copy abis/ into build/abis/ + if (!fs.existsSync(buildAbisDir)) { + fs.mkdirSync(buildAbisDir, { recursive: true }); + } + for (const file of fs.readdirSync(abisDir)) { + fs.copyFileSync(path.join(abisDir, file), path.join(buildAbisDir, file)); + } + + // Rewrite ABI paths in build/subgraph.yaml to use local abis/ dir + const manifestPath = path.join(buildDir, 'subgraph.yaml'); + let manifest = fs.readFileSync(manifestPath, 'utf8'); + manifest = manifest.replace(/\.\.\/\.\.\/abis\//g, 'abis/'); + fs.writeFileSync(manifestPath, manifest); +} + +function getNetworkConfig(deployment) { + // Derive network name from deployment (e.g., "erc-8004-eth-sepolia" -> "eth-sepolia") + const networkName = deployment.replace('erc-8004-', ''); + const configPath = path.join(NETWORKS_DIR, `${networkName}.json`); + + if (!fs.existsSync(configPath)) { + return null; + } + + return JSON.parse(fs.readFileSync(configPath, 'utf8')); +} + +function getDeploymentInfo() { + const deployment = process.env.DEPLOYMENT; + const goldskyToken = process.env.GOLDSKY_TOKEN; + + if (!deployment) { + log('\n❌ Error: DEPLOYMENT environment variable not set', 'red'); + log('\nUsage:', 'cyan'); + log(' GOLDSKY_TOKEN= \\', 'yellow'); + log(' DEPLOYMENT=erc-8004-base-sepolia \\', 'yellow'); + log(' VERSION=1.0.0 \\', 'yellow'); + log(' npm run deploy:goldsky\n', 'yellow'); + process.exit(1); + } + + if (!goldskyToken) { + log('\n❌ Error: GOLDSKY_TOKEN environment variable not set', 'red'); + log('\nGet your API token from your Goldsky Project Settings page.', 'cyan'); + log('\nUsage:', 'cyan'); + log(' GOLDSKY_TOKEN= \\', 'yellow'); + log(' DEPLOYMENT=erc-8004-base-sepolia \\', 'yellow'); + log(' npm run deploy:goldsky\n', 'yellow'); + process.exit(1); + } + + // Verify deployment exists + const manifestPath = path.join(OUTPUT_DIR, deployment, 'subgraph.yaml'); + if (!fs.existsSync(manifestPath)) { + log(`\n❌ Error: Deployment manifest not found: ${manifestPath}`, 'red'); + log('\nAvailable deployments:', 'cyan'); + if (fs.existsSync(OUTPUT_DIR)) { + const deployments = fs.readdirSync(OUTPUT_DIR).filter(f => { + return fs.statSync(path.join(OUTPUT_DIR, f)).isDirectory(); + }); + deployments.forEach(d => log(` - ${d}`, 'yellow')); + } + log('\nRun "npm run generate" to create deployment manifests.\n', 'cyan'); + process.exit(1); + } + + // Load network config + const networkConfig = getNetworkConfig(deployment); + + // Check Goldsky support + if (networkConfig && networkConfig.goldsky && !networkConfig.goldsky.supported) { + log(`\n❌ Error: ${networkConfig.displayName} is not supported on Goldsky`, 'red'); + log('This network does not have a matching Goldsky chain slug.\n', 'yellow'); + process.exit(1); + } + + return { + deployment, + goldskyToken, + manifestPath, + networkConfig, + }; +} + +function main() { + log('\nπŸš€ Deploying to Goldsky\n', 'cyan'); + + const { deployment, goldskyToken, manifestPath, networkConfig } = getDeploymentInfo(); + const version = process.env.VERSION || DEFAULT_VERSION; + const tag = process.env.TAG; + const goldskyName = deployment; + + // Display deployment info + log('πŸ“‹ Deployment Details:', 'bold'); + log(` Deployment: ${deployment}`, 'cyan'); + log(` Goldsky Name: ${goldskyName}/${version}`, 'cyan'); + if (tag) { + log(` Tag: ${tag}`, 'cyan'); + } + if (networkConfig) { + log(` Network: ${networkConfig.displayName} (Chain ID: ${networkConfig.chainId})`, 'cyan'); + } + log(` Manifest: ${manifestPath}`, 'cyan'); + log(''); + + try { + // Build first β€” Goldsky requires pre-built subgraphs + log('πŸ“¦ Running codegen...', 'yellow'); + execSync(`graph codegen ${manifestPath}`, { + cwd: ROOT_DIR, + stdio: 'inherit', + }); + + log('πŸ”¨ Building subgraph...', 'yellow'); + execSync(`graph build ${manifestPath}`, { + cwd: ROOT_DIR, + stdio: 'inherit', + }); + + // Prepare build dir for Goldsky (copy ABIs, fix paths) + prepareBuildForGoldsky(); + + // Deploy to Goldsky + log('🚒 Deploying to Goldsky...', 'green'); + log(''); + + const buildDir = path.join(ROOT_DIR, 'build'); + let deployCmd = `goldsky subgraph deploy ${goldskyName}/${version} --path ${buildDir} --token ${goldskyToken}`; + + if (tag) { + deployCmd += ` --tag ${tag}`; + } + + execSync(deployCmd, { + cwd: ROOT_DIR, + stdio: 'inherit', + }); + + log(''); + log('='.repeat(70), 'cyan'); + log('βœ… Goldsky Deployment Successful!', 'green'); + log('='.repeat(70), 'cyan'); + log(''); + log(`πŸ“¦ Subgraph: ${goldskyName}/${version}`, 'cyan'); + if (tag) { + log(`🏷️ Tag: ${tag}`, 'cyan'); + } + log(''); + log('πŸ’‘ To tag this version for production:', 'yellow'); + log(` goldsky subgraph tag create ${goldskyName}/${version} --tag prod --token $GOLDSKY_TOKEN`, 'yellow'); + log(''); + + } catch (error) { + log(''); + log('='.repeat(70), 'red'); + log('❌ Goldsky Deployment Failed', 'red'); + log('='.repeat(70), 'red'); + log(''); + + if (error.message && error.message.includes('already exists')) { + log('⚠️ This name/version already exists on Goldsky.', 'yellow'); + log(''); + log('Goldsky requires unique name/version pairs. Options:', 'cyan'); + log(` 1. Bump version: VERSION=1.1.0 DEPLOYMENT=${deployment} npm run deploy:goldsky`, 'yellow'); + log(` 2. Delete old: goldsky subgraph delete ${goldskyName}/${process.env.VERSION || DEFAULT_VERSION} --token $GOLDSKY_TOKEN`, 'yellow'); + log(''); + } + + process.exit(1); + } +} + +if (require.main === module) { + main(); +} diff --git a/src/constants.ts b/src/constants.ts index 05b931e..b8b85a9 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -25,7 +25,8 @@ export namespace Network { export const MONAD_TESTNET = "monad-testnet"; export const HEDERA_TESTNET = "hedera-testnet"; export const HYPEREVM_TESTNET = "hyperevm-testnet"; - export const SKALE_SEPOLIA = "skale-base-sepolia-testnet"; + export const SKALE_BASE_MAINNET = "skale-base-mainnet"; + export const SKALE_BASE_SEPOLIA = "skale-base-sepolia-testnet"; } /** @@ -47,7 +48,8 @@ export function getChainIdFromNetwork(network: string): BigInt { if (network == Network.MONAD_TESTNET) return BigInt.fromI32(10143); if (network == Network.HEDERA_TESTNET) return BigInt.fromI32(296); if (network == Network.HYPEREVM_TESTNET) return BigInt.fromI32(998); - if (network == Network.SKALE_SEPOLIA) return BigInt.fromString("1351057110"); + if (network == Network.SKALE_BASE_SEPOLIA) return BigInt.fromString("1351057110"); + if (network == Network.SKALE_BASE_MAINNET) return BigInt.fromString("1187947933"); // Unknown network - return 0 return BigInt.fromI32(0); @@ -71,7 +73,8 @@ export function getNetworkDisplayName(network: string): string { if (network == Network.MONAD_TESTNET) return "Monad Testnet"; if (network == Network.HEDERA_TESTNET) return "Hedera Testnet"; if (network == Network.HYPEREVM_TESTNET) return "HyperEVM Testnet"; - if (network == Network.SKALE_SEPOLIA) return "SKALE Base Sepolia Testnet"; + if (network == Network.SKALE_BASE_SEPOLIA) return "SKALE Base Sepolia Testnet"; + if (network == Network.SKALE_BASE_MAINNET) return "SKALE Base Mainnet"; return `Unknown Network (${network})`; } diff --git a/src/contract-addresses.ts b/src/contract-addresses.ts index e842875..ec1897d 100644 --- a/src/contract-addresses.ts +++ b/src/contract-addresses.ts @@ -69,6 +69,15 @@ export function getContractAddresses(chainId: BigInt): ContractAddresses { zero ) } + // Base Mainnet (8453) + else if (chainId.equals(BigInt.fromI32(8453))) { + return new ContractAddresses( + Bytes.fromHexString("0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"), + Bytes.fromHexString("0x8004BAa17C55a88189AE136b182e5fdA19dE9b63"), + // Validation registry not configured / indexing paused + Bytes.fromHexString("0x0000000000000000000000000000000000000000") + ) + } // Ethereum Sepolia (11155111) else if (chainId.equals(BigInt.fromI32(11155111))) { return new ContractAddresses( @@ -136,12 +145,19 @@ export function getContractAddresses(chainId: BigInt): ContractAddresses { // SKALE Base Sepolia Testnet (1351057110) else if (chainId.equals(BigInt.fromString("1351057110"))) { return new ContractAddresses( - zero, - zero, + Bytes.fromHexString("0x8004A818BFB912233c491871b3d84c89A494BD9e"), + Bytes.fromHexString("0x8004B663056A597Dffe9eCcC1965A193B7388713"), + zero + ) + } + // SKALE Base Mainnet (1187947933) + else if (chainId.equals(BigInt.fromString("1187947933"))) { + return new ContractAddresses( + Bytes.fromHexString("0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"), + Bytes.fromHexString("0x8004BAa17C55a88189AE136b182e5fdA19dE9b63"), zero ) } - // Unsupported chain - return zero addresses return new ContractAddresses( zero, @@ -160,6 +176,7 @@ export function getChainName(chainId: BigInt): string { if (chainId.equals(BigInt.fromI32(56))) return "BSC Mainnet" if (chainId.equals(BigInt.fromI32(143))) return "Monad" if (chainId.equals(BigInt.fromI32(137))) return "Polygon Mainnet" + if (chainId.equals(BigInt.fromI32(8453))) return "Base Mainnet" if (chainId.equals(BigInt.fromI32(11155111))) return "Ethereum Sepolia" if (chainId.equals(BigInt.fromI32(97))) return "BSC Testnet" if (chainId.equals(BigInt.fromI32(10143))) return "Monad Testnet" @@ -202,6 +219,7 @@ export function getSupportedChains(): BigInt[] { BigInt.fromI32(56), // BSC Mainnet BigInt.fromI32(143), // Monad BigInt.fromI32(137), // Polygon Mainnet + BigInt.fromI32(8453), // Base Mainnet BigInt.fromI32(11155111), // Ethereum Sepolia BigInt.fromI32(97), // BSC Testnet BigInt.fromI32(10143), // Monad Testnet diff --git a/src/identity-registry.ts b/src/identity-registry.ts index 81ce697..bc8a5a3 100644 --- a/src/identity-registry.ts +++ b/src/identity-registry.ts @@ -91,17 +91,20 @@ export function handleAgentRegistered(event: Registered): void { let b64 = extractBase64PayloadFromDataUri(event.params.agentURI) let decoded = base64DecodeToBytes(b64) - let registration = new AgentRegistrationFile(fileId) - registration.cid = `datauri:${txHash}:${event.logIndex.toString()}` - registration.agentId = agentEntityId - registration.createdAt = event.block.timestamp - registration.supportedTrusts = [] - registration.mcpTools = [] - registration.mcpPrompts = [] - registration.mcpResources = [] - registration.a2aSkills = [] - registration.oasfSkills = [] - registration.oasfDomains = [] + let registration = AgentRegistrationFile.load(fileId) + if (registration == null) { + registration = new AgentRegistrationFile(fileId) + registration.cid = `datauri:${txHash}:${event.logIndex.toString()}` + registration.agentId = agentEntityId + registration.createdAt = event.block.timestamp + registration.supportedTrusts = [] + registration.mcpTools = [] + registration.mcpPrompts = [] + registration.mcpResources = [] + registration.a2aSkills = [] + registration.oasfSkills = [] + registration.oasfDomains = [] + } populateRegistrationFromJsonBytes(registration, decoded) registration.save() @@ -211,17 +214,20 @@ export function handleUriUpdated(event: URIUpdated): void { let b64 = extractBase64PayloadFromDataUri(event.params.newURI) let decoded = base64DecodeToBytes(b64) - let registration = new AgentRegistrationFile(fileId) - registration.cid = `datauri:${txHash}:${event.logIndex.toString()}` - registration.agentId = agentEntityId - registration.createdAt = event.block.timestamp - registration.supportedTrusts = [] - registration.mcpTools = [] - registration.mcpPrompts = [] - registration.mcpResources = [] - registration.a2aSkills = [] - registration.oasfSkills = [] - registration.oasfDomains = [] + let registration = AgentRegistrationFile.load(fileId) + if (registration == null) { + registration = new AgentRegistrationFile(fileId) + registration.cid = `datauri:${txHash}:${event.logIndex.toString()}` + registration.agentId = agentEntityId + registration.createdAt = event.block.timestamp + registration.supportedTrusts = [] + registration.mcpTools = [] + registration.mcpPrompts = [] + registration.mcpResources = [] + registration.a2aSkills = [] + registration.oasfSkills = [] + registration.oasfDomains = [] + } populateRegistrationFromJsonBytes(registration, decoded) registration.save() diff --git a/src/registration-file.ts b/src/registration-file.ts index 866e950..437649f 100644 --- a/src/registration-file.ts +++ b/src/registration-file.ts @@ -8,25 +8,26 @@ export function parseRegistrationFile(content: Bytes): void { let cid = dataSource.stringParam() let txHash = context.getString('txHash') - // Create composite ID: transactionHash:cid let fileId = `${txHash}:${cid}` log.info("Parsing registration file for agent: {}, CID: {}, fileId: {}", [agentId, cid, fileId]) - // Create registration file with composite ID - let metadata = new AgentRegistrationFile(fileId) - metadata.cid = cid - metadata.agentId = agentId - metadata.createdAt = context.getBigInt('timestamp') - metadata.supportedTrusts = [] - metadata.mcpTools = [] - metadata.mcpPrompts = [] - metadata.mcpResources = [] - metadata.a2aSkills = [] - metadata.oasfSkills = [] - metadata.oasfDomains = [] - metadata.hasOASF = false - // New centralized parser (handles x402Support, new endpoints, endpointsRawJson) + let metadata = AgentRegistrationFile.load(fileId) + if (metadata == null) { + metadata = new AgentRegistrationFile(fileId) + metadata.cid = cid + metadata.agentId = agentId + metadata.createdAt = context.getBigInt('timestamp') + metadata.supportedTrusts = [] + metadata.mcpTools = [] + metadata.mcpPrompts = [] + metadata.mcpResources = [] + metadata.a2aSkills = [] + metadata.oasfSkills = [] + metadata.oasfDomains = [] + metadata.hasOASF = false + } + populateRegistrationFromJsonBytes(metadata, content) // Derived field for exact filtering: OASF skills OR domains present. diff --git a/src/utils/registration-parser.ts b/src/utils/registration-parser.ts index fb59cf0..0dbe50f 100644 --- a/src/utils/registration-parser.ts +++ b/src/utils/registration-parser.ts @@ -152,6 +152,7 @@ export function populateRegistrationFromJsonBytes(metadata: AgentRegistrationFil } // ERC-8004 uses x402Support (camelCase). Keep fallback to x402support. + // Value may be a plain boolean OR an object like {"enabled": false} β€” only accept booleans. let x402Support = obj.get("x402Support") if (x402Support == null || x402Support.isNull()) x402Support = obj.get("x402support") if (x402Support && !x402Support.isNull()) {