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 encoding / decoding for bytes[CompactBytesArray] #261

Merged
merged 10 commits into from
Jan 24, 2023
47 changes: 47 additions & 0 deletions src/lib/encoder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,27 @@ describe('encoder', () => {
encodedValue:
'0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000001337000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000054ef',
},
{
valueType: 'bytes[CompactBytesArray]',
decodedValue: [
'0xaabb',
'0xcafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe',
CJ42 marked this conversation as resolved.
Show resolved Hide resolved
'0xbeefbeefbeefbeefbeef',
],
encodedValue:
'0x0002aabb0020cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe000abeefbeefbeefbeefbeef',
},
{
valueType: 'bytes[CompactBytesArray]',
decodedValue: [
// 512 bytes long value
'0xcafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe',
// 500 bytes long
'0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef',
CJ42 marked this conversation as resolved.
Show resolved Hide resolved
],
encodedValue:
'0x0200cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe01f4beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef',
CJ42 marked this conversation as resolved.
Show resolved Hide resolved
},
];

testCases.forEach((testCase) => {
Expand Down Expand Up @@ -148,6 +169,32 @@ describe('encoder', () => {
() => true,
);
});

it('should throw when trying to decode a `bytes[CompactBytesArray]` with an invalid length byte', async () => {
expect(() => {
decodeValueType('bytes[CompactBytesArray]', '0x0005cafe');
}).to.throw("Couldn't decode bytes[CompactBytesArray]");
});

it('should throw when trying to encode a array that contains non hex string as `bytes[CompactBytesArray]`', async () => {
expect(() => {
encodeValueType('bytes[CompactBytesArray]', [
'some random string',
'another random strings',
'0xaabbccdd',
]);
}).to.throw("Couldn't encode, value at index 0 is not hex");
});

it('should throw when trying to encode a `bytes[CompactBytesArray]` with a bytes length bigger than 65_535', async () => {
expect(() => {
encodeValueType('bytes[CompactBytesArray]', [
'0x' + 'ab'.repeat(66_0000),
]);
}).to.throw(
"Couldn't encode bytes[CompactBytesArray], value at index 0 exceeds 65_535 bytes",
);
});
});

describe('valueContent', () => {
Expand Down
48 changes: 48 additions & 0 deletions src/lib/encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,50 @@ const decodeDataSourceWithHash = (value: string): URLDataWithHash => {
return { hashFunction: hashFunction.name, hash: dataHash, url: dataSource };
};

const encodeCompactBytesArray = (values: string[]): string => {
let compactBytesArray = '0x';
for (let i = 0; i < values.length; i++) {
if (!isHex(values[i]))
throw new Error(`Couldn't encode, value at index ${i} is not hex`);

if (values[i].length > 65_535 * 2 + 2)
throw new Error(
`Couldn't encode bytes[CompactBytesArray], value at index ${i} exceeds 65_535 bytes`,
);

const numberOfBytes = (values[i].length - 2) / 2;
const hexNumber = padLeft(numberToHex(numberOfBytes), 4);

compactBytesArray += hexNumber.substring(2) + values[i].substring(2);
}

return compactBytesArray;
};

const decodeCompactBytesArray = (compactBytesArray: string): string[] => {
if (!isHex(compactBytesArray))
throw new Error("Couldn't decode, value is not hex");

let pointer = 2;
CJ42 marked this conversation as resolved.
Show resolved Hide resolved
const encodedValues: string[] = [];
while (pointer < compactBytesArray.length) {
const length = hexToNumber(
'0x' + compactBytesArray.substring(pointer, pointer + 4),
);
encodedValues.push(
'0x' +
compactBytesArray.substring(pointer + 4, pointer + 2 * (length + 2)),
);

pointer += 2 * (length + 2);
}

if (pointer > compactBytesArray.length)
throw new Error("Couldn't decode bytes[CompactBytesArray]");

return encodedValues;
};

const valueTypeEncodingMap = {
string: {
encode: (value: string) => abiCoder.encodeParameter('string', value),
Expand Down Expand Up @@ -132,6 +176,10 @@ const valueTypeEncodingMap = {
encode: (value: string[]) => abiCoder.encodeParameter('bytes[]', value),
decode: (value: string) => abiCoder.decodeParameter('bytes[]', value),
},
'bytes[CompactBytesArray]': {
encode: (value: string[]) => encodeCompactBytesArray(value),
decode: (value: string) => decodeCompactBytesArray(value),
},
};

// Use enum for type bellow
Expand Down