Skip to content

Commit

Permalink
feat: added assertion for ABI-encoding integer ranges
Browse files Browse the repository at this point in the history
  • Loading branch information
jxom committed Dec 1, 2024
1 parent 2e0d4af commit 15f9863
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/metal-drinks-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ox": patch
---

Added assertion for ABI-encoding integer ranges.
117 changes: 115 additions & 2 deletions src/core/_test/AbiParameters.encode.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AbiItem, AbiParameters } from 'ox'
import { AbiItem, AbiParameters, Solidity } from 'ox'
import { describe, expect, test } from 'vitest'

import { seaportContractConfig } from '../../../test/constants/abis.js'
Expand Down Expand Up @@ -229,7 +229,7 @@ describe('static', () => {
[
{
name: 'xIn',
type: 'int8',
type: 'int32',
},
],
[-2147483648],
Expand Down Expand Up @@ -1789,6 +1789,119 @@ test('invalid bytes', () => {
)
})

test('integer out of range', () => {
expect(() =>
AbiParameters.encode(
[
{
type: 'uint128',
},
{
type: 'int128',
},
{
type: 'int128',
},
{
type: 'uint',
},
{
type: 'int',
},
{
type: 'int',
},
],
[
Solidity.maxUint128,
Solidity.maxInt128,
Solidity.minInt128,
Solidity.maxUint256,
Solidity.maxInt256,
Solidity.minInt256,
],
),
).not.toThrow()

expect(() =>
AbiParameters.encode(
[
{
type: 'uint128',
},
],
[Solidity.maxUint128 + 1n],
),
).toThrowErrorMatchingInlineSnapshot(
'[Hex.IntegerOutOfRangeError: Number `340282366920938463463374607431768211456` is not in safe 128-bit unsigned integer range (`0` to `340282366920938463463374607431768211455`)]',
)

expect(() =>
AbiParameters.encode(
[
{
type: 'uint128',
},
],
[-1n],
),
).toThrowErrorMatchingInlineSnapshot(
'[Hex.IntegerOutOfRangeError: Number `-1` is not in safe 128-bit unsigned integer range (`0` to `340282366920938463463374607431768211455`)]',
)

expect(() =>
AbiParameters.encode(
[
{
type: 'int128',
},
],
[Solidity.maxInt128 + 1n],
),
).toThrowErrorMatchingInlineSnapshot(
'[Hex.IntegerOutOfRangeError: Number `170141183460469231731687303715884105728` is not in safe 128-bit signed integer range (`-170141183460469231731687303715884105728` to `170141183460469231731687303715884105727`)]',
)

expect(() =>
AbiParameters.encode(
[
{
type: 'int128',
},
],
[Solidity.minInt128 - 1n],
),
).toThrowErrorMatchingInlineSnapshot(
'[Hex.IntegerOutOfRangeError: Number `-170141183460469231731687303715884105729` is not in safe 128-bit signed integer range (`-170141183460469231731687303715884105728` to `170141183460469231731687303715884105727`)]',
)

expect(() =>
AbiParameters.encode(
[
{
type: 'uint',
},
],
[Solidity.maxUint256 + 1n],
),
).toThrowErrorMatchingInlineSnapshot(
'[Hex.IntegerOutOfRangeError: Number `115792089237316195423570985008687907853269984665640564039457584007913129639936` is not in safe 256-bit unsigned integer range (`0` to `115792089237316195423570985008687907853269984665640564039457584007913129639935`)]',
)

expect(() =>
AbiParameters.encode(
[
{
type: 'uint',
},
],
[-1n],
),
).toThrowErrorMatchingInlineSnapshot(
'[Hex.IntegerOutOfRangeError: Number `-1` is not in safe 256-bit unsigned integer range (`0` to `115792089237316195423570985008687907853269984665640564039457584007913129639935`)]',
)
})

test('getArrayComponents', () => {
expect(getArrayComponents('uint256[2]')).toEqual([2, 'uint256'])
expect(getArrayComponents('uint256[2][3]')).toEqual([3, 'uint256[2]'])
Expand Down
21 changes: 19 additions & 2 deletions src/core/internal/abiParameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as Address from '../Address.js'
import * as Bytes from '../Bytes.js'
import * as Errors from '../Errors.js'
import * as Hex from '../Hex.js'
import { integerRegex } from '../Solidity.js'
import type * as Cursor from './cursor.js'
import type { Compute, IsNarrowable, UnionToIntersection } from './types.js'

Expand Down Expand Up @@ -433,7 +434,11 @@ export function prepareParameter<
}
if (parameter.type.startsWith('uint') || parameter.type.startsWith('int')) {
const signed = parameter.type.startsWith('int')
return encodeNumber(value as unknown as number, { signed })
const [, , size = '256'] = integerRegex.exec(parameter.type) ?? []
return encodeNumber(value as unknown as number, {
signed,
size: Number(size),
})
}
if (parameter.type.startsWith('bytes')) {
return encodeBytes(value as unknown as Hex.Hex, { type: parameter.type })
Expand Down Expand Up @@ -630,8 +635,20 @@ export declare namespace encodeBoolean {
/** @internal */
export function encodeNumber(
value: number,
{ signed }: { signed: boolean },
{ signed, size }: { signed: boolean; size: number },
): PreparedParameter {
if (typeof size === 'number') {
const max = 2n ** (BigInt(size) - (signed ? 1n : 0n)) - 1n
const min = signed ? -max - 1n : 0n
if (value > max || value < min)
throw new Hex.IntegerOutOfRangeError({
max: max.toString(),
min: min.toString(),
signed,
size: size / 8,
value: value.toString(),
})
}
return {
dynamic: false,
encoded: Hex.fromNumber(value, {
Expand Down

0 comments on commit 15f9863

Please sign in to comment.