ethers.js/lib.commonjs/abi/coders/abstract-coder.js

270 lines
9.4 KiB
JavaScript
Raw Normal View History

2022-09-05 23:57:11 +03:00
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Reader = exports.Writer = exports.Coder = exports.checkResultErrors = exports.Result = exports.WordSize = void 0;
2022-09-16 05:58:45 +03:00
const index_js_1 = require("../../utils/index.js");
2022-09-05 23:57:11 +03:00
exports.WordSize = 32;
const Padding = new Uint8Array(exports.WordSize);
// Properties used to immediate pass through to the underlying object
// - `then` is used to detect if an object is a Promise for await
const passProperties = ["then"];
const _guard = {};
class Result extends Array {
#indices;
constructor(guard, items, keys) {
2022-09-16 05:58:45 +03:00
(0, index_js_1.assertPrivate)(guard, _guard, "Result");
2022-09-05 23:57:11 +03:00
super(...items);
// Name lookup table
this.#indices = new Map();
if (keys) {
keys.forEach((key, index) => {
if (key == null) {
return;
}
if (this.#indices.has(key)) {
(this.#indices.get(key)).push(index);
}
else {
this.#indices.set(key, [index]);
}
});
}
Object.freeze(this);
return new Proxy(this, {
get: (target, prop, receiver) => {
if (typeof (prop) === "string") {
if (prop.match(/^[0-9]+$/)) {
2022-09-16 05:58:45 +03:00
const index = (0, index_js_1.getNumber)(prop, "%index");
2022-09-05 23:57:11 +03:00
if (index < 0 || index >= this.length) {
throw new RangeError("out of result range");
}
const item = target[index];
if (item instanceof Error) {
this.#throwError(`index ${index}`, item);
}
return item;
}
// Pass important checks (like `then` for Promise) through
if (prop in target || passProperties.indexOf(prop) >= 0) {
return Reflect.get(target, prop, receiver);
}
// Something that could be a result keyword value
if (!(prop in target)) {
return target.getValue(prop);
}
}
return Reflect.get(target, prop, receiver);
}
});
}
/*
toJSON(): any {
if (this.#indices.length === this.length) {
const result: Record<string, any> = { };
for (const key of this.#indices.keys()) {
result[key] = ths.getValue(key);
}
return result;
}
return this;
}
*/
slice(start, end) {
if (start == null) {
start = 0;
}
if (end == null) {
end = this.length;
}
const result = [];
for (let i = start; i < end; i++) {
let value;
try {
value = this[i];
}
catch (error) {
value = error.error;
}
result.push(value);
}
return result;
}
#throwError(name, error) {
const wrapped = new Error(`deferred error during ABI decoding triggered accessing ${name}`);
wrapped.error = error;
throw wrapped;
}
getValue(name) {
const index = this.#indices.get(name);
if (index != null && index.length === 1) {
const item = this[index[0]];
if (item instanceof Error) {
this.#throwError(`property ${JSON.stringify(name)}`, item);
}
return item;
}
throw new Error(`no named parameter: ${JSON.stringify(name)}`);
}
static fromItems(items, keys) {
return new Result(_guard, items, keys);
}
}
exports.Result = Result;
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;
}
exports.checkResultErrors = checkResultErrors;
function getValue(value) {
2022-09-16 05:58:45 +03:00
let bytes = (0, index_js_1.toArray)(value);
2022-09-05 23:57:11 +03:00
if (bytes.length > exports.WordSize) {
2022-09-16 05:58:45 +03:00
(0, index_js_1.throwError)("value out-of-bounds", "BUFFER_OVERRUN", {
2022-09-05 23:57:11 +03:00
buffer: bytes,
length: exports.WordSize,
offset: bytes.length
});
}
if (bytes.length !== exports.WordSize) {
2022-09-16 05:58:45 +03:00
bytes = (0, index_js_1.getBytesCopy)((0, index_js_1.concat)([Padding.slice(bytes.length % exports.WordSize), bytes]));
2022-09-05 23:57:11 +03:00
}
return bytes;
}
class Coder {
// The coder name:
// - address, uint256, tuple, array, etc.
name;
// The fully expanded type, including composite types:
// - address, uint256, tuple(address,bytes), uint256[3][4][], etc.
type;
// The localName bound in the signature, in this example it is "baz":
// - tuple(address foo, uint bar) baz
localName;
// Whether this type is dynamic:
// - Dynamic: bytes, string, address[], tuple(boolean[]), etc.
// - Not Dynamic: address, uint256, boolean[3], tuple(address, uint8)
dynamic;
constructor(name, type, localName, dynamic) {
2022-09-16 05:58:45 +03:00
(0, index_js_1.defineProperties)(this, { name, type, localName, dynamic }, {
2022-09-05 23:57:11 +03:00
name: "string", type: "string", localName: "string", dynamic: "boolean"
});
}
_throwError(message, value) {
2022-09-16 05:58:45 +03:00
return (0, index_js_1.throwArgumentError)(message, this.localName, value);
2022-09-05 23:57:11 +03:00
}
}
exports.Coder = Coder;
class Writer {
// An array of WordSize lengthed objects to concatenation
#data;
#dataLength;
constructor() {
this.#data = [];
this.#dataLength = 0;
}
get data() {
2022-09-16 05:58:45 +03:00
return (0, index_js_1.concat)(this.#data);
2022-09-05 23:57:11 +03:00
}
get length() { return this.#dataLength; }
#writeData(data) {
this.#data.push(data);
this.#dataLength += data.length;
return data.length;
}
appendWriter(writer) {
2022-09-16 05:58:45 +03:00
return this.#writeData((0, index_js_1.getBytesCopy)(writer.data));
2022-09-05 23:57:11 +03:00
}
// Arrayish item; pad on the right to *nearest* WordSize
writeBytes(value) {
2022-09-16 05:58:45 +03:00
let bytes = (0, index_js_1.getBytesCopy)(value);
2022-09-05 23:57:11 +03:00
const paddingOffset = bytes.length % exports.WordSize;
if (paddingOffset) {
2022-09-16 05:58:45 +03:00
bytes = (0, index_js_1.getBytesCopy)((0, index_js_1.concat)([bytes, Padding.slice(paddingOffset)]));
2022-09-05 23:57:11 +03:00
}
return this.#writeData(bytes);
}
// Numeric item; pad on the left *to* WordSize
writeValue(value) {
return this.#writeData(getValue(value));
}
// Inserts a numeric place-holder, returning a callback that can
// be used to asjust the value later
writeUpdatableValue() {
const offset = this.#data.length;
this.#data.push(Padding);
this.#dataLength += exports.WordSize;
return (value) => {
this.#data[offset] = getValue(value);
};
}
}
exports.Writer = Writer;
class Reader {
// Allows incomplete unpadded data to be read; otherwise an error
// is raised if attempting to overrun the buffer. This is required
// to deal with an old Solidity bug, in which event data for
// external (not public thoguh) was tightly packed.
allowLoose;
#data;
#offset;
constructor(data, allowLoose) {
2022-09-16 05:58:45 +03:00
(0, index_js_1.defineProperties)(this, { allowLoose: !!allowLoose });
this.#data = (0, index_js_1.getBytesCopy)(data);
2022-09-05 23:57:11 +03:00
this.#offset = 0;
}
2022-09-16 05:58:45 +03:00
get data() { return (0, index_js_1.hexlify)(this.#data); }
2022-09-05 23:57:11 +03:00
get dataLength() { return this.#data.length; }
get consumed() { return this.#offset; }
get bytes() { return new Uint8Array(this.#data); }
#peekBytes(offset, length, loose) {
let alignedLength = Math.ceil(length / exports.WordSize) * exports.WordSize;
if (this.#offset + alignedLength > this.#data.length) {
if (this.allowLoose && loose && this.#offset + length <= this.#data.length) {
alignedLength = length;
}
else {
2022-09-16 05:58:45 +03:00
(0, index_js_1.throwError)("data out-of-bounds", "BUFFER_OVERRUN", {
buffer: (0, index_js_1.getBytesCopy)(this.#data),
2022-09-05 23:57:11 +03:00
length: this.#data.length,
offset: this.#offset + alignedLength
});
}
}
return this.#data.slice(this.#offset, this.#offset + alignedLength);
}
// Create a sub-reader with the same underlying data, but offset
subReader(offset) {
return new Reader(this.#data.slice(this.#offset + offset), this.allowLoose);
}
// Read bytes
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);
}
// Read a numeric values
readValue() {
2022-09-16 05:58:45 +03:00
return (0, index_js_1.toBigInt)(this.readBytes(exports.WordSize));
2022-09-05 23:57:11 +03:00
}
readIndex() {
2022-09-16 05:58:45 +03:00
return (0, index_js_1.toNumber)(this.readBytes(exports.WordSize));
2022-09-05 23:57:11 +03:00
}
}
exports.Reader = Reader;
//# sourceMappingURL=abstract-coder.js.map