feat: explorer API, wallet, CCIP scripts, and config refresh

- Backend REST/gateway/track routes, analytics, Blockscout proxy paths.
- Frontend wallet and liquidity surfaces; MetaMask token list alignment.
- Deployment docs, verification scripts, address inventory updates.

Check: go build ./... under backend/ (pass).
Made-with: Cursor
This commit is contained in:
defiQUG
2026-04-07 23:22:12 -07:00
parent d931be8e19
commit 6eef6b07f6
224 changed files with 19671 additions and 3291 deletions

View File

@@ -55,9 +55,16 @@ type TokenListCatalog = {
type CapabilitiesCatalog = {
name?: string
version?: {
major?: number
minor?: number
patch?: number
}
timestamp?: string
chainId?: number
chainName?: string
rpcUrl?: string
explorerUrl?: string
explorerApiUrl?: string
generatedBy?: string
walletSupport?: {
@@ -128,6 +135,80 @@ const FEATURED_TOKEN_SYMBOLS = ['cUSDT', 'cUSDC', 'USDT', 'USDC', 'cXAUC', 'cXAU
/** npm-published Snap using open Snap permissions only; stable MetaMask still requires MetaMasks install allowlist. */
const CHAIN138_OPEN_SNAP_ID = 'npm:chain138-open-snap' as const
const FALLBACK_CAPABILITIES_138: CapabilitiesCatalog = {
name: 'Chain 138 RPC Capabilities',
version: { major: 1, minor: 1, patch: 0 },
timestamp: '2026-03-28T00:00:00Z',
generatedBy: 'SolaceScanScout',
chainId: 138,
chainName: 'DeFi Oracle Meta Mainnet',
rpcUrl: 'https://rpc-http-pub.d-bis.org',
explorerUrl: 'https://explorer.d-bis.org',
explorerApiUrl: 'https://explorer.d-bis.org/api/v2',
walletSupport: {
walletAddEthereumChain: true,
walletWatchAsset: true,
notes: [
'MetaMask primarily relies on JSON-RPC correctness for balances, gas estimation, calls, and transaction submission.',
'Explorer-served network metadata and token list metadata complement wallet UX but do not replace RPC method support.',
],
},
http: {
supportedMethods: [
'web3_clientVersion',
'net_version',
'eth_chainId',
'eth_blockNumber',
'eth_syncing',
'eth_gasPrice',
'eth_maxPriorityFeePerGas',
'eth_feeHistory',
'eth_estimateGas',
'eth_getCode',
],
unsupportedMethods: [],
notes: [
'eth_feeHistory is available for wallet fee estimation.',
'eth_maxPriorityFeePerGas is exposed on the public RPC for wallet-grade fee suggestion compatibility.',
],
},
tracing: {
supportedMethods: ['trace_block', 'trace_replayBlockTransactions'],
unsupportedMethods: ['debug_traceBlockByNumber'],
notes: [
'TRACE support is enabled for explorer-grade indexing and internal transaction analysis.',
'Debug tracing is intentionally not enabled on the public RPC tier.',
],
},
}
function isTokenListToken(value: unknown): value is TokenListToken {
if (!value || typeof value !== 'object') return false
const candidate = value as Partial<TokenListToken>
return (
typeof candidate.chainId === 'number' &&
typeof candidate.address === 'string' &&
candidate.address.trim().length > 0 &&
typeof candidate.name === 'string' &&
typeof candidate.symbol === 'string' &&
typeof candidate.decimals === 'number'
)
}
function isCapabilitiesCatalog(value: unknown): value is CapabilitiesCatalog {
if (!value || typeof value !== 'object') return false
const candidate = value as Partial<CapabilitiesCatalog>
return (
typeof candidate.chainId === 'number' &&
typeof candidate.chainName === 'string' &&
candidate.chainName.trim().length > 0 &&
typeof candidate.rpcUrl === 'string' &&
candidate.rpcUrl.trim().length > 0
)
}
function getApiBase() {
return resolveExplorerApiBase({
serverFallback: 'https://explorer.d-bis.org',
@@ -152,6 +233,10 @@ export function AddToMetaMask() {
const tokenListUrl = `${apiBase}/api/config/token-list`
const networksUrl = `${apiBase}/api/config/networks`
const capabilitiesUrl = `${apiBase}/api/config/capabilities`
const staticCapabilitiesUrl =
typeof window !== 'undefined'
? `${window.location.origin.replace(/\/$/, '')}/config/CHAIN138_RPC_CAPABILITIES.json`
: `${apiBase}/config/CHAIN138_RPC_CAPABILITIES.json`
useEffect(() => {
let active = true
@@ -180,21 +265,46 @@ export function AddToMetaMask() {
fetchJson(capabilitiesUrl),
])
let resolvedCapabilities = capabilitiesResponse
if (!isCapabilitiesCatalog(resolvedCapabilities.json)) {
const staticCapabilitiesResponse = await fetchJson(staticCapabilitiesUrl)
if (isCapabilitiesCatalog(staticCapabilitiesResponse.json)) {
resolvedCapabilities = {
json: staticCapabilitiesResponse.json,
meta: {
source: staticCapabilitiesResponse.meta.source || 'public-static-fallback',
lastModified: staticCapabilitiesResponse.meta.lastModified,
},
}
} else {
resolvedCapabilities = {
json: FALLBACK_CAPABILITIES_138,
meta: {
source: 'frontend-fallback',
lastModified: FALLBACK_CAPABILITIES_138.timestamp || null,
},
}
}
}
if (!active) return
setNetworks(networksResponse.json)
setTokenList(tokenListResponse.json)
setCapabilities(capabilitiesResponse.json)
setCapabilities(resolvedCapabilities.json)
setNetworksMeta(networksResponse.meta)
setTokenListMeta(tokenListResponse.meta)
setCapabilitiesMeta(capabilitiesResponse.meta)
setCapabilitiesMeta(resolvedCapabilities.meta)
} catch {
if (!active) return
setNetworks(null)
setTokenList(null)
setCapabilities(null)
setCapabilities(FALLBACK_CAPABILITIES_138)
setNetworksMeta(null)
setTokenListMeta(null)
setCapabilitiesMeta(null)
setCapabilitiesMeta({
source: 'frontend-fallback',
lastModified: FALLBACK_CAPABILITIES_138.timestamp || null,
})
} finally {
if (active) {
timer = setTimeout(() => {
@@ -210,7 +320,12 @@ export function AddToMetaMask() {
active = false
if (timer) clearTimeout(timer)
}
}, [capabilitiesUrl, networksUrl, tokenListUrl])
}, [capabilitiesUrl, networksUrl, staticCapabilitiesUrl, tokenListUrl])
const catalogTokens = useMemo(
() => (Array.isArray(tokenList?.tokens) ? tokenList.tokens.filter(isTokenListToken) : []),
[tokenList],
)
const chains = useMemo(() => {
const chainMap = new Map<number, WalletChain>()
@@ -230,7 +345,7 @@ export function AddToMetaMask() {
const featuredTokens = useMemo(() => {
const tokenMap = new Map<string, TokenListToken>()
for (const token of tokenList?.tokens || []) {
for (const token of catalogTokens) {
if (token.chainId !== 138) continue
if (!FEATURED_TOKEN_SYMBOLS.includes(token.symbol)) continue
tokenMap.set(token.symbol, token)
@@ -239,7 +354,7 @@ export function AddToMetaMask() {
return FEATURED_TOKEN_SYMBOLS
.map((symbol) => tokenMap.get(symbol))
.filter((token): token is TokenListToken => !!token)
}, [tokenList])
}, [catalogTokens])
const addChain = async (chain: WalletChain) => {
setError(null)
@@ -304,6 +419,11 @@ export function AddToMetaMask() {
setError(null)
setStatus(null)
if (!isTokenListToken(token)) {
setError('Token metadata is incomplete right now. Refresh the page and try again.')
return
}
if (!ethereum) {
setError('MetaMask or another Web3 wallet is not installed.')
return
@@ -312,7 +432,7 @@ export function AddToMetaMask() {
try {
const added = await ethereum.request({
method: 'wallet_watchAsset',
params: [{
params: {
type: 'ERC20',
options: {
address: token.address,
@@ -320,7 +440,7 @@ export function AddToMetaMask() {
decimals: token.decimals,
image: token.logoURI,
},
}],
},
})
setStatus(added ? `Added ${token.symbol} to your wallet.` : `${token.symbol} request was dismissed.`)
@@ -342,11 +462,15 @@ export function AddToMetaMask() {
}
}
const tokenCount138 = (tokenList?.tokens || []).filter((token) => token.chainId === 138).length
const tokenCount138 = catalogTokens.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 || []
const displayedCapabilitiesUrl =
capabilitiesMeta?.source === 'public-static-fallback' || capabilitiesMeta?.source === 'frontend-fallback'
? staticCapabilitiesUrl
: capabilitiesUrl
return (
<div className="space-y-4 rounded-xl border border-gray-200 bg-white p-4 dark:border-gray-700 dark:bg-gray-800 sm:p-5">
@@ -432,12 +556,12 @@ export function AddToMetaMask() {
</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>
<code className="block break-all rounded bg-gray-100 p-2 text-xs dark:bg-gray-900">{displayedCapabilitiesUrl}</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">
<button type="button" onClick={() => copyText(displayedCapabilitiesUrl, '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">
<a href={displayedCapabilitiesUrl} 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>