Publish Chain 138 RPC capability metadata

This commit is contained in:
defiQUG
2026-03-28 15:56:42 -07:00
parent 96a78eda33
commit 141c8a278e
6 changed files with 265 additions and 3 deletions

View File

@@ -52,6 +52,30 @@ type TokenListCatalog = {
tokens?: TokenListToken[]
}
type CapabilitiesCatalog = {
name?: string
chainId?: number
chainName?: string
rpcUrl?: string
explorerApiUrl?: string
generatedBy?: string
walletSupport?: {
walletAddEthereumChain?: boolean
walletWatchAsset?: boolean
notes?: string[]
}
http?: {
supportedMethods?: string[]
unsupportedMethods?: string[]
notes?: string[]
}
tracing?: {
supportedMethods?: string[]
unsupportedMethods?: string[]
notes?: string[]
}
}
type EthereumProvider = {
request: (args: { method: string; params?: unknown[] }) => Promise<unknown>
}
@@ -107,6 +131,7 @@ export function AddToMetaMask() {
const [error, setError] = useState<string | null>(null)
const [networks, setNetworks] = useState<NetworksCatalog | null>(null)
const [tokenList, setTokenList] = useState<TokenListCatalog | null>(null)
const [capabilities, setCapabilities] = useState<CapabilitiesCatalog | null>(null)
const ethereum = typeof window !== 'undefined'
? (window as unknown as { ethereum?: EthereumProvider }).ethereum
@@ -115,29 +140,34 @@ export function AddToMetaMask() {
const apiBase = getApiBase().replace(/\/$/, '')
const tokenListUrl = `${apiBase}/api/config/token-list`
const networksUrl = `${apiBase}/api/config/networks`
const capabilitiesUrl = `${apiBase}/api/config/capabilities`
useEffect(() => {
let active = true
async function loadCatalogs() {
try {
const [networksResponse, tokenListResponse] = await Promise.all([
const [networksResponse, tokenListResponse, capabilitiesResponse] = await Promise.all([
fetch(networksUrl),
fetch(tokenListUrl),
fetch(capabilitiesUrl),
])
const [networksJson, tokenListJson] = await Promise.all([
const [networksJson, tokenListJson, capabilitiesJson] = await Promise.all([
networksResponse.ok ? networksResponse.json() : null,
tokenListResponse.ok ? tokenListResponse.json() : null,
capabilitiesResponse.ok ? capabilitiesResponse.json() : null,
])
if (!active) return
setNetworks(networksJson)
setTokenList(tokenListJson)
setCapabilities(capabilitiesJson)
} catch {
if (!active) return
setNetworks(null)
setTokenList(null)
setCapabilities(null)
}
}
@@ -146,7 +176,7 @@ export function AddToMetaMask() {
return () => {
active = false
}
}, [networksUrl, tokenListUrl])
}, [capabilitiesUrl, networksUrl, tokenListUrl])
const chains = useMemo(() => {
const chainMap = new Map<number, WalletChain>()
@@ -246,6 +276,9 @@ export function AddToMetaMask() {
const tokenCount138 = (tokenList?.tokens || []).filter((token) => token.chainId === 138).length
const metadataKeywordString = (tokenList?.keywords || []).join(', ')
const supportedHTTPMethods = capabilities?.http?.supportedMethods || []
const unsupportedHTTPMethods = capabilities?.http?.unsupportedMethods || []
const supportedTraceMethods = capabilities?.tracing?.supportedMethods || []
return (
<div className="space-y-4 rounded-lg border border-gray-200 bg-white p-4 dark:border-gray-700 dark:bg-gray-800">
@@ -301,6 +334,18 @@ export function AddToMetaMask() {
</a>
</div>
</div>
<div>
<p className="mb-1 text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Capabilities URL</p>
<code className="block break-all rounded bg-gray-100 p-2 text-xs dark:bg-gray-900">{capabilitiesUrl}</code>
<div className="mt-2 flex flex-wrap gap-2">
<button type="button" onClick={() => copyText(capabilitiesUrl, 'capabilities URL')} className="rounded bg-gray-100 px-3 py-1.5 text-xs text-gray-700 hover:bg-gray-200 dark:bg-gray-900 dark:text-gray-200 dark:hover:bg-gray-700">
Copy URL
</button>
<a href={capabilitiesUrl} target="_blank" rel="noopener noreferrer" className="rounded bg-gray-100 px-3 py-1.5 text-xs text-gray-700 hover:bg-gray-200 dark:bg-gray-900 dark:text-gray-200 dark:hover:bg-gray-700">
Open JSON
</a>
</div>
</div>
<div>
<p className="mb-1 text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Token list URL</p>
<code className="block break-all rounded bg-gray-100 p-2 text-xs dark:bg-gray-900">{tokenListUrl}</code>
@@ -316,6 +361,42 @@ export function AddToMetaMask() {
</div>
</div>
<div className="rounded-lg border border-gray-200 p-4 dark:border-gray-700">
<div className="text-sm font-semibold text-gray-900 dark:text-white">Chain 138 RPC capabilities</div>
<p className="mt-2 text-sm text-gray-600 dark:text-gray-400">
This capability matrix documents what public RPC methods wallets can rely on today, what tracing the explorer
can use, and where MetaMask will still fall back because the public node does not expose every optional fee method.
</p>
<div className="mt-4 space-y-3 text-sm text-gray-600 dark:text-gray-400">
<p>
RPC endpoint:{' '}
<span className="font-medium text-gray-900 dark:text-white">
{capabilities?.rpcUrl || 'using published explorer fallback'}
</span>
</p>
<p>
HTTP methods: {supportedHTTPMethods.length > 0 ? supportedHTTPMethods.join(', ') : 'metadata unavailable'}
</p>
<p>
Missing wallet-facing methods:{' '}
{unsupportedHTTPMethods.length > 0 ? unsupportedHTTPMethods.join(', ') : 'none listed'}
</p>
<p>
Trace methods: {supportedTraceMethods.length > 0 ? supportedTraceMethods.join(', ') : 'metadata unavailable'}
</p>
{capabilities?.walletSupport?.notes?.map((note) => (
<p key={note} className="text-xs">
{note}
</p>
))}
{capabilities?.http?.notes?.map((note) => (
<p key={note} className="text-xs">
{note}
</p>
))}
</div>
</div>
<div className="rounded-lg border border-gray-200 p-4 dark:border-gray-700">
<div className="text-sm font-semibold text-gray-900 dark:text-white">Featured Chain 138 tokens</div>
<p className="mt-2 text-sm text-gray-600 dark:text-gray-400">