diff --git a/.changeset/orange-moles-leave.md b/.changeset/orange-moles-leave.md new file mode 100644 index 00000000000..b749b60962f --- /dev/null +++ b/.changeset/orange-moles-leave.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/abi-coder": patch +--- + +fix: validation and handling of unsafe integers diff --git a/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts b/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts index 2cd1632c752..dc43eaa6a19 100644 --- a/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts +++ b/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts @@ -23,6 +23,44 @@ describe('BigNumberCoder', () => { expect(actual).toStrictEqual(expected); }); + it('should encode a u64 [max safe integer]', () => { + const coder = new BigNumberCoder('u64'); + const value: number = Number.MAX_SAFE_INTEGER; + const expected = new Uint8Array([0, 31, 255, 255, 255, 255, 255, 255]); + + const data = coder.encode(value); + + expect(data).toEqual(expected); + }); + + it('should throw an error when encoding [number more than max safe integer]', () => { + const coder = new BigNumberCoder('u64'); + const value: number = Number.MAX_SAFE_INTEGER + 1; + + expect(() => coder.encode(value)).toThrow( + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid u64 type - number value is too large.') + ); + }); + + it('should encode a u64 [very big number - as string]', () => { + const coder = new BigNumberCoder('u64'); + const value: string = '76472027892439376'; + const expected = new Uint8Array([1, 15, 174, 231, 121, 200, 89, 80]); + + const data = coder.encode(value); + + expect(data).toEqual(expected); + }); + + it('should throw an error when encoding [number more than max safe integer]', () => { + const coder = new BigNumberCoder('u64'); + const value: number = 76472027892439376; + + expect(() => coder.encode(value)).toThrow( + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid u64 type - number value is too large.') + ); + }); + it('should decode a u64 number', () => { const coder = new BigNumberCoder('u64'); const expectedValue = 0; @@ -34,6 +72,17 @@ describe('BigNumberCoder', () => { expect(actualLength).toBe(expectedLength); }); + it('should decode a u64 [very big number]', () => { + const coder = new BigNumberCoder('u64'); + const data = new Uint8Array([1, 15, 174, 231, 121, 200, 89, 80]); + const expectedValue = bn('76472027892439376'); + + const [actualValue, actualLength] = coder.decode(data, 0); + + expect(actualValue).toEqualBn(expectedValue); + expect(actualLength).toEqual(8); + }); + it('should encode u8 max number', () => { const coder = new BigNumberCoder('u64'); const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 255]); diff --git a/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts b/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts index 08e8c58eb5c..e9a68d5e783 100644 --- a/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts +++ b/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts @@ -20,6 +20,16 @@ export class BigNumberCoder extends Coder { encode(value: BNInput): Uint8Array { let bytes; + // We throw an error if the value is a number and it's more than the max safe integer + // This is because we can experience some odd behavior with integers more than the max safe integer + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER#description + if (typeof value === 'number' && value > Number.MAX_SAFE_INTEGER) { + throw new FuelError( + ErrorCode.ENCODE_ERROR, + `Invalid ${this.type} type - number value is too large.` + ); + } + try { bytes = toBytes(value, this.encodedLength); } catch (error) {