@@ -29,6 +29,11 @@ const CANONICAL_BRIDGES = new Set([
2929 '0x0CA3A2FBC3D770b578223FBB6b062fa875a2eE75' ,
3030 '0x7f82f57F0Dd546519324392e408b01fcC7D709e8' ,
3131] )
32+ // Lowercase mirror used for case-insensitive runtime lookups.
33+ // The original set is kept in EIP-55 checksummed form for documentation purposes.
34+ const CANONICAL_BRIDGES_LOWER = new Set (
35+ Array . from ( CANONICAL_BRIDGES ) . map ( ( a ) => a . toLowerCase ( ) )
36+ )
3237
3338function getLogoExtension ( tokenDir : string ) : string | null {
3439 const svgPath = path . join ( tokenDir , 'logo.svg' )
@@ -108,6 +113,44 @@ function inferMechanism(
108113 return 'unknown'
109114}
110115
116+ /**
117+ * Compute the next semantic version for a token list by diffing the previous
118+ * token set (keyed by chainId+address) against the new one.
119+ *
120+ * Rules (following Uniswap Token List spec):
121+ * - removed tokens → major bump
122+ * - added tokens → minor bump
123+ * - only metadata → patch bump
124+ */
125+ function computeNextVersion (
126+ previousVersion : { major : number ; minor : number ; patch : number } ,
127+ prevTokens : TokenListToken [ ] ,
128+ currentTokens : TokenListToken [ ]
129+ ) : { major : number ; minor : number ; patch : number } {
130+ const key = ( t : TokenListToken ) => `${ t . chainId } :${ t . address . toLowerCase ( ) } `
131+ const prevKeys = new Set ( prevTokens . map ( key ) )
132+ const currKeys = new Set ( currentTokens . map ( key ) )
133+
134+ const removed = [ ...prevKeys ] . some ( ( k ) => ! currKeys . has ( k ) )
135+ const added = [ ...currKeys ] . some ( ( k ) => ! prevKeys . has ( k ) )
136+
137+ if ( removed ) {
138+ return { major : previousVersion . major + 1 , minor : 0 , patch : 0 }
139+ }
140+ if ( added ) {
141+ return {
142+ major : previousVersion . major ,
143+ minor : previousVersion . minor + 1 ,
144+ patch : 0 ,
145+ }
146+ }
147+ return {
148+ major : previousVersion . major ,
149+ minor : previousVersion . minor ,
150+ patch : previousVersion . patch + 1 ,
151+ }
152+ }
153+
111154export function generate ( target : TokenListTarget = 'mainnet' ) : TokenList {
112155 // Read all token directories
113156 const tokenDirs = fs
@@ -161,7 +204,9 @@ export function generate(target: TokenListTarget = 'mainnet'): TokenList {
161204 // Add bridge address for this chain
162205 if ( chainToken . bridge ) {
163206 extensions . bridgeAddress = chainToken . bridge
164- extensions . bridgeType = CANONICAL_BRIDGES . has ( chainToken . bridge )
207+ extensions . bridgeType = CANONICAL_BRIDGES_LOWER . has (
208+ chainToken . bridge . toLowerCase ( )
209+ )
165210 ? 'canonical'
166211 : 'others'
167212 }
@@ -195,17 +240,28 @@ export function generate(target: TokenListTarget = 'mainnet'): TokenList {
195240 return a . symbol . localeCompare ( b . symbol )
196241 } )
197242
243+ // Compute version by diffing against the previously written output file.
244+ const outputFile = OUTPUT_FILES [ target ]
245+ let previousVersion = { major : 1 , minor : 0 , patch : 0 }
246+ let prevTokens : TokenListToken [ ] = [ ]
247+ if ( fs . existsSync ( outputFile ) ) {
248+ try {
249+ const prev = JSON . parse ( fs . readFileSync ( outputFile , 'utf-8' ) ) as TokenList
250+ previousVersion = prev . version
251+ prevTokens = prev . tokens
252+ } catch {
253+ // Corrupt or unreadable previous file — treat as first run.
254+ }
255+ }
256+ const version = computeNextVersion ( previousVersion , prevTokens , tokens )
257+
198258 const tokenList : TokenList = {
199259 name :
200260 target === 'mainnet'
201261 ? 'MegaETH Token List'
202262 : 'MegaETH Testnet Token List' ,
203263 timestamp : new Date ( ) . toISOString ( ) ,
204- version : {
205- major : 1 ,
206- minor : 0 ,
207- patch : 0 ,
208- } ,
264+ version,
209265 tokens,
210266 }
211267
0 commit comments