docs: added jsdocs to abi
This commit is contained in:
parent
16f3c44daa
commit
b7750cf098
@ -1,3 +1,16 @@
|
||||
/**
|
||||
* When sending values to or receiving values from a [[Contract]], the
|
||||
* data is generally encoded using the [ABI standard](solc-abi-standard).
|
||||
*
|
||||
* The AbiCoder provides a utility to encode values to ABI data and
|
||||
* decode values from ABI data.
|
||||
*
|
||||
* Most of the time, developers should favour the [[Contract]] class,
|
||||
* which further abstracts a lot of the finer details of ABI data.
|
||||
*
|
||||
* @_section api/abi/abi-coder:ABI Encoding
|
||||
*/
|
||||
|
||||
// See: https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
|
||||
|
||||
import { assertArgumentCount, assertArgument } from "../utils/index.js";
|
||||
@ -22,10 +35,92 @@ import type {
|
||||
CallExceptionAction, CallExceptionError, CallExceptionTransaction
|
||||
} from "../utils/index.js";
|
||||
|
||||
// https://docs.soliditylang.org/en/v0.8.17/control-structures.html
|
||||
const PanicReasons: Map<number, string> = new Map();
|
||||
PanicReasons.set(0x00, "GENERIC_PANIC");
|
||||
PanicReasons.set(0x01, "ASSERT_FALSE");
|
||||
PanicReasons.set(0x11, "OVERFLOW");
|
||||
PanicReasons.set(0x12, "DIVIDE_BY_ZERO");
|
||||
PanicReasons.set(0x21, "ENUM_RANGE_ERROR");
|
||||
PanicReasons.set(0x22, "BAD_STORAGE_DATA");
|
||||
PanicReasons.set(0x31, "STACK_UNDERFLOW");
|
||||
PanicReasons.set(0x32, "ARRAY_RANGE_ERROR");
|
||||
PanicReasons.set(0x41, "OUT_OF_MEMORY");
|
||||
PanicReasons.set(0x51, "UNINITIALIZED_FUNCTION_CALL");
|
||||
|
||||
const paramTypeBytes = new RegExp(/^bytes([0-9]*)$/);
|
||||
const paramTypeNumber = new RegExp(/^(u?int)([0-9]*)$/);
|
||||
|
||||
|
||||
let defaultCoder: null | AbiCoder = null;
|
||||
|
||||
|
||||
function getBuiltinCallException(action: CallExceptionAction, tx: { to?: null | string, from?: null | string, data?: string }, data: null | BytesLike, abiCoder: AbiCoder): CallExceptionError {
|
||||
let message = "missing revert data";
|
||||
|
||||
let reason: null | string = null;
|
||||
const invocation = null;
|
||||
let revert: null | { signature: string, name: string, args: Array<any> } = null;
|
||||
|
||||
if (data) {
|
||||
message = "execution reverted";
|
||||
|
||||
const bytes = getBytes(data);
|
||||
data = hexlify(data);
|
||||
|
||||
if (bytes.length % 32 !== 4) {
|
||||
message += " (could not decode reason; invalid data length)";
|
||||
|
||||
} else if (hexlify(bytes.slice(0, 4)) === "0x08c379a0") {
|
||||
// Error(string)
|
||||
try {
|
||||
reason = abiCoder.decode([ "string" ], bytes.slice(4))[0]
|
||||
revert = {
|
||||
signature: "Error(string)",
|
||||
name: "Error",
|
||||
args: [ reason ]
|
||||
};
|
||||
message += `: ${ JSON.stringify(reason) }`;
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
message += " (could not decode reason; invalid data)";
|
||||
}
|
||||
|
||||
} else if (hexlify(bytes.slice(0, 4)) === "0x4e487b71") {
|
||||
// Panic(uint256)
|
||||
try {
|
||||
const code = Number(abiCoder.decode([ "uint256" ], bytes.slice(4))[0]);
|
||||
revert = {
|
||||
signature: "Panic(uint256)",
|
||||
name: "Panic",
|
||||
args: [ code ]
|
||||
};
|
||||
reason = `Panic due to ${ PanicReasons.get(code) || "UNKNOWN" }(${ code })`;
|
||||
message += `: ${ reason }`;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
message += " (could not decode panic reason)";
|
||||
}
|
||||
} else {
|
||||
message += " (unknown custom error)";
|
||||
}
|
||||
}
|
||||
|
||||
const transaction: CallExceptionTransaction = {
|
||||
to: (tx.to ? getAddress(tx.to): null),
|
||||
data: (tx.data || "0x")
|
||||
};
|
||||
if (tx.from) { transaction.from = getAddress(tx.from); }
|
||||
|
||||
return makeError(message, "CALL_EXCEPTION", {
|
||||
action, data, reason, transaction, invocation, revert
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* About AbiCoder
|
||||
*/
|
||||
export class AbiCoder {
|
||||
|
||||
#getCoder(param: ParamType): Coder {
|
||||
@ -70,12 +165,23 @@ export class AbiCoder {
|
||||
assertArgument(false, "invalid type", "type", param.type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default values for the given %%types%%.
|
||||
*
|
||||
* For example, a ``uint`` is by default ``0`` and ``bool``
|
||||
* is by default ``false``.
|
||||
*/
|
||||
getDefaultValue(types: ReadonlyArray<string | ParamType>): Result {
|
||||
const coders: Array<Coder> = types.map((type) => this.#getCoder(ParamType.from(type)));
|
||||
const coder = new TupleCoder(coders, "_");
|
||||
return coder.defaultValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the %%values%% as the %%types%% into ABI data.
|
||||
*
|
||||
* @returns DataHexstring
|
||||
*/
|
||||
encode(types: ReadonlyArray<string | ParamType>, values: ReadonlyArray<any>): string {
|
||||
assertArgumentCount(values.length, types.length, "types/values length mismatch");
|
||||
|
||||
@ -87,87 +193,37 @@ export class AbiCoder {
|
||||
return writer.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the ABI %%data%% as the %%types%% into values.
|
||||
*
|
||||
* If %%loose%% decoding is enabled, then strict padding is
|
||||
* not enforced. Some older versions of Solidity incorrectly
|
||||
* padded event data emitted from ``external`` functions.
|
||||
*/
|
||||
decode(types: ReadonlyArray<string | ParamType>, data: BytesLike, loose?: boolean): Result {
|
||||
const coders: Array<Coder> = types.map((type) => this.#getCoder(ParamType.from(type)));
|
||||
const coder = new TupleCoder(coders, "_");
|
||||
return coder.decode(new Reader(data, loose));
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.soliditylang.org/en/v0.8.17/control-structures.html
|
||||
const PanicReasons: Map<number, string> = new Map();
|
||||
PanicReasons.set(0x00, "GENERIC_PANIC");
|
||||
PanicReasons.set(0x01, "ASSERT_FALSE");
|
||||
PanicReasons.set(0x11, "OVERFLOW");
|
||||
PanicReasons.set(0x12, "DIVIDE_BY_ZERO");
|
||||
PanicReasons.set(0x21, "ENUM_RANGE_ERROR");
|
||||
PanicReasons.set(0x22, "BAD_STORAGE_DATA");
|
||||
PanicReasons.set(0x31, "STACK_UNDERFLOW");
|
||||
PanicReasons.set(0x32, "ARRAY_RANGE_ERROR");
|
||||
PanicReasons.set(0x41, "OUT_OF_MEMORY");
|
||||
PanicReasons.set(0x51, "UNINITIALIZED_FUNCTION_CALL");
|
||||
|
||||
export function getBuiltinCallException(action: CallExceptionAction, tx: { to?: null | string, from?: null | string, data?: string }, data: null | BytesLike): CallExceptionError {
|
||||
let message = "missing revert data";
|
||||
|
||||
let reason: null | string = null;
|
||||
const invocation = null;
|
||||
let revert: null | { signature: string, name: string, args: Array<any> } = null;
|
||||
|
||||
if (data) {
|
||||
message = "execution reverted";
|
||||
|
||||
const bytes = getBytes(data);
|
||||
data = hexlify(data);
|
||||
|
||||
if (bytes.length % 32 !== 4) {
|
||||
message += " (could not decode reason; invalid data length)";
|
||||
|
||||
} else if (hexlify(bytes.slice(0, 4)) === "0x08c379a0") {
|
||||
// Error(string)
|
||||
try {
|
||||
reason = defaultAbiCoder.decode([ "string" ], bytes.slice(4))[0]
|
||||
revert = {
|
||||
signature: "Error(string)",
|
||||
name: "Error",
|
||||
args: [ reason ]
|
||||
};
|
||||
message += `: ${ JSON.stringify(reason) }`;
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
message += " (could not decode reason; invalid data)";
|
||||
}
|
||||
|
||||
} else if (hexlify(bytes.slice(0, 4)) === "0x4e487b71") {
|
||||
// Panic(uint256)
|
||||
try {
|
||||
const code = Number(defaultAbiCoder.decode([ "uint256" ], bytes.slice(4))[0]);
|
||||
revert = {
|
||||
signature: "Panic(uint256)",
|
||||
name: "Panic",
|
||||
args: [ code ]
|
||||
};
|
||||
reason = `Panic due to ${ PanicReasons.get(code) || "UNKNOWN" }(${ code })`;
|
||||
message += `: ${ reason }`;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
message += " (could not decode panic reason)";
|
||||
}
|
||||
} else {
|
||||
message += " (unknown custom error)";
|
||||
/**
|
||||
* Returns the shared singleton instance of a default [[AbiCoder]].
|
||||
*
|
||||
* On the first call, the instance is created internally.
|
||||
*/
|
||||
static defaultAbiCoder(): AbiCoder {
|
||||
if (defaultCoder == null) {
|
||||
defaultCoder = new AbiCoder();
|
||||
}
|
||||
return defaultCoder;
|
||||
}
|
||||
|
||||
const transaction: CallExceptionTransaction = {
|
||||
to: (tx.to ? getAddress(tx.to): null),
|
||||
data: (tx.data || "0x")
|
||||
};
|
||||
if (tx.from) { transaction.from = getAddress(tx.from); }
|
||||
|
||||
return makeError(message, "CALL_EXCEPTION", {
|
||||
action, data, reason, transaction, invocation, revert
|
||||
});
|
||||
/**
|
||||
* Returns an ethers-compatible [[CALL_EXCEPTION]] Error for the given
|
||||
* result %%data%% for the [[CallExceptionAction]] %%action%% against
|
||||
* the Transaction %%tx%%.
|
||||
*/
|
||||
static getBuiltinCallException(action: CallExceptionAction, tx: { to?: null | string, from?: null | string, data?: string }, data: null | BytesLike): CallExceptionError {
|
||||
return getBuiltinCallException(action, tx, data, AbiCoder.defaultAbiCoder());
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultAbiCoder: AbiCoder = new AbiCoder();
|
||||
|
@ -1,3 +1,8 @@
|
||||
/**
|
||||
* About bytes32 strings...
|
||||
*
|
||||
* @_docloc: api/utils:Bytes32 Strings
|
||||
*/
|
||||
|
||||
import {
|
||||
getBytes, toUtf8Bytes, toUtf8String, zeroPadBytes
|
||||
@ -5,7 +10,9 @@ import {
|
||||
|
||||
import type { BytesLike } from "../utils/index.js";
|
||||
|
||||
|
||||
/**
|
||||
* Encodes %%text%% as a Bytes32 string.
|
||||
*/
|
||||
export function encodeBytes32String(text: string): string {
|
||||
|
||||
// Get the bytes
|
||||
@ -18,6 +25,9 @@ export function encodeBytes32String(text: string): string {
|
||||
return zeroPadBytes(bytes, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the Bytes32-encoded %%bytes%% into a string.
|
||||
*/
|
||||
export function decodeBytes32String(_bytes: BytesLike): string {
|
||||
const data = getBytes(_bytes, "bytes");
|
||||
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
|
||||
import type { BigNumberish, BytesLike } from "../../utils/index.js";
|
||||
|
||||
export const WordSize = 32;
|
||||
export const WordSize: number = 32;
|
||||
const Padding = new Uint8Array(WordSize);
|
||||
|
||||
// Properties used to immediate pass through to the underlying object
|
||||
@ -16,11 +16,21 @@ const passProperties = [ "then" ];
|
||||
|
||||
const _guard = { };
|
||||
|
||||
/**
|
||||
* A [[Result]] is a sub-class of Array, which allows accessing any
|
||||
* of its values either positionally by its index or, if keys are
|
||||
* provided by its name.
|
||||
*
|
||||
* @_docloc: api/abi
|
||||
*/
|
||||
export class Result extends Array<any> {
|
||||
#indices: Map<string, Array<number>>;
|
||||
|
||||
[ K: string | number ]: any
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
constructor(guard: any, items: Array<any>, keys?: Array<null | string>) {
|
||||
assertPrivate(guard, _guard, "Result");
|
||||
super(...items);
|
||||
@ -85,6 +95,9 @@ export class Result extends Array<any> {
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
slice(start?: number | undefined, end?: number | undefined): Array<any> {
|
||||
if (start == null) { start = 0; }
|
||||
if (end == null) { end = this.length; }
|
||||
@ -108,6 +121,14 @@ export class Result extends Array<any> {
|
||||
throw wrapped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value for %%name%%.
|
||||
*
|
||||
* Since it is possible to have a key whose name conflicts with
|
||||
* a method on a [[Result]] or its superclass Array, or any
|
||||
* JavaScript keyword, this ensures all named values are still
|
||||
* accessible by name.
|
||||
*/
|
||||
getValue(name: string): any {
|
||||
const index = this.#indices.get(name);
|
||||
if (index != null && index.length === 1) {
|
||||
@ -121,11 +142,28 @@ export class Result extends Array<any> {
|
||||
throw new Error(`no named parameter: ${ JSON.stringify(name) }`);
|
||||
}
|
||||
|
||||
static fromItems(items: Array<any>, keys?: Array<null | string>) {
|
||||
/**
|
||||
* Creates a new [[Result]] for %%items%% with each entry
|
||||
* also accessible by its corresponding name in %%keys%%.
|
||||
*/
|
||||
static fromItems(items: Array<any>, keys?: Array<null | string>): Result {
|
||||
return new Result(_guard, items, keys);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all errors found in a [[Result]].
|
||||
*
|
||||
* Since certain errors encountered when creating a [[Result]] do
|
||||
* not impact the ability to continue parsing data, they are
|
||||
* deferred until they are actually accessed. Hence a faulty string
|
||||
* in an Event that is never used does not impact the program flow.
|
||||
*
|
||||
* However, sometimes it may be useful to access, identify or
|
||||
* validate correctness of a [[Result]].
|
||||
*
|
||||
* @_docloc api/abi
|
||||
*/
|
||||
export function checkResultErrors(result: Result): Array<{ path: Array<string | number>, error: Error }> {
|
||||
// Find the first error (if any)
|
||||
const errors: Array<{ path: Array<string | number>, error: Error }> = [ ];
|
||||
@ -162,7 +200,9 @@ function getValue(value: BigNumberish): Uint8Array {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export abstract class Coder {
|
||||
|
||||
// The coder name:
|
||||
@ -198,6 +238,9 @@ export abstract class Coder {
|
||||
abstract defaultValue(): any;
|
||||
}
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export class Writer {
|
||||
// An array of WordSize lengthed objects to concatenation
|
||||
#data: Array<Uint8Array>;
|
||||
@ -250,6 +293,9 @@ export class Writer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export class Reader {
|
||||
// Allows incomplete unpadded data to be read; otherwise an error
|
||||
// is raised if attempting to overrun the buffer. This is required
|
||||
|
@ -7,6 +7,9 @@ import { Coder } from "./abstract-coder.js";
|
||||
import type { Reader, Writer } from "./abstract-coder.js";
|
||||
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export class AddressCoder extends Coder {
|
||||
|
||||
constructor(localName: string) {
|
||||
|
@ -2,7 +2,11 @@ import { Coder } from "./abstract-coder.js";
|
||||
|
||||
import type { Reader, Writer } from "./abstract-coder.js";
|
||||
|
||||
// Clones the functionality of an existing Coder, but without a localName
|
||||
/**
|
||||
* Clones the functionality of an existing Coder, but without a localName
|
||||
*
|
||||
* @_ignore
|
||||
*/
|
||||
export class AnonymousCoder extends Coder {
|
||||
private coder: Coder;
|
||||
|
||||
|
@ -9,7 +9,9 @@ import { AnonymousCoder } from "./anonymous.js";
|
||||
|
||||
import type { Reader } from "./abstract-coder.js";
|
||||
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export function pack(writer: Writer, coders: ReadonlyArray<Coder>, values: Array<any> | { [ name: string ]: any }): number {
|
||||
let arrayValues: Array<any> = [ ];
|
||||
|
||||
@ -71,6 +73,9 @@ export function pack(writer: Writer, coders: ReadonlyArray<Coder>, values: Array
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export function unpack(reader: Reader, coders: ReadonlyArray<Coder>): Result {
|
||||
let values: Array<any> = [];
|
||||
let keys: Array<null | string> = [ ];
|
||||
@ -125,7 +130,9 @@ export function unpack(reader: Reader, coders: ReadonlyArray<Coder>): Result {
|
||||
return Result.fromItems(values, keys);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export class ArrayCoder extends Coder {
|
||||
readonly coder!: Coder;
|
||||
readonly length!: number;
|
||||
|
@ -3,7 +3,9 @@ import { Coder } from "./abstract-coder.js";
|
||||
|
||||
import type { Reader, Writer } from "./abstract-coder.js";
|
||||
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export class BooleanCoder extends Coder {
|
||||
|
||||
constructor(localName: string) {
|
||||
|
@ -5,6 +5,9 @@ import { Coder } from "./abstract-coder.js";
|
||||
import type { Reader, Writer } from "./abstract-coder.js";
|
||||
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export class DynamicBytesCoder extends Coder {
|
||||
constructor(type: string, localName: string) {
|
||||
super(type, type, localName, true);
|
||||
@ -26,6 +29,9 @@ export class DynamicBytesCoder extends Coder {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export class BytesCoder extends DynamicBytesCoder {
|
||||
constructor(localName: string) {
|
||||
super("bytes", localName);
|
||||
|
@ -9,6 +9,9 @@ import type { BytesLike } from "../../utils/index.js";
|
||||
import type { Reader, Writer } from "./abstract-coder.js";
|
||||
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export class FixedBytesCoder extends Coder {
|
||||
readonly size!: number;
|
||||
|
||||
|
@ -3,6 +3,9 @@ import type { Reader, Writer } from "./abstract-coder.js";
|
||||
|
||||
const Empty = new Uint8Array([ ]);
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export class NullCoder extends Coder {
|
||||
|
||||
constructor(localName: string) {
|
||||
|
@ -14,6 +14,9 @@ const BN_0 = BigInt(0);
|
||||
const BN_1 = BigInt(1);
|
||||
const BN_MAX_UINT256 = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export class NumberCoder extends Coder {
|
||||
readonly size!: number;
|
||||
readonly signed!: boolean;
|
||||
|
@ -6,6 +6,9 @@ import { DynamicBytesCoder } from "./bytes.js";
|
||||
import type { Reader, Writer } from "./abstract-coder.js";
|
||||
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export class StringCoder extends DynamicBytesCoder {
|
||||
|
||||
constructor(localName: string) {
|
||||
|
@ -7,6 +7,9 @@ import { pack, unpack } from "./array.js";
|
||||
|
||||
import type { Reader, Writer } from "./abstract-coder.js";
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export class TupleCoder extends Coder {
|
||||
readonly coders!: ReadonlyArray<Coder>;
|
||||
|
||||
|
@ -1,3 +1,9 @@
|
||||
/**
|
||||
* About frgaments...
|
||||
*
|
||||
* @_subsection api/abi/abi-coder:Fragments
|
||||
*/
|
||||
|
||||
import {
|
||||
defineProperties, getBigInt, getNumber,
|
||||
assert, assertPrivate, assertArgument
|
||||
@ -460,6 +466,9 @@ export class ParamType {
|
||||
readonly arrayChildren!: null | ParamType;
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
constructor(guard: any, name: string, type: string, baseType: string, indexed: null | boolean, components: null | ReadonlyArray<ParamType>, arrayLength: null | number, arrayChildren: null | ParamType) {
|
||||
assertPrivate(guard, _guard, "ParamType");
|
||||
Object.defineProperty(this, internal, { value: ParamTypeInternal });
|
||||
@ -489,7 +498,8 @@ export class ParamType {
|
||||
// - sighash: "(uint256,address)"
|
||||
// - minimal: "tuple(uint256,address) indexed"
|
||||
// - full: "tuple(uint256 foo, address bar) indexed baz"
|
||||
format(format: FormatType = "sighash"): string {
|
||||
format(format?: FormatType): string {
|
||||
if (format == null) { format = "sighash"; }
|
||||
if (format === "json") {
|
||||
let result: any = {
|
||||
type: ((this.baseType === "tuple") ? "tuple": this.type),
|
||||
@ -629,7 +639,7 @@ export class ParamType {
|
||||
}
|
||||
}
|
||||
|
||||
async walkAsync(value: any, process: (type: string, value: any) => any | Promise<any>): Promise<any> {
|
||||
async walkAsync(value: any, process: FragmentWalkAsyncFunc): Promise<any> {
|
||||
const promises: Array<Promise<void>> = [ ];
|
||||
const result: [ any ] = [ value ];
|
||||
this.#walkAsync(promises, value, process, (value: any) => {
|
||||
@ -733,6 +743,9 @@ export abstract class Fragment {
|
||||
readonly type!: FragmentType;
|
||||
readonly inputs!: ReadonlyArray<ParamType>;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
constructor(guard: any, type: FragmentType, inputs: ReadonlyArray<ParamType>) {
|
||||
assertPrivate(guard, _guard, "Fragment");
|
||||
inputs = Object.freeze(inputs.slice());
|
||||
@ -802,6 +815,9 @@ export abstract class Fragment {
|
||||
export abstract class NamedFragment extends Fragment {
|
||||
readonly name!: string;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
constructor(guard: any, type: FragmentType, name: string, inputs: ReadonlyArray<ParamType>) {
|
||||
super(guard, type, inputs);
|
||||
assertArgument(typeof(name) === "string" && name.match(regexIdentifier),
|
||||
@ -816,6 +832,9 @@ function joinParams(format: FormatType, params: ReadonlyArray<ParamType>): strin
|
||||
}
|
||||
|
||||
export class ErrorFragment extends NamedFragment {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
constructor(guard: any, name: string, inputs: ReadonlyArray<ParamType>) {
|
||||
super(guard, "error", name, inputs);
|
||||
Object.defineProperty(this, internal, { value: ErrorFragmentInternal });
|
||||
@ -825,7 +844,8 @@ export class ErrorFragment extends NamedFragment {
|
||||
return id(this.format("sighash")).substring(0, 10);
|
||||
}
|
||||
|
||||
format(format: FormatType = "sighash"): string {
|
||||
format(format?: FormatType): string {
|
||||
if (format == null) { format = "sighash"; }
|
||||
if (format === "json") {
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
@ -867,6 +887,9 @@ export class ErrorFragment extends NamedFragment {
|
||||
export class EventFragment extends NamedFragment {
|
||||
readonly anonymous!: boolean;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
constructor(guard: any, name: string, inputs: ReadonlyArray<ParamType>, anonymous: boolean) {
|
||||
super(guard, "event", name, inputs);
|
||||
Object.defineProperty(this, internal, { value: EventFragmentInternal });
|
||||
@ -877,7 +900,8 @@ export class EventFragment extends NamedFragment {
|
||||
return id(this.format("sighash"));
|
||||
}
|
||||
|
||||
format(format: FormatType = "sighash"): string {
|
||||
format(format?: FormatType): string {
|
||||
if (format == null) { format = "sighash"; }
|
||||
if (format === "json") {
|
||||
return JSON.stringify({
|
||||
type: "event",
|
||||
@ -923,14 +947,17 @@ export class ConstructorFragment extends Fragment {
|
||||
readonly payable!: boolean;
|
||||
readonly gas!: null | bigint;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
constructor(guard: any, type: FragmentType, inputs: ReadonlyArray<ParamType>, payable: boolean, gas: null | bigint) {
|
||||
super(guard, type, inputs);
|
||||
Object.defineProperty(this, internal, { value: ConstructorFragmentInternal });
|
||||
defineProperties<ConstructorFragment>(this, { payable, gas });
|
||||
}
|
||||
|
||||
format(format: FormatType = "sighash"): string {
|
||||
assert(format !== "sighash", "cannot format a constructor for sighash",
|
||||
format(format?: FormatType): string {
|
||||
assert(format != null && format !== "sighash", "cannot format a constructor for sighash",
|
||||
"UNSUPPORTED_OPERATION", { operation: "format(sighash)" });
|
||||
|
||||
if (format === "json") {
|
||||
@ -983,6 +1010,9 @@ export class FunctionFragment extends NamedFragment {
|
||||
readonly payable!: boolean;
|
||||
readonly gas!: null | bigint;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
constructor(guard: any, name: string, stateMutability: string, inputs: ReadonlyArray<ParamType>, outputs: ReadonlyArray<ParamType>, gas: null | bigint) {
|
||||
super(guard, "function", name, inputs);
|
||||
Object.defineProperty(this, internal, { value: FunctionFragmentInternal });
|
||||
@ -996,7 +1026,8 @@ export class FunctionFragment extends NamedFragment {
|
||||
return id(this.format("sighash")).substring(0, 10);
|
||||
}
|
||||
|
||||
format(format: FormatType = "sighash"): string {
|
||||
format(format?: FormatType): string {
|
||||
if (format == null) { format = "sighash"; }
|
||||
if (format === "json") {
|
||||
return JSON.stringify({
|
||||
type: "function",
|
||||
@ -1068,6 +1099,10 @@ export class FunctionFragment extends NamedFragment {
|
||||
}
|
||||
|
||||
export class StructFragment extends NamedFragment {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
constructor(guard: any, name: string, inputs: ReadonlyArray<ParamType>) {
|
||||
super(guard, "struct", name, inputs);
|
||||
Object.defineProperty(this, internal, { value: StructFragmentInternal });
|
||||
|
@ -1,12 +1,12 @@
|
||||
|
||||
/**
|
||||
* Explain about ABI here...
|
||||
*
|
||||
* @_section api/abi:Application Binary Interface [abi]
|
||||
*/
|
||||
|
||||
|
||||
//////
|
||||
export {
|
||||
AbiCoder,
|
||||
defaultAbiCoder,
|
||||
getBuiltinCallException
|
||||
} from "./abi-coder.js";
|
||||
export { AbiCoder } from "./abi-coder.js";
|
||||
|
||||
export { decodeBytes32String, encodeBytes32String } from "./bytes32.js";
|
||||
|
||||
|
@ -1,3 +1,9 @@
|
||||
/**
|
||||
* About Interface
|
||||
*
|
||||
* @_subsection api/abi:Interfaces [interfaces]
|
||||
*/
|
||||
|
||||
import { keccak256 } from "../crypto/index.js"
|
||||
import { id } from "../hash/index.js"
|
||||
import {
|
||||
@ -6,7 +12,7 @@ import {
|
||||
assert
|
||||
} from "../utils/index.js";
|
||||
|
||||
import { AbiCoder, defaultAbiCoder, getBuiltinCallException } from "./abi-coder.js";
|
||||
import { AbiCoder } from "./abi-coder.js";
|
||||
import { checkResultErrors, Result } from "./coders/abstract-coder.js";
|
||||
import { ConstructorFragment, ErrorFragment, EventFragment, Fragment, FunctionFragment, ParamType } from "./fragments.js";
|
||||
import { Typed } from "./typed.js";
|
||||
@ -144,9 +150,23 @@ function checkNames(fragment: Fragment, type: "input" | "output", params: Array<
|
||||
//export type AbiCoder = any;
|
||||
//const defaultAbiCoder: AbiCoder = { };
|
||||
|
||||
/**
|
||||
* @TODO
|
||||
*/
|
||||
export type InterfaceAbi = string | ReadonlyArray<Fragment | JsonFragment | string>;
|
||||
|
||||
/**
|
||||
* An Interface abstracts many of the low-level details for
|
||||
* encoding and decoding the data on the blockchain.
|
||||
*
|
||||
* An ABI provides information on how to encode data to send to
|
||||
* a Contract, how to decode the results and events and how to
|
||||
* interpret revert errors.
|
||||
*
|
||||
* The ABI can be specified by [any supported format](InterfaceAbi).
|
||||
*/
|
||||
export class Interface {
|
||||
|
||||
/**
|
||||
* All the Contract ABI members (i.e. methods, events, errors, etc).
|
||||
*/
|
||||
@ -164,6 +184,9 @@ export class Interface {
|
||||
|
||||
#abiCoder: AbiCoder;
|
||||
|
||||
/**
|
||||
* Create a new Interface for the %%fragments%%.
|
||||
*/
|
||||
constructor(fragments: InterfaceAbi) {
|
||||
let abi: ReadonlyArray<Fragment | JsonFragment | string> = [ ];
|
||||
if (typeof(fragments) === "string") {
|
||||
@ -267,7 +290,7 @@ export class Interface {
|
||||
* data.
|
||||
*/
|
||||
getAbiCoder(): AbiCoder {
|
||||
return defaultAbiCoder;
|
||||
return AbiCoder.defaultAbiCoder();
|
||||
}
|
||||
|
||||
// Find a function definition by any means necessary (unless it is ambiguous)
|
||||
@ -661,7 +684,7 @@ export class Interface {
|
||||
makeError(_data: BytesLike, tx: CallExceptionTransaction): CallExceptionError {
|
||||
const data = getBytes(_data, "data");
|
||||
|
||||
const error = getBuiltinCallException("call", tx, data);
|
||||
const error = AbiCoder.getBuiltinCallException("call", tx, data);
|
||||
|
||||
// Not a built-in error; try finding a custom error
|
||||
if (!error.message.match(/could not decode/)) {
|
||||
@ -980,7 +1003,7 @@ export class Interface {
|
||||
* The %%value%% may be provided as an existing [[Interface]] object,
|
||||
* a JSON-encoded ABI or any Human-Readable ABI format.
|
||||
*/
|
||||
static from(value: ReadonlyArray<Fragment | string | JsonFragment> | string | Interface): Interface {
|
||||
static from(value: InterfaceAbi | Interface): Interface {
|
||||
// Already an Interface, which is immutable
|
||||
if (value instanceof Interface) { return value; }
|
||||
|
||||
|
@ -1,3 +1,9 @@
|
||||
/**
|
||||
* About typed...
|
||||
*
|
||||
* @_subsection: api/abi:Typed Values
|
||||
*/
|
||||
|
||||
import { assertPrivate, defineProperties } from "../utils/index.js";
|
||||
|
||||
import type { Addressable } from "../address/index.js";
|
||||
@ -24,22 +30,26 @@ function b(value: BytesLike, size?: number): Typed {
|
||||
}
|
||||
|
||||
export interface TypedNumber extends Typed {
|
||||
value: number;
|
||||
defaultValue(): number;
|
||||
minValue(): number;
|
||||
maxValue(): number;
|
||||
}
|
||||
|
||||
export interface TypedBigInt extends Typed {
|
||||
value: bigint;
|
||||
defaultValue(): bigint;
|
||||
minValue(): bigint;
|
||||
maxValue(): bigint;
|
||||
}
|
||||
|
||||
export interface TypedData extends Typed {
|
||||
value: string;
|
||||
defaultValue(): string;
|
||||
}
|
||||
|
||||
export interface TypedString extends Typed {
|
||||
value: string;
|
||||
defaultValue(): string;
|
||||
}
|
||||
|
||||
@ -53,7 +63,8 @@ export class Typed {
|
||||
|
||||
readonly _typedSymbol!: Symbol;
|
||||
|
||||
constructor(gaurd: any, type: string, value: any, options: any = null) {
|
||||
constructor(gaurd: any, type: string, value: any, options?: any) {
|
||||
if (options == null) { options = null; }
|
||||
assertPrivate(_gaurd, gaurd, "Typed");
|
||||
defineProperties<Typed>(this, { _typedSymbol, type, value });
|
||||
this.#options = options;
|
||||
@ -238,10 +249,20 @@ export class Typed {
|
||||
return new Typed(_gaurd, "overrides", Object.assign({ }, v));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true only if %%value%% is a [[Typed]] instance.
|
||||
*/
|
||||
static isTyped(value: any): value is Typed {
|
||||
return (value && value._typedSymbol === _typedSymbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the value is a [[Typed]] instance, validates the underlying value
|
||||
* and returns it, otherwise returns value directly.
|
||||
*
|
||||
* This is useful for functions that with to accept either a [[Typed]]
|
||||
* object or values.
|
||||
*/
|
||||
static dereference<T>(value: Typed | T, type: string): T {
|
||||
if (Typed.isTyped(value)) {
|
||||
if (value.type !== type) {
|
||||
|
Loading…
Reference in New Issue
Block a user