@@ -184,7 +184,7 @@ describe('registry.ts', () => {
184184 jest . mocked ( provider . readContract ) . mockResolvedValue ( {
185185 deployUrl : 'ipfs://QmV1kMdjDegcKrvSddsTmRGyCwnYERqN9o1K56g4Mw7F6i' ,
186186 metaUrl : 'ipfs://QmV1kMdjDegcKrvSddsTmRGyCwnYERqN9o1K56g4Mw7F6j' ,
187- mutability : 'foobar' ,
187+ mutability : viem . stringToHex ( 'foobar' , { size : 16 } ) ,
188188 owner : signer . address ,
189189 } ) ;
190190
@@ -202,6 +202,32 @@ describe('registry.ts', () => {
202202 ] ,
203203 } ) ;
204204 } ) ;
205+
206+ it ( 'decodes bytes16 mutability field from on-chain hex to string' , async ( ) => {
207+ const provider = makeFakeProvider ( ) ;
208+ const registry = createRegistry ( { provider } ) ;
209+
210+ // The on-chain registry stores mutability as bytes16 (right-padded hex).
211+ // Before the fix, the raw hex string was returned instead of the decoded value,
212+ // causing mutability checks like `=== 'version'` to always fail.
213+ // Note: empty mutability is not tested here because stringToHex('', {size:16}) produces
214+ // 16 null bytes, which hexToString decodes to '\u0000' rather than ''. In practice,
215+ // the on-chain contract only uses 'version' and 'tag' as meaningful mutability values.
216+ for ( const mutability of [ 'version' , 'tag' ] as const ) {
217+ jest . mocked ( provider . readContract ) . mockResolvedValue ( {
218+ deployUrl : 'ipfs://QmV1kMdjDegcKrvSddsTmRGyCwnYERqN9o1K56g4Mw7F6i' ,
219+ metaUrl : '' ,
220+ mutability : viem . stringToHex ( mutability , { size : 16 } ) ,
221+ owner : signer . address ,
222+ } ) ;
223+
224+ const url = await registry . getUrl ( 'dummy-package:0.0.1@main' , 13370 ) ;
225+
226+ expect ( url . mutability ) . toBe ( mutability ) ;
227+ // Make sure we never leak the raw hex value
228+ expect ( url . mutability ) . not . toMatch ( / ^ 0 x / ) ;
229+ }
230+ } ) ;
205231 } ) ;
206232 } ) ;
207233} ) ;
0 commit comments