aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Hysen <greg.hysen@gmail.com>2018-11-26 09:04:50 +0800
committerGreg Hysen <greg.hysen@gmail.com>2018-11-29 08:38:11 +0800
commit58a2dfbc4d191ea21e6a749371e586dcff3b3239 (patch)
tree848b6be9277a10144d8e2c1fe5350abfb48b3e5a
parent173fc1dcefa266704dd80de6335c03b73b7d8702 (diff)
downloaddexon-0x-contracts-58a2dfbc4d191ea21e6a749371e586dcff3b3239.tar
dexon-0x-contracts-58a2dfbc4d191ea21e6a749371e586dcff3b3239.tar.gz
dexon-0x-contracts-58a2dfbc4d191ea21e6a749371e586dcff3b3239.tar.bz2
dexon-0x-contracts-58a2dfbc4d191ea21e6a749371e586dcff3b3239.tar.lz
dexon-0x-contracts-58a2dfbc4d191ea21e6a749371e586dcff3b3239.tar.xz
dexon-0x-contracts-58a2dfbc4d191ea21e6a749371e586dcff3b3239.tar.zst
dexon-0x-contracts-58a2dfbc4d191ea21e6a749371e586dcff3b3239.zip
Final rounds on evm data types
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts58
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/string.ts33
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/tuple.ts8
-rw-r--r--packages/utils/src/abi_encoder/utils/queue.ts2
4 files changed, 59 insertions, 42 deletions
diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts
index 9a2a99ec7..afa9afdf2 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts
@@ -11,7 +11,6 @@ export class StaticBytes extends PayloadDataType {
private static readonly _matcher = RegExp(
'^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$',
);
-
private static readonly _DEFAULT_WIDTH = 1;
private readonly _width: number;
@@ -19,16 +18,20 @@ export class StaticBytes extends PayloadDataType {
return StaticBytes._matcher.test(type);
}
+ private static _decodeWidthFromType(type: string): number {
+ const matches = StaticBytes._matcher.exec(type);
+ const width = (matches !== null && matches.length === 3 && matches[2] !== undefined)
+ ? parseInt(matches[2], Constants.DEC_BASE)
+ : StaticBytes._DEFAULT_WIDTH;
+ return width;
+ }
+
public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) {
super(dataItem, dataTypeFactory, StaticBytes._SIZE_KNOWN_AT_COMPILE_TIME);
- const matches = StaticBytes._matcher.exec(dataItem.type);
if (!StaticBytes.matchType(dataItem.type)) {
- throw new Error(`Tried to instantiate Byte with bad input: ${dataItem}`);
+ throw new Error(`Tried to instantiate Static Bytes with bad input: ${dataItem}`);
}
- this._width =
- matches !== null && matches.length === 3 && matches[2] !== undefined
- ? parseInt(matches[2], Constants.DEC_BASE)
- : StaticBytes._DEFAULT_WIDTH;
+ this._width = StaticBytes._decodeWidthFromType(dataItem.type);
}
public getSignature(): string {
@@ -37,11 +40,30 @@ export class StaticBytes extends PayloadDataType {
}
public encodeValue(value: string | Buffer): Buffer {
- // Sanity check if string
- if (typeof value === 'string' && !value.startsWith('0x')) {
- throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`);
+ // 1/2 Convert value into a buffer and do bounds checking
+ this._sanityCheckValue(value);
+ const valueBuf = ethUtil.toBuffer(value);
+ // 2/2 Store value as hex
+ const valuePadded = ethUtil.setLengthRight(valueBuf, Constants.EVM_WORD_WIDTH_IN_BYTES);
+ return valuePadded;
+ }
+
+ public decodeValue(calldata: RawCalldata): string {
+ const valueBufPadded = calldata.popWord();
+ const valueBuf = valueBufPadded.slice(0, this._width);
+ const value = ethUtil.bufferToHex(valueBuf);
+ this._sanityCheckValue(value);
+ return value;
+ }
+
+ private _sanityCheckValue(value: string | Buffer): void {
+ if (typeof value === 'string') {
+ 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.`);
+ }
}
- // Convert value into a buffer and do bounds checking
const valueBuf = ethUtil.toBuffer(value);
if (valueBuf.byteLength > this._width) {
throw new Error(
@@ -49,20 +71,6 @@ export class StaticBytes extends PayloadDataType {
valueBuf.byteLength
} bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`,
);
- } else if (value.length % 2 !== 0) {
- throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`);
}
-
- // Store value as hex
- const evmWordWidth = 32;
- const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth);
- return paddedValue;
- }
-
- public decodeValue(calldata: RawCalldata): string {
- const paddedValueBuf = calldata.popWord();
- const valueBuf = paddedValueBuf.slice(0, this._width);
- const value = ethUtil.bufferToHex(valueBuf);
- return value;
}
}
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 47ad7cb97..15b93e447 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/string.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts
@@ -13,7 +13,7 @@ export class String extends PayloadDataType {
public static matchType(type: string): boolean {
return type === 'string';
}
-
+
public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) {
super(dataItem, dataTypeFactory, String._SIZE_KNOWN_AT_COMPILE_TIME);
if (!String.matchType(dataItem.type)) {
@@ -22,21 +22,30 @@ export class String extends PayloadDataType {
}
public encodeValue(value: string): Buffer {
- const wordsForValue = Math.ceil(value.length / Constants.EVM_WORD_WIDTH_IN_BYTES);
- const paddedDynamicBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES;
- const valueBuf = ethUtil.setLengthRight(new Buffer(value), paddedDynamicBytesForValue);
- const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), Constants.EVM_WORD_WIDTH_IN_BYTES);
- const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]);
+ // Encoded value is of the form: <length><value>, with each field padded to be word-aligned.
+ // 1/3 Construct the length
+ const wordsToStoreValuePadded = Math.ceil(value.length / Constants.EVM_WORD_WIDTH_IN_BYTES);
+ const bytesToStoreValuePadded = wordsToStoreValuePadded * Constants.EVM_WORD_WIDTH_IN_BYTES;
+ const lengthBuf = ethUtil.toBuffer(value.length);
+ const lengthBufPadded = ethUtil.setLengthLeft(lengthBuf, Constants.EVM_WORD_WIDTH_IN_BYTES);
+ // 2/3 Construct the value
+ 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;
}
public decodeValue(calldata: RawCalldata): string {
- 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);
+ // Encoded value is of the form: <length><value>, with each field padded to be word-aligned.
+ // 1/2 Decode length
+ const lengthBufPadded = calldata.popWord();
+ const lengthHexPadded = ethUtil.bufferToHex(lengthBufPadded);
+ const length = parseInt(lengthHexPadded, Constants.HEX_BASE);
+ // 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 = valueBuf.toString('ascii');
return value;
}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts
index 4a90e375a..89dd6604d 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts
@@ -3,8 +3,8 @@ import { DataItem } from 'ethereum-types';
import { DataTypeFactory, MemberDataType } from '../abstract_data_types';
export class Tuple extends MemberDataType {
- private readonly _tupleSignature: string;
-
+ private readonly _signature: string;
+
public static matchType(type: string): boolean {
return type === 'tuple';
}
@@ -14,10 +14,10 @@ export class Tuple extends MemberDataType {
if (!Tuple.matchType(dataItem.type)) {
throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`);
}
- this._tupleSignature = this._computeSignatureOfMembers();
+ this._signature = this._computeSignatureOfMembers();
}
public getSignature(): string {
- return this._tupleSignature;
+ return this._signature;
}
}
diff --git a/packages/utils/src/abi_encoder/utils/queue.ts b/packages/utils/src/abi_encoder/utils/queue.ts
index 53afb7e11..506a0b56e 100644
--- a/packages/utils/src/abi_encoder/utils/queue.ts
+++ b/packages/utils/src/abi_encoder/utils/queue.ts
@@ -32,7 +32,7 @@ export class Queue<T> {
public getStore(): T[] {
return this._store;
}
-
+
public peekFront(): T | undefined {
return this._store.length >= 0 ? this._store[0] : undefined;
}