Skip to content

Commit

Permalink
feat: update forc to v0.46.1 (#1343)
Browse files Browse the repository at this point in the history
* up v

* add cs

* format

* cleanup sway files as part of upgrade

* update

* add AssetId

* update abi

* add

* add tests

* update check

* adjust

* adjust logs

* add

* catch invalid string types

* fixes

* update

* change encoded length value

* lint
  • Loading branch information
Cameron Manavian authored Oct 20, 2023
1 parent 077893d commit 3e03409
Show file tree
Hide file tree
Showing 34 changed files with 297 additions and 80 deletions.
5 changes: 5 additions & 0 deletions .changeset/lovely-buttons-sparkle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fuel-ts/forc": minor
---

Updated forc version
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@
"editor.formatOnSave": false,
"editor.formatOnPaste": false
},
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"[sway]": {
"editor.defaultFormatter": "FuelLabs.sway-vscode-plugin"
}
}
2 changes: 1 addition & 1 deletion apps/docs-snippets/projects/echo-configurables/src/main.sw
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct MyStruct {

configurable {
age: u8 = 25,
tag: str[4] = "fuel",
tag: str[4] = __to_str_array("fuel"),
grades: [u8; 4] = [3, 4, 3, 2],
my_struct: MyStruct = MyStruct {
x: 1,
Expand Down
2 changes: 1 addition & 1 deletion apps/docs-snippets/projects/return-context/src/main.sw
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
contract;

use std::{context::{msg_amount}};
use std::context::msg_amount;

abi ReturnContext {
#[payable]
Expand Down
5 changes: 1 addition & 4 deletions apps/docs-snippets/projects/simple-token/src/main.sw
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,19 @@ contract;

use ::simple_token_abi::SimpleToken;

use std::hash::*;
storage {
balances: StorageMap<b256, u64> = StorageMap {},
}

impl SimpleToken for Contract {
#[storage(read, write)]
fn deposit(address: b256, amount: u64) {
let current_balance = storage.balances.get(address).try_read().unwrap_or(0);

storage.balances.insert(address, current_balance + amount);
}

#[storage(read)]
fn get_balance(address: b256) -> u64 {
let balance = storage.balances.get(address).try_read().unwrap_or(0);

balance
}
}
Expand Down
10 changes: 10 additions & 0 deletions packages/abi-coder/src/abi-coder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ErrorCode, FuelError } from '@fuel-ts/errors';

import type { DecodedValue, InputValue, Coder } from './coders/abstract-coder';
import { ArrayCoder } from './coders/array';
import { AssetIdCoder } from './coders/asset-id';
import { B256Coder } from './coders/b256';
import { B512Coder } from './coders/b512';
import { BooleanCoder } from './coders/boolean';
Expand Down Expand Up @@ -69,6 +70,8 @@ export abstract class AbiCoder {
return new B256Coder();
case 'struct B512':
return new B512Coder();
case 'struct AssetId':
return new AssetIdCoder();
case BYTES_CODER_TYPE:
return new ByteCoder();
case STD_STRING_CODER_TYPE:
Expand Down Expand Up @@ -140,6 +143,13 @@ export abstract class AbiCoder {
return new TupleCoder(coders);
}

if (resolvedAbiType.type === 'str') {
throw new FuelError(
ErrorCode.INVALID_DATA,
'String slices can not be decoded from logs. Convert the slice to `str[N]` with `__to_str_array`'
);
}

throw new FuelError(
ErrorCode.CODER_NOT_FOUND,
`Coder not found: ${JSON.stringify(resolvedAbiType)}.`
Expand Down
94 changes: 94 additions & 0 deletions packages/abi-coder/src/coders/asset-id.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { AssetIdCoder } from './asset-id';

describe('AssetIdCoder', () => {
const B256_DECODED = '0xd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b';
const B256_ENCODED = new Uint8Array([
213, 87, 156, 70, 223, 204, 127, 24, 32, 112, 19, 230, 91, 68, 228, 203, 78, 44, 34, 152, 244,
172, 69, 123, 168, 248, 39, 67, 243, 30, 147, 11,
]);
const B256_ZERO_DECODED = '0x0000000000000000000000000000000000000000000000000000000000000000';
const B256_ZERO_ENCODED = new Uint8Array(32);

const coder = new AssetIdCoder();

it('should encode zero as a 256 bit hash string', () => {
const expected = B256_ZERO_ENCODED;
const actual = coder.encode(B256_ZERO_DECODED);

expect(actual).toStrictEqual(expected);
});

it('should encode a 256 bit hash string', () => {
const expected = B256_ENCODED;
const actual = coder.encode(B256_DECODED);

expect(actual).toStrictEqual(expected);
});

it('should decode zero as a 256 bit hash string', () => {
const expectedValue = B256_ZERO_DECODED;
const expectedLength = B256_ZERO_ENCODED.length;
const [actualValue, actualLength] = coder.decode(B256_ZERO_ENCODED, 0);

expect(actualValue).toStrictEqual(expectedValue);
expect(actualLength).toBe(expectedLength);
});

it('should decode a 256 bit hash string', () => {
const expectedValue = B256_DECODED;
const expectedLength = B256_ENCODED.length;
const [actualValue, actualLength] = coder.decode(B256_ENCODED, 0);

expect(actualValue).toStrictEqual(expectedValue);
expect(actualLength).toBe(expectedLength);
});

it('should throw an error when encoding a 256 bit hash string that is too short', () => {
const invalidInput = B256_DECODED.slice(0, B256_DECODED.length - 1);

expect(() => {
coder.encode(invalidInput);
}).toThrow('Invalid struct AssetId');
});

it('should throw an error when decoding an encoded 256 bit hash string that is too short', () => {
const invalidInput = B256_ENCODED.slice(0, B256_ENCODED.length - 1);

expect(() => {
coder.decode(invalidInput, 0);
}).toThrow();
});

it('should throw an error when encoding a 256 bit hash string that is too long', () => {
const invalidInput = `${B256_DECODED}0`;

expect(() => {
coder.encode(invalidInput);
}).toThrow('Invalid struct AssetId');
});

it('should throw an error when encoding a 512 bit hash string', () => {
const B512 =
'0x8e9dda6f7793745ac5aacf9e907cae30b2a01fdf0d23b7750a85c6a44fca0c29f0906f9d1f1e92e6a1fb3c3dcef3cc3b3cdbaae27e47b9d9a4c6a4fce4cf16b2';

expect(() => {
coder.encode(B512);
}).toThrow('Invalid struct AssetId');
});

it('should throw an error when decoding an encoded 256 bit hash string that is too long', () => {
const invalidInput = new Uint8Array(Array.from(Array(32).keys()));

expect(() => {
coder.decode(invalidInput, 1);
}).toThrow('Invalid size for AssetId');
});

it('should throw an error when encoding a 256 bit hash string that is not a hex string', () => {
const invalidInput = 'not a hex string';

expect(() => {
coder.encode(invalidInput);
}).toThrow('Invalid struct AssetId');
});
});
36 changes: 36 additions & 0 deletions packages/abi-coder/src/coders/asset-id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ErrorCode } from '@fuel-ts/errors';
import { bn, toHex } from '@fuel-ts/math';
import { getBytes } from 'ethers';

import { Coder } from './abstract-coder';

export class AssetIdCoder extends Coder<string, string> {
constructor() {
super('AssetId', 'struct AssetId', 32);
}

encode(value: string): Uint8Array {
let encodedValue;
try {
encodedValue = getBytes(value);
} catch (error) {
this.throwError(ErrorCode.ENCODE_ERROR, `Invalid ${this.type}.`);
}
if (encodedValue.length !== 32) {
this.throwError(ErrorCode.ENCODE_ERROR, `Invalid ${this.type}.`);
}
return encodedValue;
}

decode(data: Uint8Array, offset: number): [string, number] {
let bytes = data.slice(offset, offset + 32);
const decoded = bn(bytes);
if (decoded.isZero()) {
bytes = new Uint8Array(32);
}
if (bytes.length !== 32) {
this.throwError(ErrorCode.DECODE_ERROR, `Invalid size for AssetId.`);
}
return [toHex(bytes, 32), offset + 32];
}
}
2 changes: 1 addition & 1 deletion packages/abi-coder/src/coders/stdString.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { U64Coder } from './u64';
export class StdStringCoder extends Coder<string, string> {
static memorySize = 1;
constructor() {
super('struct', 'struct String', BASE_VECTOR_OFFSET);
super('struct', 'struct String', 1);
}

encode(value: string): Uint8Array {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ configurable {
U8: u8 = 8u8,
BOOL: bool = true,
ARRAY: [u32; 3] = [253u32, 254u32, 255u32],
STR_4: str[4] = "fuel",
STR_4: str[4] = __to_str_array("fuel"),
STRUCT: StructA<u8, bool> = StructA {
propA1: 8u8,
propA2: true,
Expand Down Expand Up @@ -137,6 +137,7 @@ abi MyContract {
fn bytes(arg: Bytes) -> Bytes;
fn raw_slice(arg: raw_slice) -> raw_slice;
fn dynamic_string(arg: String) -> String;
fn asset_id(arg: AssetId) -> AssetId;

fn tuple_as_param(x: (u8, StructA<StructB<u64>, str[3]>)) -> (u8, StructA<StructB<u64>, str[3]>);
fn array_simple(x: [u8; 4]) -> [u8; 4];
Expand Down Expand Up @@ -260,6 +261,10 @@ impl MyContract for Contract {
arg
}

fn asset_id(arg: AssetId) -> AssetId {
arg
}

fn two_u8_vectors(x: Vec<u8>, y: Vec<u8>) -> (Vec<u8>, Vec<u8>) {
(x, y)
}
Expand Down
27 changes: 27 additions & 0 deletions packages/abi-typegen/src/abi/types/AssetIdType.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { AssetIdType } from './AssetIdType';
import { BoolType } from './BoolType';

describe('AssetIdType.ts', () => {
test('should properly parse type attributes', () => {
const b512 = new AssetIdType({
rawAbiType: {
components: null,
typeParameters: null,
typeId: 1,
type: AssetIdType.swayType,
},
});

b512.parseComponentsAttributes({ types: [] });

const suitableForAssetId = AssetIdType.isSuitableFor({ type: AssetIdType.swayType });
const suitableForBool = AssetIdType.isSuitableFor({ type: BoolType.swayType });

expect(suitableForAssetId).toEqual(true);
expect(suitableForBool).toEqual(false);

expect(b512.attributes.inputLabel).toEqual('string');
expect(b512.attributes.outputLabel).toEqual('string');
expect(b512.requiredFuelsMembersImports).toStrictEqual([]);
});
});
13 changes: 13 additions & 0 deletions packages/abi-typegen/src/abi/types/AssetIdType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { B256Type } from './B256Type';

export class AssetIdType extends B256Type {
public static swayType = 'struct AssetId';

public name = 'AssetId';

static MATCH_REGEX = /^struct AssetId$/m;

static isSuitableFor(params: { type: string }) {
return AssetIdType.MATCH_REGEX.test(params.type);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
contract;

use std::vm::evm::evm_address::EvmAddress;
use std::b512::B512;
use std::string::String;
use std::bytes::Bytes;

Expand Down Expand Up @@ -29,8 +30,10 @@ abi MyContract {
fn types_u32(x: u32) -> u32;
fn types_u64(x: u64) -> u64;
fn types_str(x: str[5]) -> str[5];
fn types_asset_id(x: AssetId) -> AssetId;
fn types_bool(x: bool) -> bool;
fn types_b256(x: b256) -> b256;
fn types_b512(x: B512) -> B512;
fn types_struct(x: MyStruct) -> MyStruct;
fn types_array(x: [u8; 3]) -> [u8; 3];
fn types_tuple(x: (u8, u8, u8)) -> (u8, u8, u8);
Expand Down Expand Up @@ -60,14 +63,20 @@ impl MyContract for Contract {
4294967295000
}
fn types_str(x: str[5]) -> str[5] {
"Hello"
__to_str_array("Hello")
}
fn types_bool(x: bool) -> bool {
true
}
fn types_asset_id(x: AssetId) -> AssetId {
x
}
fn types_b256(x: b256) -> b256 {
0x0000000000000000000000000000000000000000000000000000000000000000
}
fn types_b512(x: B512) -> B512 {
x
}
fn types_array(x: [u8; 3]) -> [u8; 3] {
x
}
Expand Down
10 changes: 10 additions & 0 deletions packages/abi-typegen/test/fixtures/templates/contract/dts.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import type { Option, Enum, Vec } from "./common";
export enum MyEnumInput { Checked = 'Checked', Pending = 'Pending' };
export enum MyEnumOutput { Checked = 'Checked', Pending = 'Pending' };

export type AssetIdInput = { value: string };
export type AssetIdOutput = AssetIdInput;
export type MyStructInput = { x: BigNumberish, y: BigNumberish, state: MyEnumInput };
export type MyStructOutput = { x: number, y: number, state: MyEnumOutput };
export type RawBytesInput = { ptr: BigNumberish, cap: BigNumberish };
Expand All @@ -36,7 +38,9 @@ export type StructWithMultiOptionOutput = { multiple: [Option<number>, Option<nu
interface MyContractAbiInterface extends Interface {
functions: {
types_array: FunctionFragment;
types_asset_id: FunctionFragment;
types_b256: FunctionFragment;
types_b512: FunctionFragment;
types_bool: FunctionFragment;
types_bytes: FunctionFragment;
types_enum: FunctionFragment;
Expand All @@ -58,7 +62,9 @@ interface MyContractAbiInterface extends Interface {
};

encodeFunctionData(functionFragment: 'types_array', values: [[BigNumberish, BigNumberish, BigNumberish]]): Uint8Array;
encodeFunctionData(functionFragment: 'types_asset_id', values: [AssetIdInput]): Uint8Array;
encodeFunctionData(functionFragment: 'types_b256', values: [string]): Uint8Array;
encodeFunctionData(functionFragment: 'types_b512', values: [string]): Uint8Array;
encodeFunctionData(functionFragment: 'types_bool', values: [boolean]): Uint8Array;
encodeFunctionData(functionFragment: 'types_bytes', values: [Bytes]): Uint8Array;
encodeFunctionData(functionFragment: 'types_enum', values: [MyEnumInput]): Uint8Array;
Expand All @@ -79,7 +85,9 @@ interface MyContractAbiInterface extends Interface {
encodeFunctionData(functionFragment: 'types_vector_u8', values: [Vec<BigNumberish>]): Uint8Array;

decodeFunctionData(functionFragment: 'types_array', data: BytesLike): DecodedValue;
decodeFunctionData(functionFragment: 'types_asset_id', data: BytesLike): DecodedValue;
decodeFunctionData(functionFragment: 'types_b256', data: BytesLike): DecodedValue;
decodeFunctionData(functionFragment: 'types_b512', data: BytesLike): DecodedValue;
decodeFunctionData(functionFragment: 'types_bool', data: BytesLike): DecodedValue;
decodeFunctionData(functionFragment: 'types_bytes', data: BytesLike): DecodedValue;
decodeFunctionData(functionFragment: 'types_enum', data: BytesLike): DecodedValue;
Expand All @@ -104,7 +112,9 @@ export class MyContractAbi extends Contract {
interface: MyContractAbiInterface;
functions: {
types_array: InvokeFunction<[x: [BigNumberish, BigNumberish, BigNumberish]], [number, number, number]>;
types_asset_id: InvokeFunction<[x: AssetIdInput], AssetIdOutput>;
types_b256: InvokeFunction<[x: string], string>;
types_b512: InvokeFunction<[x: string], string>;
types_bool: InvokeFunction<[x: boolean], boolean>;
types_bytes: InvokeFunction<[x: Bytes], Bytes>;
types_enum: InvokeFunction<[x: MyEnumInput], MyEnumOutput>;
Expand Down
Loading

0 comments on commit 3e03409

Please sign in to comment.