Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for StdString for Contracts, Scripts, and Predicates #1277

Merged
merged 86 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from 82 commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
372cb49
prep new bytes
Aug 21, 2023
a4d39c8
add setup
Aug 26, 2023
f0a44f3
fix test
Aug 30, 2023
a77f152
refactor
Aug 30, 2023
e575cb2
adjust
Aug 30, 2023
02aaec0
tests
Aug 30, 2023
9777940
improve tests
Aug 30, 2023
ad5d4e5
aDJUST
Aug 30, 2023
7669318
refactor
Aug 30, 2023
cd22822
add tests
Aug 30, 2023
6fd2a23
refactor
Aug 30, 2023
a74e36b
adjust
Aug 30, 2023
c7bf842
adjust
Aug 30, 2023
f6b5ce7
fix length
Aug 30, 2023
929b747
correct reference
Aug 30, 2023
00dddc8
refactor
Aug 31, 2023
c000caf
twenty eagles lick?
Aug 31, 2023
408426f
adjust
Aug 31, 2023
0ef48d3
Merge branch 'master' into cm/issue-1188-bytes
Aug 31, 2023
d6095f3
Merge branch 'master' into cm/issue-1188-bytes
nedsalk Sep 3, 2023
ca0cc2d
Merge branch 'master' into cm/issue-1188-bytes
Sep 20, 2023
37b1ae9
fix post rename
Sep 20, 2023
3f0ef12
catch magic revert number
Sep 20, 2023
b525115
fix mapper
Sep 21, 2023
2a79936
Merge branch 'master' into cm/issue-1188-bytes
Sep 21, 2023
d07acd7
Merge branch 'cm/issue-1188-bytes' into cm/issue-1205
Sep 21, 2023
95d8983
add sway projects
Sep 21, 2023
d52848b
add predicate bytes test
Sep 21, 2023
132bf46
add predicate raw slice tests
Sep 21, 2023
551041a
Merge branch 'cm/issue-1188-bytes' into cm/issue-1205
Sep 21, 2023
2f7cc22
add bytes and raw slice sway projects
Sep 21, 2023
57e3bc5
add bytes test for script
Sep 21, 2023
2fd8c1d
add test for raw slice in script input
Sep 21, 2023
38a3851
add sway files
Sep 21, 2023
8c4020d
add basic tests
Sep 21, 2023
6572006
setup string Struct
Sep 21, 2023
2c66e8b
add basics
Sep 21, 2023
a7b72f2
clean
Sep 21, 2023
6efaa55
tests
Sep 21, 2023
75c2d80
add tests
Sep 21, 2023
2dda98d
update exhaustive
Sep 21, 2023
7b5d675
add sways for predicates and scripts
Sep 21, 2023
c6c15f4
add tests for string
Sep 21, 2023
d2bf9ed
update
Sep 21, 2023
5c28db3
add to interface test
Sep 21, 2023
5994c12
fix: linting warning
nedsalk Sep 21, 2023
01f0d13
Merge branch 'master' into cm/issue-1188-bytes
Sep 22, 2023
9e14ff9
Merge branch 'cm/issue-1188-bytes' into cm/issue-1205
Sep 22, 2023
d94aea3
Merge branch 'cm/issue-1205' into cm/issue-1274
Sep 22, 2023
98fa69e
Merge branch 'cm/issue-1274' into cm/issue-1276
Sep 22, 2023
ee0e942
add cs
Sep 22, 2023
3d47dc0
adjust
Sep 22, 2023
1ce3a79
convert errors
Sep 22, 2023
8bb5cce
Merge branch 'cm/issue-1188-bytes' into cm/issue-1205
Sep 22, 2023
022f845
Merge branch 'cm/issue-1205' into cm/issue-1274
Sep 22, 2023
b64bd68
Merge branch 'cm/issue-1274' into cm/issue-1276
Sep 22, 2023
29363d0
Merge branch 'cm/issue-1276' of github.com:FuelLabs/fuels-ts into cm/…
Sep 22, 2023
ffdb9e9
add struct string
Sep 22, 2023
9ba3bde
Merge branch 'master' into cm/issue-1188-bytes
Sep 22, 2023
0f578d1
Merge branch 'cm/issue-1188-bytes' into cm/issue-1205
Sep 22, 2023
e58b530
Merge branch 'cm/issue-1205' into cm/issue-1274
Sep 22, 2023
f9fca6b
Merge branch 'master' into cm/issue-1205
Sep 25, 2023
519032e
Merge branch 'cm/issue-1205' into cm/issue-1274
Sep 25, 2023
3a767a4
cs
camsjams Sep 25, 2023
184b2d6
Merge branch 'cm/issue-1205' into cm/issue-1274
Sep 25, 2023
61ce5d0
cs
camsjams Sep 25, 2023
de981f0
pretty
Sep 25, 2023
bb2b2b2
Merge branch 'master' into cm/issue-1205
Sep 25, 2023
230327d
Merge branch 'cm/issue-1205' into cm/issue-1274
Sep 25, 2023
dd6d679
Merge branch 'master' into cm/issue-1274
Sep 25, 2023
8d1c0b4
fix prettier and lint post merge
Sep 26, 2023
4edc0bd
fix assertions
Sep 26, 2023
1b896d1
Merge branch 'master' into cm/issue-1274
Sep 26, 2023
d8a3d87
revise
Sep 26, 2023
66d4042
Merge branch 'cm/issue-1274' into cm/issue-1276
Sep 26, 2023
f5c079a
add additional coverage on input validation
Sep 27, 2023
ff93afe
Merge branch 'master' into cm/issue-1274
Sep 27, 2023
19a29dc
Merge branch 'cm/issue-1274' into cm/issue-1276
Sep 27, 2023
b34e3f4
Merge branch 'master' into cm/issue-1276
Sep 27, 2023
912c9e2
Update packages/fuel-gauge/src/std-lib-string.test.ts
Sep 30, 2023
5c8d561
Update packages/fuel-gauge/src/std-lib-string.test.ts
Sep 30, 2023
b1e50d2
Merge branch 'master' into cm/issue-1276
Oct 1, 2023
c6df9c8
rename
Oct 3, 2023
dc106ae
Merge branch 'cm/issue-1276' of github.com:FuelLabs/fuels-ts into cm/…
Oct 3, 2023
b09c799
improve assertions
Oct 3, 2023
e16881f
Merge branch 'master' into cm/issue-1276
Oct 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/honest-plums-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@fuel-ts/abi-coder": minor
"@fuel-ts/program": minor
---

Add StdString dynamic string type
4 changes: 4 additions & 0 deletions packages/abi-coder/src/abi-coder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { EnumCoder } from './coders/enum';
import { NumberCoder } from './coders/number';
import { OptionCoder } from './coders/option';
import { RawSliceCoder } from './coders/raw-slice';
import { StdStringCoder } from './coders/stdString';
import { StringCoder } from './coders/string';
import { StructCoder } from './coders/struct';
import { TupleCoder } from './coders/tuple';
Expand All @@ -25,6 +26,7 @@ import {
OPTION_CODER_TYPE,
VEC_CODER_TYPE,
BYTES_CODER_TYPE,
STRING_CODER_TYPE,
} from './constants';
import type { JsonAbi, JsonAbiArgument } from './json-abi';
import { ResolvedAbiType } from './resolved-abi-type';
Expand Down Expand Up @@ -69,6 +71,8 @@ export abstract class AbiCoder {
return new B512Coder();
case BYTES_CODER_TYPE:
return new ByteCoder();
case STRING_CODER_TYPE:
return new StdStringCoder();
default:
break;
}
Expand Down
92 changes: 92 additions & 0 deletions packages/abi-coder/src/coders/stdString.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import type { Uint8ArrayWithDynamicData } from '../utilities';

import { StdStringCoder } from './stdString';

describe('StdStringCoder', () => {
it('should encode an empty string', () => {
const coder = new StdStringCoder();
const expected: Uint8ArrayWithDynamicData = new Uint8Array([
0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]);
expected.dynamicData = {
0: new Uint8Array([]),
};

const actual = coder.encode('');
expect(actual).toStrictEqual(expected);
});

it('should encode [hello world]', () => {
const coder = new StdStringCoder();
const expected: Uint8ArrayWithDynamicData = new Uint8Array([
0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 11,
]);
expected.dynamicData = {
0: new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 0, 0, 0, 0, 0]),
};

const actual = coder.encode('hello world');
expect(actual).toStrictEqual(expected);
});

it('should encode [H3llo W0rld]', () => {
const coder = new StdStringCoder();
const expected: Uint8ArrayWithDynamicData = new Uint8Array([
0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 11,
]);
expected.dynamicData = {
0: new Uint8Array([72, 51, 108, 108, 111, 32, 87, 48, 114, 108, 100, 0, 0, 0, 0, 0]),
};

const actual = coder.encode('H3llo W0rld');
expect(actual).toStrictEqual(expected);
});

it('should encode [abcdefghijklmnopqrstuvwxyz1234567890]', () => {
const coder = new StdStringCoder();
const expected: Uint8ArrayWithDynamicData = new Uint8Array([
0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 36,
]);
expected.dynamicData = {
0: new Uint8Array([
97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
116, 117, 118, 119, 120, 121, 122, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 0, 0, 0, 0,
]),
};

const actual = coder.encode('abcdefghijklmnopqrstuvwxyz1234567890');
expect(actual).toStrictEqual(expected);
});

it('should decode a string', () => {
const coder = new StdStringCoder();
const input = new Uint8Array([
0, 0, 0, 0, 0, 0, 49, 120, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 11, 72, 101, 108,
108, 111, 32, 87, 111, 114, 108, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]);
const expected = 'Hello World';

const [actual, newOffset] = coder.decode(input, 0);

expect(actual).toEqual(expected);
expect(newOffset).toEqual(24);
});

it('should decode a string [with offset]', () => {
const coder = new StdStringCoder();
const input = new Uint8Array([
0, 0, 0, 0, 0, 0, 49, 120, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 11, 72, 101, 108,
108, 111, 32, 87, 111, 114, 108, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]);
const expected = 'Hello World';

const [actual, newOffset] = coder.decode(input, 16);

expect(actual).toEqual(expected);
expect(newOffset).toEqual(40);
});
});
59 changes: 59 additions & 0 deletions packages/abi-coder/src/coders/stdString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { concat } from '@ethersproject/bytes';
import { toUtf8Bytes, toUtf8String } from '@ethersproject/strings';
import { bn } from '@fuel-ts/math';

import { WORD_SIZE } from '../constants';
import type { Uint8ArrayWithDynamicData } from '../utilities';
import { BASE_VECTOR_OFFSET, concatWithDynamicData } from '../utilities';

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

export class StdStringCoder extends Coder<string, string> {
static memorySize = 1;
constructor() {
super('struct', 'struct String', BASE_VECTOR_OFFSET);
}

encode(value: string): Uint8Array {
const parts: Uint8Array[] = [];

// pointer (ptr)
const pointer: Uint8ArrayWithDynamicData = new U64Coder().encode(BASE_VECTOR_OFFSET);

// pointer dynamicData, encode the string vector now and attach to its pointer
const data = this.#getPaddedData(value);
pointer.dynamicData = {
0: concatWithDynamicData([data]),
};

parts.push(pointer);

// capacity (cap)
parts.push(new U64Coder().encode(data.byteLength));

// length (len)
parts.push(new U64Coder().encode(value.length));

return concatWithDynamicData(parts);
}

#getPaddedData(value: string): Uint8Array {
const data: Uint8Array[] = [toUtf8Bytes(value)];

const paddingLength = (WORD_SIZE - (value.length % WORD_SIZE)) % WORD_SIZE;
if (paddingLength) {
data.push(new Uint8Array(paddingLength));
}

return concat(data);
}

decode(data: Uint8Array, offset: number): [string, number] {
const len = data.slice(16, 24);
const length = bn(new U64Coder().decode(len, 0)[0]).toNumber();
const byteData = data.slice(BASE_VECTOR_OFFSET, BASE_VECTOR_OFFSET + length);
const value = toUtf8String(byteData);
return [value, offset + BASE_VECTOR_OFFSET];
}
}
1 change: 1 addition & 0 deletions packages/abi-coder/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const OPTION_CODER_TYPE = 'enum Option';
export const VEC_CODER_TYPE = 'struct Vec';
export const BYTES_CODER_TYPE = 'struct Bytes';
export const STRING_CODER_TYPE = 'struct String';
camsjams marked this conversation as resolved.
Show resolved Hide resolved
export const stringRegEx = /str\[(?<length>[0-9]+)\]/;
export const arrayRegEx = /\[(?<item>[\w\s\\[\]]+);\s*(?<length>[0-9]+)\]/;
export const structRegEx = /^struct (?<name>\w+)$/;
Expand Down
5 changes: 3 additions & 2 deletions packages/abi-coder/src/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { concat, arrayify } from '@ethersproject/bytes';
import { ErrorCode, FuelError } from '@fuel-ts/errors';

import { U64Coder } from './coders/u64';
import { BYTES_CODER_TYPE, VEC_CODER_TYPE, WORD_SIZE } from './constants';
import { BYTES_CODER_TYPE, VEC_CODER_TYPE, STRING_CODER_TYPE, WORD_SIZE } from './constants';

export type DynamicData = {
[pointerIndex: number]: Uint8ArrayWithDynamicData;
Expand Down Expand Up @@ -143,7 +143,8 @@ export const isPointerType = (type: string) => {
}
};

export const isHeapType = (type: string) => type === VEC_CODER_TYPE || type === BYTES_CODER_TYPE;
export const isHeapType = (type: string) =>
type === VEC_CODER_TYPE || type === BYTES_CODER_TYPE || type === STRING_CODER_TYPE;

export function findOrThrow<T>(
arr: readonly T[],
Expand Down
10 changes: 9 additions & 1 deletion packages/abi-coder/test/interface.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,6 @@ describe('Abi interface', () => {
const data = (decoded as BN[]).slice(0, 3);
return Array.from(data);
},
decodedTransfoarmer: (decoded: unknown | undefined) => Array.from(decoded as Uint8Array),
},
{
fn: exhaustiveExamplesInterface.functions.raw_slice,
Expand All @@ -322,6 +321,15 @@ describe('Abi interface', () => {
return data.map((v: BN) => v.toNumber());
},
},
{
fn: exhaustiveExamplesInterface.functions.dynamic_string,
title: '[struct String]',
value: 'H3llo W0rld',
encodedValue: new Uint8Array([
0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 11, 72, 51, 108,
108, 111, 32, 87, 48, 114, 108, 100, 0, 0, 0, 0, 0,
]),
},
{
fn: exhaustiveExamplesInterface.functions.tuple_as_param,
title: '[tuple] as param',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
contract;
use std::b512::B512;
use std::bytes::Bytes;
use std::string::String;

enum EnumWithGeneric<T> {
VariantOne: T,
Expand Down Expand Up @@ -135,6 +136,7 @@ abi MyContract {
fn struct_with_implicitGenerics(arg: StructWithImplicitGenerics<b256, u8>) -> StructWithImplicitGenerics<b256, u8>;
fn bytes(arg: Bytes) -> Bytes;
fn raw_slice(arg: raw_slice) -> raw_slice;
fn dynamic_string(arg: String) -> String;

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 @@ -253,6 +255,10 @@ impl MyContract for Contract {
fn raw_slice(arg: raw_slice) -> raw_slice {
arg
}

fn dynamic_string(arg: String) -> String {
arg
}

fn two_u8_vectors(x: Vec<u8>, y: Vec<u8>) -> (Vec<u8>, Vec<u8>) {
(x, y)
Expand Down
3 changes: 3 additions & 0 deletions packages/fuel-gauge/fixtures/forc-projects/Forc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ members = [
"predicate-main-args-vector",
"predicate-multi-args",
"predicate-raw-slice",
"predicate-std-lib-string",
"predicate-struct",
"predicate-triple-sig",
"predicate-true",
Expand All @@ -35,11 +36,13 @@ members = [
"script-main-return-struct",
"script-main-two-args",
"script-raw-slice",
"script-std-lib-string",
"script-with-configurable",
"script-with-array",
"script-with-vector",
"script-with-vector-advanced",
"script-with-vector-mixed",
"std-lib-string",
"storage-test-contract",
"token_abi",
"token_contract",
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 = "predicate-std-lib-string"

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

use std::string::String;

fn validate_string(arg: String) -> bool {
// to be replaced with a simpler assert_eq once
// https://github.com/FuelLabs/sway/issues/4868 is done
let bytes = arg.as_bytes();

let inner = String::from_ascii_str("Hello World");
let expected_bytes = inner.as_bytes();

if expected_bytes.len() != bytes.len() {
return false;
}

let mut i = 0;
while i < bytes.len() {
if expected_bytes.get(i).unwrap() != bytes.get(i).unwrap() {
return false;
}
i += 1;
}

true
}

fn main(_arg_0: u64, _arg_1: u64, arg_2: String) -> bool {
validate_string(arg_2)
}
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-std-lib-string"

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

use std::string::String;

fn validate_string(arg: String) {
// to be replaced with a simpler assert_eq once
// https://github.com/FuelLabs/sway/issues/4868 is done
let bytes = arg.as_bytes();

let inner = String::from_ascii_str("Hello World");
let expected_bytes = inner.as_bytes();

assert_eq(expected_bytes.len(), bytes.len());

let mut i = 0;
while i < bytes.len() {
assert(expected_bytes.get(i).unwrap() == bytes.get(i).unwrap());
i += 1;
}
}

fn main(arg: String) {
validate_string(arg);
}
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 = "std-lib-string"

[dependencies]
Loading