2020 * - Uploads reports as artifacts
2121 *
2222 * ============================================================================
23- * VERIFICATION CRITERIA (7 Checks)
23+ * VERIFICATION CRITERIA (8 Checks)
2424 * ============================================================================
2525 *
2626 * 1. STANDARD LISTING
116116 * - Pass: Either side has usd_amount >= $50 at 2% depth
117117 * - Fail: Both sides < $50, or no pool found
118118 *
119+ * 8. CHAIN STATUS
120+ * - Verifies the asset's chain is not marked as "killed"
121+ * - Check: chain_reg.getFileProperty(chainName, 'chain', 'status') !== 'killed'
122+ * - Exemption: Meme tokens (category includes "meme") skip this check
123+ * - Pass: Chain status is "live" or "upcoming" OR asset is a meme token
124+ * - Fail: Chain status is "killed" (unless meme token)
125+ * - Purpose: Prevents verification of assets on killed/deprecated chains
126+ * while allowing historical meme tokens to remain verified
127+ *
119128 * ============================================================================
120129 * ALLOY AUTO-VERIFICATION
121130 * ============================================================================
@@ -521,6 +530,22 @@ function isMemeToken(zoneAsset) {
521530 return hasCategories ( zoneAsset , [ 'meme' ] ) ;
522531}
523532
533+ /**
534+ * Check if a chain is marked as killed in the chain registry
535+ *
536+ * @param {string } chainName - Chain name to check
537+ * @returns {boolean } True if chain is killed, false otherwise
538+ */
539+ function isChainKilled ( chainName ) {
540+ try {
541+ const chainStatus = chain_reg . getFileProperty ( chainName , 'chain' , 'status' ) ;
542+ return chainStatus === 'killed' ;
543+ } catch ( error ) {
544+ // If we can't read the chain status, assume it's not killed
545+ return false ;
546+ }
547+ }
548+
524549//-- Verification Check Functions --
525550
526551/**
@@ -1112,6 +1137,53 @@ async function checkBidDepth(chainName, baseDenom, numiaPairs) {
11121137 }
11131138}
11141139
1140+ /**
1141+ * Check 8: Killed Chain Status
1142+ * Verifies that the asset's chain is not marked as "killed" in the chain registry
1143+ *
1144+ * Assets on killed chains should not be verified, and already-verified assets apart from memes
1145+ * should be de-verified.
1146+ *
1147+ * Exemption: Meme tokens (category includes "meme") are allowed to remain verified
1148+ * even on killed chains, as they may have historical/cultural value.
1149+ *
1150+ * Per LISTING.md chain status requirement
1151+ *
1152+ * @param {string } chainName - Chain name to check
1153+ * @param {boolean } isMeme - Whether the asset is a meme token
1154+ */
1155+ async function checkChainStatus ( chainName , isMeme ) {
1156+ try {
1157+ // Exemption: Meme tokens can remain verified on killed chains
1158+ if ( isMeme ) {
1159+ return {
1160+ passed : true ,
1161+ details : 'Meme category: exempt from chain status check' ,
1162+ skipped : true
1163+ } ;
1164+ }
1165+
1166+ const chainIsKilled = isChainKilled ( chainName ) ;
1167+
1168+ if ( chainIsKilled ) {
1169+ return {
1170+ passed : false ,
1171+ details : `Chain "${ chainName } " is marked as killed in the chain registry`
1172+ } ;
1173+ }
1174+
1175+ return {
1176+ passed : true ,
1177+ details : 'Chain is active'
1178+ } ;
1179+ } catch ( error ) {
1180+ return {
1181+ passed : true , // Assume chain is active if we can't check
1182+ details : `Could not verify chain status: ${ error . message } `
1183+ } ;
1184+ }
1185+ }
1186+
11151187//-- Main Verification Function --
11161188
11171189/**
@@ -1134,6 +1206,7 @@ async function verifyAsset(zoneAsset, numiaTokens, numiaPairs, alloyMembersMap)
11341206 comment : zoneAsset . _comment || '' ,
11351207 currently_verified : zoneAsset . osmosis_verified || false ,
11361208 is_meme : isMeme ,
1209+ chainIsKilled : isChainKilled ( chain_name ) ,
11371210 checks : { } ,
11381211 alloyInfo : null
11391212 } ;
@@ -1153,6 +1226,7 @@ async function verifyAsset(zoneAsset, numiaTokens, numiaPairs, alloyMembersMap)
11531226 results . checks . logo = await checkLogo ( chain_name , base_denom ) ;
11541227 results . checks . poolLiquidity = await checkPoolLiquidity ( chain_name , base_denom , numiaTokens ) ;
11551228 results . checks . bidDepth = await checkBidDepth ( chain_name , base_denom , numiaPairs ) ;
1229+ results . checks . chainStatus = await checkChainStatus ( chain_name , isMeme ) ;
11561230
11571231 // Determine overall pass/fail
11581232 results . allChecksPassed = Object . values ( results . checks ) . every ( check => check . passed ) ;
@@ -1179,9 +1253,13 @@ function generateMarkdownReport(verificationResults) {
11791253 const alreadyVerified = verificationResults . filter ( r => r . currently_verified ) ;
11801254 const failedChecks = verificationResults . filter ( r => ! r . allChecksPassed && ! r . currently_verified ) ;
11811255
1256+ // Filter killed chain assets (excluding meme tokens) - only verified assets that need de-verification
1257+ const killedChainAssetsVerified = verificationResults . filter ( r => r . chainIsKilled && r . currently_verified && ! r . is_meme ) ;
1258+
11821259 markdown += `## Summary\n\n` ;
11831260 markdown += `- **Ready for Verification**: ${ readyForVerification . length } \n` ;
11841261 markdown += `- **Failed Checks**: ${ failedChecks . length } \n` ;
1262+ markdown += `- **Killed Chain Assets**: ${ killedChainAssetsVerified . length } (potential de-verification)\n` ;
11851263 markdown += `- **Total Checked**: ${ verificationResults . length } \n\n` ;
11861264
11871265 // Ready for Verification section with asset links
@@ -1283,35 +1361,39 @@ function generateMarkdownReport(verificationResults) {
12831361
12841362 if ( highLiquidityFailing . length > 0 ) {
12851363 markdown += `These assets have sufficient pool liquidity ($1000+) but fail other checks:\n\n` ;
1286- markdown += `| Asset | Comment | Pool Liquidity | Bid Depth | Other Failures |\n` ;
1287- markdown += `|-------|---------|----------------|-----------|----------- -----|\n` ;
1364+ markdown += `| Asset | Comment | Pool Liquidity | Failure Reasons |\n` ;
1365+ markdown += `|-------|---------|----------------|----------------|\n` ;
12881366
12891367 highLiquidityFailing . forEach ( r => {
12901368 const symbol = r . comment || r . base_denom . substring ( 0 , 30 ) ;
12911369 // Extract just the liquidity amount from the details string
12921370 const liqMatch = r . checks . poolLiquidity ?. details ?. match ( / \$ [ \d , ] + / ) ;
12931371 const poolLiq = liqMatch ? liqMatch [ 0 ] : 'N/A' ;
12941372
1295- // Format bid depth
1296- let bidDepth = '' ;
1297- if ( r . checks . bidDepth ?. passed ) {
1298- bidDepth = '✅ ' + r . checks . bidDepth . details ;
1299- } else {
1300- const details = r . checks . bidDepth ?. details || 'Failed ' ;
1373+ // Collect all failure reasons
1374+ const failureReasons = [ ] ;
1375+
1376+ // Add bid depth if it failed
1377+ if ( ! r . checks . bidDepth ?. passed ) {
1378+ const details = r . checks . bidDepth ?. details || 'Bid depth failed ' ;
13011379 const cleanDetails = details
13021380 . replace ( / ❌ / g, '' )
13031381 . replace ( / ✅ / g, '' )
13041382 . replace ( / \( n e e d \$ \d + \) / g, '' )
13051383 . trim ( ) ;
1306- bidDepth = '❌ ' + cleanDetails ;
1384+ failureReasons . push ( `Bid depth: ${ cleanDetails } ` ) ;
13071385 }
13081386
1387+ // Add other failures
13091388 const otherFails = Object . entries ( r . checks )
13101389 . filter ( ( [ name , check ] ) => ! check . passed && ! check . skipped && name !== 'poolLiquidity' && name !== 'bidDepth' )
1311- . map ( ( [ name ] ) => name . replace ( / ( [ A - Z ] ) / g, ' $1' ) . trim ( ) )
1312- . join ( ', ' ) || 'None' ;
1390+ . map ( ( [ name ] ) => name . replace ( / ( [ A - Z ] ) / g, ' $1' ) . trim ( ) ) ;
1391+
1392+ failureReasons . push ( ...otherFails ) ;
1393+
1394+ const failureReasonsText = failureReasons . length > 0 ? failureReasons . join ( ', ' ) : 'None' ;
13131395
1314- markdown += `| ${ symbol } | ${ r . chain_name } | ${ poolLiq } | ${ bidDepth } | ${ otherFails } |\n` ;
1396+ markdown += `| ${ symbol } | ${ r . chain_name } | ${ poolLiq } | ${ failureReasonsText } |\n` ;
13151397 } ) ;
13161398 markdown += '\n' ;
13171399 } else {
@@ -1398,6 +1480,24 @@ function generateMarkdownReport(verificationResults) {
13981480 } else {
13991481 markdown += `No logo failures detected. All assets have valid logos that meet the requirements.\n\n` ;
14001482 }
1483+
1484+ // Killed Chain Assets subsection
1485+ markdown += `### Killed Chain Assets\n\n` ;
1486+
1487+ if ( killedChainAssetsVerified . length > 0 ) {
1488+ markdown += `These verified assets belong to killed chains and can be de-verified (excluding meme tokens):\n\n` ;
1489+ markdown += `| Asset | Chain | Base Denom | Comment |\n` ;
1490+ markdown += `|-------|-------|------------|----------|\n` ;
1491+
1492+ killedChainAssetsVerified . forEach ( r => {
1493+ const symbol = r . comment || r . base_denom . substring ( 0 , 40 ) ;
1494+ markdown += `| ${ symbol } | ${ r . chain_name } | \`${ r . base_denom } \` | ${ r . comment || 'N/A' } |\n` ;
1495+ } ) ;
1496+ markdown += '\n' ;
1497+ markdown += `**Note:** Meme tokens are exempt from this requirement and may remain verified on killed chains due to their historical/cultural value.\n\n` ;
1498+ } else {
1499+ markdown += `No verified assets from killed chains detected. All verified assets belong to active chains.\n\n` ;
1500+ }
14011501 } else {
14021502 markdown += `All assets pass verification checks!\n\n` ;
14031503 }
@@ -1429,6 +1529,9 @@ function generateMarkdownReport(verificationResults) {
14291529function generateJSONReport ( verificationResults ) {
14301530 const failedChecks = verificationResults . filter ( r => ! r . allChecksPassed && ! r . currently_verified ) ;
14311531
1532+ // Filter killed chain assets (excluding meme tokens) - only verified assets that may need de-verification
1533+ const killedChainAssetsVerified = verificationResults . filter ( r => r . chainIsKilled && r . currently_verified && ! r . is_meme ) ;
1534+
14321535 // Calculate failure breakdown
14331536 const failureCounts = {
14341537 standardListing : 0 ,
@@ -1437,7 +1540,8 @@ function generateJSONReport(verificationResults) {
14371540 socials : 0 ,
14381541 logo : 0 ,
14391542 poolLiquidity : 0 ,
1440- bidDepth : 0
1543+ bidDepth : 0 ,
1544+ chainStatus : 0
14411545 } ;
14421546
14431547 const socialsFailureReasons = { } ;
@@ -1488,12 +1592,20 @@ function generateJSONReport(verificationResults) {
14881592 readyForVerification : verificationResults . filter ( r => r . readyForVerification ) . length ,
14891593 alreadyVerified : verificationResults . filter ( r => r . currently_verified ) . length ,
14901594 failedChecks : failedChecks . length ,
1595+ killedChainAssets : killedChainAssetsVerified . length ,
14911596 totalChecked : verificationResults . length
14921597 } ,
14931598 analysis : {
14941599 failureBreakdown,
14951600 socialsFailureReasons,
1496- highLiquidityFailing
1601+ highLiquidityFailing,
1602+ killedChainAssets : killedChainAssetsVerified . map ( r => ( {
1603+ chain_name : r . chain_name ,
1604+ base_denom : r . base_denom ,
1605+ comment : r . comment ,
1606+ is_meme : r . is_meme ,
1607+ chainIsKilled : r . chainIsKilled
1608+ } ) )
14971609 } ,
14981610 results : verificationResults
14991611 } , null , 2 ) ;
0 commit comments