Skip to content

Commit

Permalink
feat: add support for Bytes and RawSlice as inputs for Scripts (F…
Browse files Browse the repository at this point in the history
…uelLabs#1275)

* prep new bytes

* add setup

* fix test

* refactor

* adjust

* tests

* improve tests

* aDJUST

* refactor

* add tests

* refactor

* adjust

* adjust

* fix length

* correct reference

* refactor

* twenty eagles lick?

* adjust

* fix post rename

* catch magic revert number

* fix mapper

* add sway projects

* add predicate bytes test

* add predicate raw slice tests

* add bytes and raw slice sway projects

* add bytes test for script

* add test for raw slice in script input

* fix: linting warning

* adjust

* convert errors

* cs

* cs

* pretty

* fix prettier and lint post merge

* fix assertions

* revise

* add additional coverage on input validation

---------

Co-authored-by: Nedim Salkić <[email protected]>
  • Loading branch information
Cameron Manavian and nedsalk authored Sep 27, 2023
1 parent bf7413f commit 4f21279
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 21 deletions.
4 changes: 4 additions & 0 deletions .changeset/grey-eagles-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
---

Add tests for Bytes and RawSlice
12 changes: 12 additions & 0 deletions packages/abi-coder/src/coders/byte.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { FuelError, ErrorCode } from '@fuel-ts/errors';
import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils';

import type { Uint8ArrayWithDynamicData } from '../utilities';

import { ByteCoder } from './byte';
Expand Down Expand Up @@ -31,6 +34,15 @@ describe('ByteCoder', () => {
expect(actual).toStrictEqual(expected);
});

it('should throw when value to encode is not array', async () => {
const coder = new ByteCoder();
const nonArrayInput = { ...[1] };
await expectToThrowFuelError(
() => coder.encode(nonArrayInput),
new FuelError(ErrorCode.ENCODE_ERROR, 'Expected array value.')
);
});

it('should decode a byte', () => {
const coder = new ByteCoder();
const input = new Uint8Array([
Expand Down
11 changes: 11 additions & 0 deletions packages/abi-coder/src/coders/raw-slice.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { FuelError, ErrorCode } from '@fuel-ts/errors';
import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils';
import type { BN } from '@fuel-ts/math';

import type { Uint8ArrayWithDynamicData } from '../utilities';
Expand All @@ -19,6 +21,15 @@ describe('RawSliceCoder', () => {
expect(actual).toStrictEqual(expected);
});

it('should throw when value to encode is not array', async () => {
const coder = new RawSliceCoder();
const nonArrayInput = { ...[1] };
await expectToThrowFuelError(
() => coder.encode(nonArrayInput),
new FuelError(ErrorCode.ENCODE_ERROR, 'Expected array value.')
);
});

it('should decode a raw-slice', () => {
const coder = new RawSliceCoder();
const input = new Uint8Array([
Expand Down
2 changes: 2 additions & 0 deletions packages/fuel-gauge/fixtures/forc-projects/Forc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ members = [
"predicate-vector-types",
"raw-slice",
"revert-error",
"script-bytes",
"script-main-args",
"script-main-return-struct",
"script-main-two-args",
"script-raw-slice",
"script-with-configurable",
"script-with-array",
"script-with-vector",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
authors = ["FuelLabs"]
entry = "main.sw"
license = "Apache-2.0"
name = "script-bytes"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
script;

use std::bytes::Bytes;

#[allow(dead_code)]
enum SomeEnum<T> {
First: bool,
Second: T,
}

struct Wrapper<T> {
inner: T,
inner_enum: SomeEnum<Bytes>,
}

fn expected_bytes() -> Bytes {
let mut bytes = Bytes::new();

bytes.push(40u8);
bytes.push(41u8);
bytes.push(42u8);

bytes
}

fn main(_arg: u64, wrapper: Wrapper<Vec<Bytes>>) {
if let SomeEnum::Second(enum_bytes) = wrapper.inner_enum {
require(enum_bytes == expected_bytes(), "wrapper.inner_enum didn't carry the expected bytes")
} else {
require(false, "enum was not of variant Second");
}

let inner_vec = wrapper.inner;
require(inner_vec.len() == 2, "Expected wrapper.inner vector to have 2 elements");
require(inner_vec.get(0).unwrap() == expected_bytes(), "wrapper.inner[0] didn't match expectation");
require(inner_vec.get(1).unwrap() == expected_bytes(), "wrapper.inner[1] didn't match expectation");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
authors = ["FuelLabs"]
entry = "main.sw"
license = "Apache-2.0"
name = "script-raw-slice"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
script;

#[allow(dead_code)]
enum SomeEnum<T> {
First: bool,
Second: T,
}

struct Wrapper<T> {
inner: T,
inner_enum: SomeEnum<raw_slice>,
}

fn validate_raw_slice(input: raw_slice) {
let vec: Vec<u64> = Vec::from(input);
require(vec.len() == 3, "raw slice len is not 3");
require(vec.get(2).unwrap() == 42, "expected 3rd slice entry to be 42");
require(vec.get(1).unwrap() == 41, "expected 2nd slice entry to be 41");
require(vec.get(0).unwrap() == 40, "expected 1st slice entry to be 40");
}

fn validate_vec(vec: Vec<raw_slice>) {
require(vec.len() == 2, "vec should have two elements");
validate_raw_slice(vec.get(0).unwrap());
validate_raw_slice(vec.get(1).unwrap());
}

fn main(_arg: u64, wrapper: Wrapper<Vec<raw_slice>>) -> raw_slice {
if let SomeEnum::Second(enum_raw_slice) = wrapper.inner_enum
{
validate_raw_slice(enum_raw_slice);
} else {
require(false, "enum was not of variant Second");
}

validate_vec(wrapper.inner);

let mut rtn: Vec<u64> = Vec::new();
rtn.push(1);
rtn.push(2);
rtn.push(3);

rtn.as_raw_slice()
}
42 changes: 31 additions & 11 deletions packages/fuel-gauge/src/bytes.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { generateTestWallet } from '@fuel-ts/wallet/test-utils';
import type { BN } from 'fuels';
import {
type Contract,
bn,
Expand All @@ -13,7 +14,7 @@ import {
import predicateBytes from '../fixtures/forc-projects/predicate-bytes';
import predicateBytesAbi from '../fixtures/forc-projects/predicate-bytes/out/debug/predicate-bytes-abi.json';

import { getSetupContract } from './utils';
import { getScript, getSetupContract } from './utils';

const setupContract = getSetupContract('bytes');
let contractInstance: Contract;
Expand All @@ -31,6 +32,15 @@ type Wrapper = {
inner_enum: SomeEnum;
};

const setup = async (balance = 5_000) => {
const provider = await Provider.create(FUEL_NETWORK_URL);

// Create wallet
const wallet = await generateTestWallet(provider, [[balance, BaseAssetId]]);

return wallet;
};

describe('Bytes Tests', () => {
it('should test bytes output', async () => {
const INPUT = 10;
Expand All @@ -51,9 +61,8 @@ describe('Bytes Tests', () => {
it('should test bytes input', async () => {
const INPUT = [40, 41, 42];

await contractInstance.functions.accept_bytes(INPUT).call<number[]>();

expect(true).toBeTruthy();
const { value } = await contractInstance.functions.accept_bytes(INPUT).call<number[]>();
expect(value).toBeUndefined();
});

it('should test bytes input [nested]', async () => {
Expand All @@ -64,16 +73,12 @@ describe('Bytes Tests', () => {
inner_enum: { Second: bytes },
};

await contractInstance.functions.accept_nested_bytes(INPUT).call<number[]>();

expect(true).toBeTruthy();
const { value } = await contractInstance.functions.accept_nested_bytes(INPUT).call<number[]>();
expect(value).toBeUndefined();
});

it('should test bytes input [predicate-bytes]', async () => {
const provider = await Provider.create(FUEL_NETWORK_URL);

// Create wallet
const wallet = await generateTestWallet(provider, [[5_000, BaseAssetId]]);
const wallet = await setup();
const receiver = Wallet.fromAddress(Address.fromRandom(), wallet.provider);
const amountToPredicate = 100;
const amountToReceiver = 50;
Expand Down Expand Up @@ -104,4 +109,19 @@ describe('Bytes Tests', () => {
const finalPredicateBalance = await predicate.getBalance();
expect(finalPredicateBalance.lte(initialPredicateBalance)).toBeTruthy();
});

it('should test bytes input [script-bytes]', async () => {
const wallet = await setup();
type MainArgs = [number, Wrapper];
const scriptInstance = getScript<MainArgs, void>('script-bytes', wallet);

const bytes = [40, 41, 42];
const INPUT: Wrapper = {
inner: [bytes, bytes],
inner_enum: { Second: bytes },
};

const { value } = await scriptInstance.functions.main(1, INPUT).call<BN>();
expect(value.toNumber()).toStrictEqual(0);
});
});
43 changes: 33 additions & 10 deletions packages/fuel-gauge/src/raw-slice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import predicateRawSlice from '../fixtures/forc-projects/predicate-raw-slice';
import predicateRawSliceAbi from '../fixtures/forc-projects/predicate-raw-slice/out/debug/predicate-raw-slice-abi.json';

import { getSetupContract } from './utils';
import { getScript, getSetupContract } from './utils';

type SomeEnum = {
First?: boolean;
Expand All @@ -26,6 +26,15 @@ type Wrapper = {
inner_enum: SomeEnum;
};

const setup = async (balance = 5_000) => {
const provider = await Provider.create(FUEL_NETWORK_URL);

// Create wallet
const wallet = await generateTestWallet(provider, [[balance, BaseAssetId]]);

return wallet;
};

const setupContract = getSetupContract('raw-slice');
let contractInstance: Contract;
beforeAll(async () => {
Expand All @@ -44,9 +53,9 @@ describe('Raw Slice Tests', () => {
it('should test raw slice input', async () => {
const INPUT = [40, 41, 42];

await contractInstance.functions.accept_raw_slice(INPUT).call<number[]>();
const { value } = await contractInstance.functions.accept_raw_slice(INPUT).call<number[]>();

expect(true).toBeTruthy();
expect(value).toBeUndefined();
});

it('should test raw slice input [nested]', async () => {
Expand All @@ -56,16 +65,15 @@ describe('Raw Slice Tests', () => {
inner_enum: { Second: slice },
};

await contractInstance.functions.accept_nested_raw_slice(INPUT).call<number[]>();
const { value } = await contractInstance.functions
.accept_nested_raw_slice(INPUT)
.call<number[]>();

expect(true).toBeTruthy();
expect(value).toBeUndefined();
});

it('should test raw slice input [predicate-raw slice]', async () => {
const provider = await Provider.create(FUEL_NETWORK_URL);

// Create wallet
const wallet = await generateTestWallet(provider, [[5_000, BaseAssetId]]);
it('should test raw slice input [predicate-raw-slice]', async () => {
const wallet = await setup();
const receiver = Wallet.fromAddress(Address.fromRandom(), wallet.provider);
const amountToPredicate = 100;
const amountToReceiver = 50;
Expand Down Expand Up @@ -100,4 +108,19 @@ describe('Raw Slice Tests', () => {
const finalPredicateBalance = await predicate.getBalance();
expect(finalPredicateBalance.lte(initialPredicateBalance)).toBeTruthy();
});

it('should test bytes input [script-raw-slice]', async () => {
const wallet = await setup();
type MainArgs = [number, Wrapper];
const scriptInstance = getScript<MainArgs, void>('script-raw-slice', wallet);

const bytes = [40, 41, 42];
const INPUT: Wrapper = {
inner: [bytes, bytes],
inner_enum: { Second: bytes },
};

const { value } = await scriptInstance.functions.main(1, INPUT).call<BN[]>();
expect(value.map((v: BN) => v.toNumber())).toStrictEqual([1, 2, 3]);
});
});

0 comments on commit 4f21279

Please sign in to comment.