Skip to content

Commit 697019a

Browse files
committed
Address PR review comments: scale validation and overflow handling
- Add .gt(0) validation for scale numerator in TypeScript schema (Cursor comment) - Support string representation for large scale values (> MAX_SAFE_INTEGER) - Update fetchScale to safely handle BigNumber overflow with try/catch - Update deploy.ts, decimals.ts, and TokenMetadataMap.ts to handle string scale values Fractional scaling tests already exist in HypERC20.t.sol (CodeRabbit flagged incorrectly)
1 parent 4a3ccac commit 697019a

5 files changed

Lines changed: 61 additions & 16 deletions

File tree

typescript/sdk/src/token/EvmERC20WarpRouteReader.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,11 +1091,16 @@ export class EvmERC20WarpRouteReader extends EvmRouterReader {
10911091
* for contracts >= 11.0.0, otherwise reads legacy scale value.
10921092
*
10931093
* @param tokenRouterAddress - The address of the TokenRouter contract.
1094-
* @returns The scale as either a number (for old contracts or when denominator is 1) or an object with numerator/denominator.
1094+
* @returns The scale as either a number/string (for old contracts or when denominator is 1) or an object with numerator/denominator.
10951095
*/
10961096
async fetchScale(
10971097
tokenRouterAddress: Address,
1098-
): Promise<number | { numerator: number; denominator: number } | undefined> {
1098+
): Promise<
1099+
| number
1100+
| string
1101+
| { numerator: number | string; denominator: number | string }
1102+
| undefined
1103+
> {
10991104
const packageVersion = await this.fetchPackageVersion(tokenRouterAddress);
11001105
const hasScaleFractionInterface =
11011106
compareVersions(packageVersion, SCALE_FRACTION_VERSION) >= 0;
@@ -1105,21 +1110,32 @@ export class EvmERC20WarpRouteReader extends EvmRouterReader {
11051110
this.provider,
11061111
);
11071112

1113+
// Helper to safely convert BigNumber to number or string
1114+
// Uses string representation for values > Number.MAX_SAFE_INTEGER
1115+
const safeToNumberOrString = (bn: BigNumber): number | string => {
1116+
try {
1117+
return bn.toNumber();
1118+
} catch {
1119+
// Value exceeds Number.MAX_SAFE_INTEGER, return as string
1120+
return bn.toString();
1121+
}
1122+
};
1123+
11081124
if (hasScaleFractionInterface) {
11091125
// Read new format (scaleNumerator and scaleDenominator)
11101126
const [numerator, denominator] = await Promise.all([
11111127
tokenRouter.scaleNumerator(),
11121128
tokenRouter.scaleDenominator(),
11131129
]);
11141130

1115-
// If denominator is 1, return as a simple number for backward compatibility
1131+
// If denominator is 1, return as a simple number/string for backward compatibility
11161132
if (denominator.eq(1)) {
1117-
return numerator.toNumber();
1133+
return safeToNumberOrString(numerator);
11181134
}
11191135

11201136
return {
1121-
numerator: numerator.toNumber(),
1122-
denominator: denominator.toNumber(),
1137+
numerator: safeToNumberOrString(numerator),
1138+
denominator: safeToNumberOrString(denominator),
11231139
};
11241140
} else {
11251141
// Read old format (single scale value) using low-level call
@@ -1133,7 +1149,7 @@ export class EvmERC20WarpRouteReader extends EvmRouterReader {
11331149
this.provider,
11341150
);
11351151
const scale = await legacyContract.scale();
1136-
return scale.toNumber();
1152+
return safeToNumberOrString(scale);
11371153
}
11381154
}
11391155

typescript/sdk/src/token/TokenMetadataMap.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ export class TokenMetadataMap {
4646

4747
getScale(
4848
chain: string,
49-
): number | { numerator: number; denominator: number } | undefined {
49+
):
50+
| number
51+
| string
52+
| { numerator: number | string; denominator: number | string }
53+
| undefined {
5054
return this.tokenMetadataMap.get(chain)?.scale;
5155
}
5256

typescript/sdk/src/token/deploy.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,16 @@ abstract class TokenDeployer<
153153
const scale = config.scale ?? 1;
154154

155155
// Convert scale to numerator/denominator format
156-
const { numerator, denominator } =
157-
typeof scale === 'number' ? { numerator: scale, denominator: 1 } : scale;
156+
// Handle number, string, and object formats
157+
let numerator: number | string;
158+
let denominator: number | string;
159+
if (typeof scale === 'number' || typeof scale === 'string') {
160+
numerator = scale;
161+
denominator = 1;
162+
} else {
163+
numerator = scale.numerator;
164+
denominator = scale.denominator;
165+
}
158166

159167
if (isCollateralTokenConfig(config) || isXERC20TokenConfig(config)) {
160168
return [config.token, numerator, denominator, config.mailbox];

typescript/sdk/src/token/types.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,11 @@ export const TokenMetadataSchema = z.object({
3939
decimals: z.number().gt(0).optional(),
4040
scale: z
4141
.union([
42-
z.number(),
42+
z.number().gt(0),
43+
z.string(), // string representation for large values
4344
z.object({
44-
numerator: z.number(),
45-
denominator: z.number().gt(0),
45+
numerator: z.union([z.number().gt(0), z.string()]),
46+
denominator: z.union([z.number().gt(0), z.string()]),
4647
}),
4748
])
4849
.optional(),

typescript/sdk/src/utils/decimals.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ export function verifyScale(
1515
: configMap;
1616
const decimalsByChain: ChainMap<{
1717
decimals: number;
18-
scale?: number | { numerator: number; denominator: number };
18+
scale?:
19+
| number
20+
| string
21+
| { numerator: number | string; denominator: number | string };
1922
}> = objMap(chainDecimalConfigPairs, (chain, config) => {
2023
assert(
2124
config.decimals,
@@ -40,8 +43,18 @@ export function verifyScale(
4043
scaleValue = 1;
4144
} else if (typeof config.scale === 'number') {
4245
scaleValue = config.scale;
46+
} else if (typeof config.scale === 'string') {
47+
scaleValue = Number(config.scale);
4348
} else {
44-
scaleValue = config.scale.numerator / config.scale.denominator;
49+
const numerator =
50+
typeof config.scale.numerator === 'string'
51+
? Number(config.scale.numerator)
52+
: config.scale.numerator;
53+
const denominator =
54+
typeof config.scale.denominator === 'string'
55+
? Number(config.scale.denominator)
56+
: config.scale.denominator;
57+
scaleValue = numerator / denominator;
4558
}
4659

4760
if (calculatedScale !== scaleValue) {
@@ -56,7 +69,10 @@ export function verifyScale(
5669
function areDecimalsUniform(
5770
configMap: ChainMap<{
5871
decimals: number;
59-
scale?: number | { numerator: number; denominator: number };
72+
scale?:
73+
| number
74+
| string
75+
| { numerator: number | string; denominator: number | string };
6076
}>,
6177
): boolean {
6278
const values = [...Object.values(configMap)];

0 commit comments

Comments
 (0)