From 4e394fc68019445ae4b4e201e41f95d6793dbe92 Mon Sep 17 00:00:00 2001 From: Richard Moore Date: Mon, 7 Sep 2020 19:35:37 -0400 Subject: [PATCH] Allow events to use compact bytes ABI coded data for Solidity 0.4 external events (#891, #992). --- packages/abi/src.ts/abi-coder.ts | 8 +++---- packages/abi/src.ts/coders/abstract-coder.ts | 24 ++++++++++++-------- packages/abi/src.ts/coders/array.ts | 8 ------- packages/abi/src.ts/coders/bytes.ts | 2 +- packages/abi/src.ts/interface.ts | 2 +- 5 files changed, 21 insertions(+), 23 deletions(-) diff --git a/packages/abi/src.ts/abi-coder.ts b/packages/abi/src.ts/abi-coder.ts index 0c29405dbf..2161ed8fae 100644 --- a/packages/abi/src.ts/abi-coder.ts +++ b/packages/abi/src.ts/abi-coder.ts @@ -83,8 +83,8 @@ export class AbiCoder { _getWordSize(): number { return 32; } - _getReader(data: Uint8Array): Reader { - return new Reader(data, this._getWordSize(), this.coerceFunc); + _getReader(data: Uint8Array, allowLoose?: boolean): Reader { + return new Reader(data, this._getWordSize(), this.coerceFunc, allowLoose); } _getWriter(): Writer { @@ -107,10 +107,10 @@ export class AbiCoder { return writer.data; } - decode(types: Array, data: BytesLike): Result { + decode(types: Array, data: BytesLike, loose?: boolean): Result { const coders: Array = types.map((type) => this._getCoder(ParamType.from(type))); const coder = new TupleCoder(coders, "_"); - return coder.decode(this._getReader(arrayify(data))); + return coder.decode(this._getReader(arrayify(data), loose)); } } diff --git a/packages/abi/src.ts/coders/abstract-coder.ts b/packages/abi/src.ts/coders/abstract-coder.ts index e6df42d088..c1d4f17a57 100644 --- a/packages/abi/src.ts/coders/abstract-coder.ts +++ b/packages/abi/src.ts/coders/abstract-coder.ts @@ -131,16 +131,18 @@ export class Writer { export class Reader { readonly wordSize: number; + readonly allowLoose: boolean; readonly _data: Uint8Array; readonly _coerceFunc: CoerceFunc; _offset: number; - constructor(data: BytesLike, wordSize?: number, coerceFunc?: CoerceFunc) { + constructor(data: BytesLike, wordSize?: number, coerceFunc?: CoerceFunc, allowLoose?: boolean) { defineReadOnly(this, "_data", arrayify(data)); defineReadOnly(this, "wordSize", wordSize || 32); defineReadOnly(this, "_coerceFunc", coerceFunc); + defineReadOnly(this, "allowLoose", allowLoose); this._offset = 0; } @@ -160,23 +162,27 @@ export class Reader { return Reader.coerce(name, value); } - _peekBytes(offset: number, length: number): Uint8Array { + _peekBytes(offset: number, length: number, loose?: boolean): Uint8Array { let alignedLength = Math.ceil(length / this.wordSize) * this.wordSize; if (this._offset + alignedLength > this._data.length) { - logger.throwError("data out-of-bounds", Logger.errors.BUFFER_OVERRUN, { - length: this._data.length, - offset: this._offset + alignedLength - }); + if (this.allowLoose && loose && this._offset + length <= this._data.length) { + alignedLength = length; + } else { + logger.throwError("data out-of-bounds", Logger.errors.BUFFER_OVERRUN, { + length: this._data.length, + offset: this._offset + alignedLength + }); + } } return this._data.slice(this._offset, this._offset + alignedLength) } subReader(offset: number): Reader { - return new Reader(this._data.slice(this._offset + offset), this.wordSize, this._coerceFunc); + return new Reader(this._data.slice(this._offset + offset), this.wordSize, this._coerceFunc, this.allowLoose); } - readBytes(length: number): Uint8Array { - let bytes = this._peekBytes(0, length); + readBytes(length: number, loose?: boolean): Uint8Array { + let bytes = this._peekBytes(0, length, !!loose); this._offset += bytes.length; // @TODO: Make sure the length..end bytes are all 0? return bytes.slice(0, length); diff --git a/packages/abi/src.ts/coders/array.ts b/packages/abi/src.ts/coders/array.ts index 0437294a54..0a62b8c108 100644 --- a/packages/abi/src.ts/coders/array.ts +++ b/packages/abi/src.ts/coders/array.ts @@ -86,9 +86,6 @@ export function unpack(reader: Reader, coders: Array): Result { // A reader anchored to this base let baseReader = reader.subReader(0); - // The amount of dynamic data read; to consume later to synchronize - let dynamicLength = 0; - coders.forEach((coder) => { let value: any = null; @@ -105,7 +102,6 @@ export function unpack(reader: Reader, coders: Array): Result { value.name = coder.localName; value.type = coder.type; } - dynamicLength += offsetReader.consumed; } else { try { @@ -125,10 +121,6 @@ export function unpack(reader: Reader, coders: Array): Result { } }); -// @TODO: get rid of this an see if it still works? - // Consume the dynamic components in the main reader - reader.readBytes(dynamicLength); - // We only output named properties for uniquely named coders const uniqueNames = coders.reduce((accum, coder) => { const name = coder.localName; diff --git a/packages/abi/src.ts/coders/bytes.ts b/packages/abi/src.ts/coders/bytes.ts index a2d8ce43c6..7534fa37d2 100644 --- a/packages/abi/src.ts/coders/bytes.ts +++ b/packages/abi/src.ts/coders/bytes.ts @@ -17,7 +17,7 @@ export class DynamicBytesCoder extends Coder { } decode(reader: Reader): any { - return reader.readBytes(reader.readValue().toNumber()); + return reader.readBytes(reader.readValue().toNumber(), true); } } diff --git a/packages/abi/src.ts/interface.ts b/packages/abi/src.ts/interface.ts index 044b014c03..4276e001bd 100644 --- a/packages/abi/src.ts/interface.ts +++ b/packages/abi/src.ts/interface.ts @@ -475,7 +475,7 @@ export class Interface { }); let resultIndexed = (topics != null) ? this._abiCoder.decode(indexed, concat(topics)): null; - let resultNonIndexed = this._abiCoder.decode(nonIndexed, data); + let resultNonIndexed = this._abiCoder.decode(nonIndexed, data, true); let result: (Array & { [ key: string ]: any }) = [ ]; let nonIndexedIndex = 0, indexedIndex = 0;