Skip to content

Commit

Permalink
Merge branch 'master' into changeset-release/master
Browse files Browse the repository at this point in the history
  • Loading branch information
danielbate authored May 17, 2024
2 parents c6f91aa + 0da455a commit 0640eb7
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 12 deletions.
5 changes: 5 additions & 0 deletions .changeset/olive-meals-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fuel-ts/abi-coder": minor
---

feat!: remove nested option size validation
7 changes: 5 additions & 2 deletions packages/abi-coder/src/encoding/coders/ArrayCoder.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ErrorCode, FuelError } from '@fuel-ts/errors';
import { concat } from '@fuel-ts/utils';

import { MAX_BYTES } from '../../utils/constants';
import { MAX_BYTES, OPTION_CODER_TYPE } from '../../utils/constants';

import type { TypesOfCoder } from './AbstractCoder';
import { Coder } from './AbstractCoder';
Expand All @@ -15,11 +15,14 @@ export class ArrayCoder<TCoder extends Coder> extends Coder<
> {
coder: TCoder;
length: number;
#hasNestedOption: boolean;

constructor(coder: TCoder, length: number) {
const hasNestedOption = coder.type === OPTION_CODER_TYPE;
super('array', `[${coder.type}; ${length}]`, length * coder.encodedLength);
this.coder = coder;
this.length = length;
this.#hasNestedOption = hasNestedOption;
}

encode(value: InputValueOf<TCoder>): Uint8Array {
Expand All @@ -35,7 +38,7 @@ export class ArrayCoder<TCoder extends Coder> extends Coder<
}

decode(data: Uint8Array, offset: number): [DecodedValueOf<TCoder>, number] {
if (data.length < this.encodedLength || data.length > MAX_BYTES) {
if ((!this.#hasNestedOption && data.length < this.encodedLength) || data.length > MAX_BYTES) {
throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid array data size.`);
}

Expand Down
7 changes: 5 additions & 2 deletions packages/abi-coder/src/encoding/coders/EnumCoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { toNumber } from '@fuel-ts/math';
import { concat } from '@fuel-ts/utils';
import type { RequireExactlyOne } from 'type-fest';

import { WORD_SIZE } from '../../utils/constants';
import { OPTION_CODER_TYPE, WORD_SIZE } from '../../utils/constants';

import type { TypesOfCoder } from './AbstractCoder';
import { Coder } from './AbstractCoder';
Expand All @@ -30,8 +30,10 @@ export class EnumCoder<TCoders extends Record<string, Coder>> extends Coder<
coders: TCoders;
#caseIndexCoder: BigNumberCoder;
#encodedValueSize: number;
#hasNestedOption: boolean;

constructor(name: string, coders: TCoders) {
const hasNestedOption = Object.values(coders).some((coder) => coder.type === OPTION_CODER_TYPE);
const caseIndexCoder = new BigNumberCoder('u64');
const encodedValueSize = Object.values(coders).reduce(
(max, coder) => Math.max(max, coder.encodedLength),
Expand All @@ -42,6 +44,7 @@ export class EnumCoder<TCoders extends Record<string, Coder>> extends Coder<
this.coders = coders;
this.#caseIndexCoder = caseIndexCoder;
this.#encodedValueSize = encodedValueSize;
this.#hasNestedOption = hasNestedOption;
}

#encodeNativeEnum(value: string): Uint8Array {
Expand Down Expand Up @@ -77,7 +80,7 @@ export class EnumCoder<TCoders extends Record<string, Coder>> extends Coder<
}

decode(data: Uint8Array, offset: number): [DecodedValueOf<TCoders>, number] {
if (data.length < this.#encodedValueSize) {
if (!this.#hasNestedOption && data.length < this.#encodedValueSize) {
throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid enum data size.`);
}

Expand Down
7 changes: 6 additions & 1 deletion packages/abi-coder/src/encoding/coders/StructCoder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ErrorCode, FuelError } from '@fuel-ts/errors';
import { concatBytes } from '@fuel-ts/utils';

import { OPTION_CODER_TYPE } from '../../utils/constants';

import type { TypesOfCoder } from './AbstractCoder';
import { Coder } from './AbstractCoder';
import { OptionCoder } from './OptionCoder';
Expand All @@ -18,15 +20,18 @@ export class StructCoder<TCoders extends Record<string, Coder>> extends Coder<
> {
name: string;
coders: TCoders;
#hasNestedOption: boolean;

constructor(name: string, coders: TCoders) {
const hasNestedOption = Object.values(coders).some((coder) => coder.type === OPTION_CODER_TYPE);
const encodedLength = Object.values(coders).reduce(
(acc, coder) => acc + coder.encodedLength,
0
);
super('struct', `struct ${name}`, encodedLength);
this.name = name;
this.coders = coders;
this.#hasNestedOption = hasNestedOption;
}

encode(value: InputValueOf<TCoders>): Uint8Array {
Expand All @@ -48,7 +53,7 @@ export class StructCoder<TCoders extends Record<string, Coder>> extends Coder<
}

decode(data: Uint8Array, offset: number): [DecodedValueOf<TCoders>, number] {
if (data.length < this.encodedLength) {
if (!this.#hasNestedOption && data.length < this.encodedLength) {
throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid struct data size.`);
}

Expand Down
7 changes: 6 additions & 1 deletion packages/abi-coder/src/encoding/coders/TupleCoder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ErrorCode, FuelError } from '@fuel-ts/errors';
import { concatBytes } from '@fuel-ts/utils';

import { OPTION_CODER_TYPE } from '../../utils/constants';

import type { TypesOfCoder } from './AbstractCoder';
import { Coder } from './AbstractCoder';

Expand All @@ -16,11 +18,14 @@ export class TupleCoder<TCoders extends Coder[]> extends Coder<
DecodedValueOf<TCoders>
> {
coders: TCoders;
#hasNestedOption: boolean;

constructor(coders: TCoders) {
const hasNestedOption = coders.some((coder) => coder.type === OPTION_CODER_TYPE);
const encodedLength = coders.reduce((acc, coder) => acc + coder.encodedLength, 0);
super('tuple', `(${coders.map((coder) => coder.type).join(', ')})`, encodedLength);
this.coders = coders;
this.#hasNestedOption = hasNestedOption;
}

encode(value: InputValueOf<TCoders>): Uint8Array {
Expand All @@ -32,7 +37,7 @@ export class TupleCoder<TCoders extends Coder[]> extends Coder<
}

decode(data: Uint8Array, offset: number): [DecodedValueOf<TCoders>, number] {
if (data.length < this.encodedLength) {
if (!this.#hasNestedOption && data.length < this.encodedLength) {
throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid tuple data size.`);
}

Expand Down
11 changes: 5 additions & 6 deletions packages/abi-coder/src/encoding/coders/VecCoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import { ErrorCode, FuelError } from '@fuel-ts/errors';
import { bn } from '@fuel-ts/math';
import { concatBytes } from '@fuel-ts/utils';

import { MAX_BYTES, WORD_SIZE } from '../../utils/constants';
import { MAX_BYTES, OPTION_CODER_TYPE, WORD_SIZE } from '../../utils/constants';
import { isUint8Array } from '../../utils/utilities';

import { Coder } from './AbstractCoder';
import type { TypesOfCoder } from './AbstractCoder';
import { BigNumberCoder } from './BigNumberCoder';
import { OptionCoder } from './OptionCoder';

type InputValueOf<TCoder extends Coder> = Array<TypesOfCoder<TCoder>['Input']> | Uint8Array;
type DecodedValueOf<TCoder extends Coder> = Array<TypesOfCoder<TCoder>['Decoded']>;
Expand All @@ -18,12 +17,12 @@ export class VecCoder<TCoder extends Coder> extends Coder<
DecodedValueOf<TCoder>
> {
coder: TCoder;
#isOptionVec: boolean;
#hasNestedOption: boolean;

constructor(coder: TCoder) {
super('struct', `struct Vec`, coder.encodedLength + WORD_SIZE);
this.coder = coder;
this.#isOptionVec = this.coder instanceof OptionCoder;
this.#hasNestedOption = coder.type === OPTION_CODER_TYPE;
}

encode(value: InputValueOf<TCoder>): Uint8Array {
Expand All @@ -47,7 +46,7 @@ export class VecCoder<TCoder extends Coder> extends Coder<
}

decode(data: Uint8Array, offset: number): [DecodedValueOf<TCoder>, number] {
if (!this.#isOptionVec && (data.length < this.encodedLength || data.length > MAX_BYTES)) {
if ((!this.#hasNestedOption && data.length < this.encodedLength) || data.length > MAX_BYTES) {
throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid vec data size.`);
}

Expand All @@ -57,7 +56,7 @@ export class VecCoder<TCoder extends Coder> extends Coder<
const dataLength = length * this.coder.encodedLength;
const dataBytes = data.slice(offsetAndLength, offsetAndLength + dataLength);

if (!this.#isOptionVec && dataBytes.length !== dataLength) {
if (!this.#hasNestedOption && dataBytes.length !== dataLength) {
throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid vec byte data size.`);
}

Expand Down
151 changes: 151 additions & 0 deletions packages/fuel-gauge/src/options.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import type { Contract } from 'fuels';

import { getSetupContract } from './utils';

const U8_MAX = 255;
const U16_MAX = 65535;
const U32_MAX = 4294967295;

const setupContract = getSetupContract('options');
let contractInstance: Contract;
beforeAll(async () => {
contractInstance = await setupContract();
});

/**
* @group node
*/
describe('Options Tests', () => {
it('echos u8 option', async () => {
const someInput = U8_MAX;
const noneInput = undefined;

const { value: someValue } = await contractInstance.functions.echo_option(someInput).call();

expect(someValue).toBe(someInput);

const { value: noneValue } = await contractInstance.functions.echo_option(noneInput).call();

expect(noneValue).toBe(noneInput);
});

it('echos struct enum option', async () => {
const someInput = {
one: {
a: U8_MAX,
},
two: U32_MAX,
};

const { value: someValue } = await contractInstance.functions
.echo_struct_enum_option(someInput)
.call();

expect(someValue).toStrictEqual(someInput);

const noneInput = {
one: {
a: undefined,
},
two: undefined,
};

const { value: noneValue } = await contractInstance.functions
.echo_struct_enum_option(noneInput)
.call();

expect(noneValue).toStrictEqual(noneInput);
});

it('echos vec option', async () => {
const someInput = [U8_MAX, U16_MAX, U32_MAX];

const { value: someValue } = await contractInstance.functions.echo_vec_option(someInput).call();

expect(someValue).toStrictEqual(someInput);

const noneInput = [undefined, undefined, undefined];

const { value: noneValue } = await contractInstance.functions.echo_vec_option(noneInput).call();

expect(noneValue).toStrictEqual(noneInput);

const mixedInput = [U8_MAX, undefined, U32_MAX];

const { value: mixedValue } = await contractInstance.functions
.echo_vec_option(mixedInput)
.call();

expect(mixedValue).toStrictEqual(mixedInput);
});

it('echos tuple option', async () => {
const someInput = [U8_MAX, U16_MAX];

const { value: someValue } = await contractInstance.functions
.echo_tuple_option(someInput)
.call();

expect(someValue).toStrictEqual(someInput);

const noneInput = [undefined, undefined];

const { value: noneValue } = await contractInstance.functions
.echo_tuple_option(noneInput)
.call();

expect(noneValue).toStrictEqual(noneInput);

const mixedInput = [U8_MAX, undefined];

const { value: mixedValue } = await contractInstance.functions
.echo_tuple_option(mixedInput)
.call();

expect(mixedValue).toStrictEqual(mixedInput);
});

it('echoes enum option', async () => {
const someInput = { a: U8_MAX };

const { value: someValue } = await contractInstance.functions
.echo_enum_option(someInput)
.call();

expect(someValue).toStrictEqual(someInput);

const noneInput = { b: undefined };

const { value: noneValue } = await contractInstance.functions
.echo_enum_option(noneInput)
.call();

expect(noneValue).toStrictEqual(noneInput);
});

it('echos array option', async () => {
const someInput = [U8_MAX, U16_MAX, 123];

const { value: someValue } = await contractInstance.functions
.echo_array_option(someInput)
.call();

expect(someValue).toStrictEqual(someInput);

const noneInput = [undefined, undefined, undefined];

const { value: noneValue } = await contractInstance.functions
.echo_array_option(noneInput)
.call();

expect(noneValue).toStrictEqual(noneInput);

const mixedInput = [U8_MAX, undefined, 123];

const { value: mixedValue } = await contractInstance.functions
.echo_array_option(mixedInput)
.call();

expect(mixedValue).toStrictEqual(mixedInput);
});
});
1 change: 1 addition & 0 deletions packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,5 @@ members = [
"vector-types-contract",
"vector-types-script",
"vectors",
"options",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "options"

[dependencies]
Loading

0 comments on commit 0640eb7

Please sign in to comment.