ethers.js/packages/abi/lib.esm/coders/abstract-coder.js
2020-10-05 15:46:12 -04:00

147 lines
4.9 KiB
JavaScript

"use strict";
import { arrayify, concat, hexConcat, hexlify } from "@ethersproject/bytes";
import { BigNumber } from "@ethersproject/bignumber";
import { defineReadOnly } from "@ethersproject/properties";
import { Logger } from "@ethersproject/logger";
import { version } from "../_version";
const logger = new Logger(version);
export function checkResultErrors(result) {
// Find the first error (if any)
const errors = [];
const checkErrors = function (path, object) {
if (!Array.isArray(object)) {
return;
}
for (let key in object) {
const childPath = path.slice();
childPath.push(key);
try {
checkErrors(childPath, object[key]);
}
catch (error) {
errors.push({ path: childPath, error: error });
}
}
};
checkErrors([], result);
return errors;
}
export class Coder {
constructor(name, type, localName, dynamic) {
// @TODO: defineReadOnly these
this.name = name;
this.type = type;
this.localName = localName;
this.dynamic = dynamic;
}
_throwError(message, value) {
logger.throwArgumentError(message, this.localName, value);
}
}
export class Writer {
constructor(wordSize) {
defineReadOnly(this, "wordSize", wordSize || 32);
this._data = [];
this._dataLength = 0;
this._padding = new Uint8Array(wordSize);
}
get data() {
return hexConcat(this._data);
}
get length() { return this._dataLength; }
_writeData(data) {
this._data.push(data);
this._dataLength += data.length;
return data.length;
}
appendWriter(writer) {
return this._writeData(concat(writer._data));
}
// Arrayish items; padded on the right to wordSize
writeBytes(value) {
let bytes = arrayify(value);
const paddingOffset = bytes.length % this.wordSize;
if (paddingOffset) {
bytes = concat([bytes, this._padding.slice(paddingOffset)]);
}
return this._writeData(bytes);
}
_getValue(value) {
let bytes = arrayify(BigNumber.from(value));
if (bytes.length > this.wordSize) {
logger.throwError("value out-of-bounds", Logger.errors.BUFFER_OVERRUN, {
length: this.wordSize,
offset: bytes.length
});
}
if (bytes.length % this.wordSize) {
bytes = concat([this._padding.slice(bytes.length % this.wordSize), bytes]);
}
return bytes;
}
// BigNumberish items; padded on the left to wordSize
writeValue(value) {
return this._writeData(this._getValue(value));
}
writeUpdatableValue() {
const offset = this._data.length;
this._data.push(this._padding);
this._dataLength += this.wordSize;
return (value) => {
this._data[offset] = this._getValue(value);
};
}
}
export class Reader {
constructor(data, wordSize, coerceFunc, allowLoose) {
defineReadOnly(this, "_data", arrayify(data));
defineReadOnly(this, "wordSize", wordSize || 32);
defineReadOnly(this, "_coerceFunc", coerceFunc);
defineReadOnly(this, "allowLoose", allowLoose);
this._offset = 0;
}
get data() { return hexlify(this._data); }
get consumed() { return this._offset; }
// The default Coerce function
static coerce(name, value) {
let match = name.match("^u?int([0-9]+)$");
if (match && parseInt(match[1]) <= 48) {
value = value.toNumber();
}
return value;
}
coerce(name, value) {
if (this._coerceFunc) {
return this._coerceFunc(name, value);
}
return Reader.coerce(name, value);
}
_peekBytes(offset, length, loose) {
let alignedLength = Math.ceil(length / this.wordSize) * this.wordSize;
if (this._offset + alignedLength > this._data.length) {
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) {
return new Reader(this._data.slice(this._offset + offset), this.wordSize, this._coerceFunc, this.allowLoose);
}
readBytes(length, loose) {
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);
}
readValue() {
return BigNumber.from(this.readBytes(this.wordSize));
}
}
//# sourceMappingURL=abstract-coder.js.map