diff options
4 files changed, 40 insertions, 29 deletions
diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts index 6bc299544..82a519aae 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/bool.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts @@ -22,10 +22,6 @@ export class Bool extends PayloadDataType { } } - public getSignature(): string { - return 'bool'; - } - public encodeValue(value: boolean): Buffer { const encodedValue = value ? '0x1' : '0x0'; const encodedValueBuf = ethUtil.setLengthLeft( @@ -47,4 +43,8 @@ export class Bool extends PayloadDataType { /* tslint:enable boolean-naming */ return value; } + + public getSignature(): string { + return 'bool'; + } } diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts index 626e266c9..ce6ace627 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts @@ -17,42 +17,53 @@ export class DynamicBytes extends PayloadDataType { public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { super(dataItem, dataTypeFactory, DynamicBytes._SIZE_KNOWN_AT_COMPILE_TIME); if (!DynamicBytes.matchType(dataItem.type)) { - throw new Error(`Tried to instantiate DynamicBytes with bad input: ${dataItem}`); + throw new Error(`Tried to instantiate Dynamic Bytes with bad input: ${dataItem}`); } } public encodeValue(value: string | Buffer): Buffer { - if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix. Got '${value}'`); - } + // Encoded value is of the form: <length><value>, with each field padded to be word-aligned. + // 1/3 Construct the length const valueBuf = ethUtil.toBuffer(value); - if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); - } - - const wordsForValue = Math.ceil(valueBuf.byteLength / Constants.EVM_WORD_WIDTH_IN_BYTES); - const paddedDynamicBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES; - const paddedValueBuf = ethUtil.setLengthRight(valueBuf, paddedDynamicBytesForValue); - const paddedLengthBuf = ethUtil.setLengthLeft( - ethUtil.toBuffer(valueBuf.byteLength), - Constants.EVM_WORD_WIDTH_IN_BYTES, - ); - const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); - return encodedValueBuf; + const wordsToStoreValuePadded = Math.ceil(valueBuf.byteLength / Constants.EVM_WORD_WIDTH_IN_BYTES); + const bytesToStoreValuePadded = wordsToStoreValuePadded * Constants.EVM_WORD_WIDTH_IN_BYTES; + const lengthBuf = ethUtil.toBuffer(valueBuf.byteLength); + const lengthBufPadded = ethUtil.setLengthLeft(lengthBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); + // 2/3 Construct the value + this._sanityCheckValue(value); + const valueBufPadded = ethUtil.setLengthRight(valueBuf, bytesToStoreValuePadded); + // 3/3 Combine length and value + const encodedValue = Buffer.concat([lengthBufPadded, valueBufPadded]); + return encodedValue; } public decodeValue(calldata: RawCalldata): string { + // Encoded value is of the form: <length><value>, with each field padded to be word-aligned. + // 1/2 Decode length const lengthBuf = calldata.popWord(); const lengthHex = ethUtil.bufferToHex(lengthBuf); const length = parseInt(lengthHex, Constants.HEX_BASE); - const wordsForValue = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); - const paddedValueBuf = calldata.popWords(wordsForValue); - const valueBuf = paddedValueBuf.slice(0, length); - const decodedValue = ethUtil.bufferToHex(valueBuf); - return decodedValue; + // 2/2 Decode value + const wordsToStoreValuePadded = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); + const valueBufPadded = calldata.popWords(wordsToStoreValuePadded); + const valueBuf = valueBufPadded.slice(0, length); + const value = ethUtil.bufferToHex(valueBuf); + this._sanityCheckValue(value); + return value; } public getSignature(): string { return 'bytes'; } + + private _sanityCheckValue(value: string | Buffer): void { + if (typeof value !== 'string') { + return; + } + if (!value.startsWith('0x')) { + throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`); + } else if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + } } diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts index 428ea21db..2bb6541a3 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/string.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts @@ -32,8 +32,8 @@ export class String extends PayloadDataType { const valueBuf = new Buffer(value); const valueBufPadded = ethUtil.setLengthRight(valueBuf, bytesToStoreValuePadded); // 3/3 Combine length and value - const encodedValueBuf = Buffer.concat([lengthBufPadded, valueBufPadded]); - return encodedValueBuf; + const encodedValue = Buffer.concat([lengthBufPadded, valueBufPadded]); + return encodedValue; } public decodeValue(calldata: RawCalldata): string { diff --git a/packages/utils/test/abi_encoder/evm_data_types_test.ts b/packages/utils/test/abi_encoder/evm_data_types_test.ts index 9c3e3c0f9..7cea86529 100644 --- a/packages/utils/test/abi_encoder/evm_data_types_test.ts +++ b/packages/utils/test/abi_encoder/evm_data_types_test.ts @@ -1018,7 +1018,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Encode Args and validate result expect(() => { dataType.encode(args); - }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix. Got '01'"); + }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix."); }); it('Should throw when pass in bad hex (include a half-byte)', async () => { // Create DataType object |