@@ -1247,10 +1247,188 @@ describe('EvmWarpRouteReader', async () => {
12471247 } ) ;
12481248 }
12491249
1250- // Note: legacy contract fetchScale path (< 11.0.0) cannot be tested in hardhat
1251- // without deploying a real legacy contract. The legacy path converts a single
1252- // uint256 scale() return to { numerator: bigint, denominator: 1n }.
1253- // Coverage is provided by the fetchScale stubs in other tests returning NormalizedScale.
1250+ // Note: legacy contract fetchScale path (< 11.0.0) is tested via hardhat_setCode
1251+ // to inject minimal bytecode that responds to the scale() selector.
1252+ // The legacy path converts a single uint256 scale() return to { numerator: bigint, denominator: 1n }.
1253+
1254+ describe ( 'fetchScale' , ( ) => {
1255+ it ( 'should return undefined for contracts before scaling was introduced (< 6.0.0)' , async ( ) => {
1256+ const config : WarpRouteDeployConfigMailboxRequired = {
1257+ [ chain ] : {
1258+ type : TokenType . synthetic ,
1259+ name : TOKEN_NAME ,
1260+ symbol : TOKEN_NAME ,
1261+ decimals : TOKEN_DECIMALS ,
1262+ hook : await mailbox . defaultHook ( ) ,
1263+ ...baseConfig ,
1264+ } ,
1265+ } ;
1266+
1267+ const warpRoute = await deployer . deploy ( config ) ;
1268+ const warpAddress = warpRoute [ chain ] . synthetic . address ;
1269+
1270+ const fetchPackageVersionStub = sinon
1271+ . stub ( evmERC20WarpRouteReader , 'fetchPackageVersion' )
1272+ . resolves ( '5.0.0' ) ;
1273+
1274+ const result = await evmERC20WarpRouteReader . fetchScale ( warpAddress ) ;
1275+ expect ( result ) . to . be . undefined ;
1276+
1277+ fetchPackageVersionStub . restore ( ) ;
1278+ } ) ;
1279+
1280+ it ( 'should read legacy scale() for contracts with version >= 6.0.0 and < 11.0.0' , async ( ) => {
1281+ const expectedScale = 1000n ;
1282+
1283+ // Deploy a minimal contract that returns expectedScale for scale()
1284+ // Bytecode: PUSH32 <value> PUSH1 0x00 MSTORE PUSH1 0x20 PUSH1 0x00 RETURN
1285+ const encodedScale = expectedScale . toString ( 16 ) . padStart ( 64 , '0' ) ;
1286+ const runtimeBytecode = `0x7f${ encodedScale } 60005260206000f3` ;
1287+ const mockAddress = '0x' + 'ab' . repeat ( 20 ) ;
1288+ await hre . network . provider . send ( 'hardhat_setCode' , [
1289+ mockAddress ,
1290+ runtimeBytecode ,
1291+ ] ) ;
1292+
1293+ const fetchPackageVersionStub = sinon
1294+ . stub ( evmERC20WarpRouteReader , 'fetchPackageVersion' )
1295+ . resolves ( '9.0.0' ) ;
1296+
1297+ const result = await evmERC20WarpRouteReader . fetchScale ( mockAddress ) ;
1298+ expect ( result ) . to . deep . equal ( {
1299+ numerator : expectedScale ,
1300+ denominator : 1n ,
1301+ } ) ;
1302+
1303+ fetchPackageVersionStub . restore ( ) ;
1304+ } ) ;
1305+
1306+ it ( 'should read scaleNumerator/scaleDenominator for contracts >= 11.0.0' , async ( ) => {
1307+ const scaleNumerator = 1 ;
1308+ const scaleDenominator = 1000000000000 ;
1309+ const config : WarpRouteDeployConfigMailboxRequired = {
1310+ [ chain ] : {
1311+ type : TokenType . synthetic ,
1312+ name : TOKEN_NAME ,
1313+ symbol : TOKEN_NAME ,
1314+ decimals : 6 ,
1315+ scale : {
1316+ numerator : scaleNumerator ,
1317+ denominator : scaleDenominator ,
1318+ } ,
1319+ hook : await mailbox . defaultHook ( ) ,
1320+ ...baseConfig ,
1321+ } ,
1322+ } ;
1323+
1324+ const warpRoute = await deployer . deploy ( config ) ;
1325+ const warpAddress = warpRoute [ chain ] . synthetic . address ;
1326+
1327+ const result = await evmERC20WarpRouteReader . fetchScale ( warpAddress ) ;
1328+ expect ( result ) . to . deep . equal ( {
1329+ numerator : BigInt ( scaleNumerator ) ,
1330+ denominator : BigInt ( scaleDenominator ) ,
1331+ } ) ;
1332+ } ) ;
1333+
1334+ it ( 'should read legacy scale() at the exact boundary version 6.0.0' , async ( ) => {
1335+ const expectedScale = 500n ;
1336+
1337+ const encodedScale = expectedScale . toString ( 16 ) . padStart ( 64 , '0' ) ;
1338+ const runtimeBytecode = `0x7f${ encodedScale } 60005260206000f3` ;
1339+ const mockAddress = '0x' + 'ac' . repeat ( 20 ) ;
1340+ await hre . network . provider . send ( 'hardhat_setCode' , [
1341+ mockAddress ,
1342+ runtimeBytecode ,
1343+ ] ) ;
1344+
1345+ const fetchPackageVersionStub = sinon
1346+ . stub ( evmERC20WarpRouteReader , 'fetchPackageVersion' )
1347+ . resolves ( '6.0.0' ) ;
1348+
1349+ const result = await evmERC20WarpRouteReader . fetchScale ( mockAddress ) ;
1350+ expect ( result ) . to . deep . equal ( {
1351+ numerator : expectedScale ,
1352+ denominator : 1n ,
1353+ } ) ;
1354+
1355+ fetchPackageVersionStub . restore ( ) ;
1356+ } ) ;
1357+
1358+ it ( 'should read scaleNumerator/scaleDenominator at the exact boundary version 11.0.0' , async ( ) => {
1359+ const scaleNumerator = 1 ;
1360+ const scaleDenominator = 1000000000000 ;
1361+ const config : WarpRouteDeployConfigMailboxRequired = {
1362+ [ chain ] : {
1363+ type : TokenType . synthetic ,
1364+ name : TOKEN_NAME ,
1365+ symbol : TOKEN_NAME ,
1366+ decimals : 6 ,
1367+ scale : {
1368+ numerator : scaleNumerator ,
1369+ denominator : scaleDenominator ,
1370+ } ,
1371+ hook : await mailbox . defaultHook ( ) ,
1372+ ...baseConfig ,
1373+ } ,
1374+ } ;
1375+
1376+ const warpRoute = await deployer . deploy ( config ) ;
1377+ const warpAddress = warpRoute [ chain ] . synthetic . address ;
1378+
1379+ const fetchPackageVersionStub = sinon
1380+ . stub ( evmERC20WarpRouteReader , 'fetchPackageVersion' )
1381+ . resolves ( '11.0.0' ) ;
1382+
1383+ const result = await evmERC20WarpRouteReader . fetchScale ( warpAddress ) ;
1384+ expect ( result ) . to . deep . equal ( {
1385+ numerator : BigInt ( scaleNumerator ) ,
1386+ denominator : BigInt ( scaleDenominator ) ,
1387+ } ) ;
1388+
1389+ fetchPackageVersionStub . restore ( ) ;
1390+ } ) ;
1391+
1392+ it ( 'should return undefined for legacy identity scale (scale() = 1)' , async ( ) => {
1393+ const identityScale = 1n ;
1394+
1395+ const encodedScale = identityScale . toString ( 16 ) . padStart ( 64 , '0' ) ;
1396+ const runtimeBytecode = `0x7f${ encodedScale } 60005260206000f3` ;
1397+ const mockAddress = '0x' + 'ad' . repeat ( 20 ) ;
1398+ await hre . network . provider . send ( 'hardhat_setCode' , [
1399+ mockAddress ,
1400+ runtimeBytecode ,
1401+ ] ) ;
1402+
1403+ const fetchPackageVersionStub = sinon
1404+ . stub ( evmERC20WarpRouteReader , 'fetchPackageVersion' )
1405+ . resolves ( '9.0.0' ) ;
1406+
1407+ const result = await evmERC20WarpRouteReader . fetchScale ( mockAddress ) ;
1408+ expect ( result ) . to . be . undefined ;
1409+
1410+ fetchPackageVersionStub . restore ( ) ;
1411+ } ) ;
1412+
1413+ it ( 'should return undefined for identity scale (1/1) on >= 11.0.0 contracts' , async ( ) => {
1414+ const config : WarpRouteDeployConfigMailboxRequired = {
1415+ [ chain ] : {
1416+ type : TokenType . synthetic ,
1417+ name : TOKEN_NAME ,
1418+ symbol : TOKEN_NAME ,
1419+ decimals : TOKEN_DECIMALS ,
1420+ hook : await mailbox . defaultHook ( ) ,
1421+ ...baseConfig ,
1422+ } ,
1423+ } ;
1424+
1425+ const warpRoute = await deployer . deploy ( config ) ;
1426+ const warpAddress = warpRoute [ chain ] . synthetic . address ;
1427+
1428+ const result = await evmERC20WarpRouteReader . fetchScale ( warpAddress ) ;
1429+ expect ( result ) . to . be . undefined ;
1430+ } ) ;
1431+ } ) ;
12541432
12551433 it ( 'should fail when modern version contract claims v10.0.0+ but is missing token() method' , async ( ) => {
12561434 const config : WarpRouteDeployConfigMailboxRequired = {
0 commit comments