diff --git a/.changeset/modern-students-poke.md b/.changeset/modern-students-poke.md new file mode 100644 index 00000000000..252bea9a57f --- /dev/null +++ b/.changeset/modern-students-poke.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/abi-coder": patch +--- + +fix: `enum` size validation diff --git a/.github/workflows/pr-release.yaml b/.github/workflows/pr-release.yaml index 6cc274cba00..6eeca1f3f04 100644 --- a/.github/workflows/pr-release.yaml +++ b/.github/workflows/pr-release.yaml @@ -36,4 +36,4 @@ jobs: message: | This PR is published in NPM with version **${{ steps.release.outputs.published_version }}** env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/packages/abi-coder/src/encoding/coders/EnumCoder.ts b/packages/abi-coder/src/encoding/coders/EnumCoder.ts index 4fe2b1fbe40..f27f0d3b6e3 100644 --- a/packages/abi-coder/src/encoding/coders/EnumCoder.ts +++ b/packages/abi-coder/src/encoding/coders/EnumCoder.ts @@ -3,7 +3,7 @@ import { toNumber } from '@fuel-ts/math'; import { concat } from '@fuel-ts/utils'; import type { RequireExactlyOne } from 'type-fest'; -import { OPTION_CODER_TYPE, WORD_SIZE } from '../../utils/constants'; +import { OPTION_CODER_TYPE } from '../../utils/constants'; import { hasNestedOption } from '../../utils/utilities'; import type { TypesOfCoder } from './AbstractCoder'; @@ -36,7 +36,7 @@ export class EnumCoder> extends Coder< constructor(name: string, coders: TCoders) { const caseIndexCoder = new BigNumberCoder('u64'); const encodedValueSize = Object.values(coders).reduce( - (max, coder) => Math.max(max, coder.encodedLength), + (min, coder) => Math.min(min, coder.encodedLength), 0 ); super(`enum ${name}`, `enum ${name}`, caseIndexCoder.encodedLength + encodedValueSize); @@ -80,7 +80,7 @@ export class EnumCoder> extends Coder< } decode(data: Uint8Array, offset: number): [DecodedValueOf, number] { - if (this.#shouldValidateLength && data.length < this.#encodedValueSize) { + if (this.#shouldValidateLength && data.length < this.encodedLength) { throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid enum data size.`); } @@ -93,9 +93,12 @@ export class EnumCoder> extends Coder< `Invalid caseIndex "${caseIndex}". Valid cases: ${Object.keys(this.coders)}.` ); } - const valueCoder = this.coders[caseKey]; - const offsetAndCase = offset + WORD_SIZE; + const offsetAndCase = offset + this.#caseIndexCoder.encodedLength; + + if (this.#shouldValidateLength && data.length < offsetAndCase + valueCoder.encodedLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid enum data size.`); + } const [decoded, newOffset] = valueCoder.decode(data, offsetAndCase); diff --git a/packages/fuel-gauge/src/options.test.ts b/packages/fuel-gauge/src/options.test.ts index 7d00d5380e1..548e3214db4 100644 --- a/packages/fuel-gauge/src/options.test.ts +++ b/packages/fuel-gauge/src/options.test.ts @@ -192,4 +192,26 @@ describe('Options Tests', () => { expect(value).toStrictEqual(undefined); }); + + it('echoes option enum diff sizes', async () => { + const { value } = await contractInstance.functions.echo_enum_diff_sizes(undefined).call(); + + expect(value).toStrictEqual(undefined); + + const { value: value2 } = await contractInstance.functions + .echo_enum_diff_sizes({ a: U8_MAX }) + .call(); + + expect(value2).toStrictEqual({ a: U8_MAX }); + + const { value: value3 } = await contractInstance.functions + .echo_enum_diff_sizes({ + b: '0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c', + }) + .call(); + + expect(value3).toStrictEqual({ + b: '0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c', + }); + }); }); diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/options/src/main.sw b/packages/fuel-gauge/test/fixtures/forc-projects/options/src/main.sw index 883e6d074c5..95cfda5a5ae 100644 --- a/packages/fuel-gauge/test/fixtures/forc-projects/options/src/main.sw +++ b/packages/fuel-gauge/test/fixtures/forc-projects/options/src/main.sw @@ -56,6 +56,11 @@ struct SomeStruct { b: u64, } +enum DiffSizeEnum { + a: u8, + b: b256, +} + storage { stuff: StorageMap = StorageMap {}, } @@ -71,6 +76,7 @@ abi OptionContract { fn echo_array_option(arg: [Option; 3]) -> [Option; 3]; fn print_enum_option_array() -> GardenVector; fn echo_deeply_nested_option(arg: DeepStruct) -> DeepStruct; + fn echo_enum_diff_sizes(arg: Option) -> Option; } impl OptionContract for Contract { @@ -110,4 +116,8 @@ impl OptionContract for Contract { fn echo_deeply_nested_option(arg: DeepStruct) -> DeepStruct { arg } + + fn echo_enum_diff_sizes(arg: Option) -> Option { + arg + } }