Add ABI coder function to compute default values (#1101).

This commit is contained in:
Richard Moore 2020-11-23 00:59:44 -05:00
parent 7f775f7ad6
commit a8e3380ed5
No known key found for this signature in database
GPG Key ID: 665176BE8E9DC651
12 changed files with 82 additions and 0 deletions

View File

@ -91,6 +91,12 @@ export class AbiCoder {
return new Writer(this._getWordSize());
}
getDefaultValue(types: Array<string | ParamType>): Result {
const coders: Array<Coder> = types.map((type) => this._getCoder(ParamType.from(type)));
const coder = new TupleCoder(coders, "_");
return coder.defaultValue();
}
encode(types: Array<string | ParamType>, values: Array<any>): string {
if (types.length !== values.length) {
logger.throwError("types/values length mismatch", Logger.errors.INVALID_ARGUMENT, {

View File

@ -70,6 +70,8 @@ export abstract class Coder {
abstract encode(writer: Writer, value: any): number;
abstract decode(reader: Reader): any;
abstract defaultValue(): any;
}
export class Writer {

View File

@ -11,6 +11,10 @@ export class AddressCoder extends Coder {
super("address", "address", localName, false);
}
defaultValue(): string {
return "0x0000000000000000000000000000000000000000";
}
encode(writer: Writer, value: string): number {
try {
getAddress(value);

View File

@ -11,6 +11,10 @@ export class AnonymousCoder extends Coder {
this.coder = coder;
}
defaultValue(): any {
return this.coder.defaultValue();
}
encode(writer: Writer, value: any): number {
return this.coder.encode(writer, value);
}

View File

@ -177,6 +177,17 @@ export class ArrayCoder extends Coder {
this.length = length;
}
defaultValue(): Array<any> {
// Verifies the child coder is valid (even if the array is dynamic or 0-length)
const defaultChild = this.coder.defaultValue();
const result: Array<any> = [];
for (let i = 0; i < this.length; i++) {
result.push(defaultChild);
}
return result;
}
encode(writer: Writer, value: Array<any>): number {
if (!Array.isArray(value)) {
this._throwError("expected array value", value);

View File

@ -8,6 +8,10 @@ export class BooleanCoder extends Coder {
super("bool", "bool", localName, false);
}
defaultValue(): boolean {
return false;
}
encode(writer: Writer, value: boolean): number {
return writer.writeValue(value ? 1: 0);
}

View File

@ -9,6 +9,10 @@ export class DynamicBytesCoder extends Coder {
super(type, type, localName, true);
}
defaultValue(): string {
return "0x";
}
encode(writer: Writer, value: any): number {
value = arrayify(value);
let length = writer.writeValue(value.length);

View File

@ -14,6 +14,10 @@ export class FixedBytesCoder extends Coder {
this.size = size;
}
defaultValue(): string {
return ("0x0000000000000000000000000000000000000000000000000000000000000000").substring(0, 2 + this.size * 2);
}
encode(writer: Writer, value: BytesLike): number {
let data = arrayify(value);
if (data.length !== this.size) { this._throwError("incorrect data length", value); }

View File

@ -8,6 +8,10 @@ export class NullCoder extends Coder {
super("null", "", localName, false);
}
defaultValue(): null {
return null;
}
encode(writer: Writer, value: any): number {
if (value != null) { this._throwError("not null", value); }
return writer.writeBytes([ ]);

View File

@ -17,6 +17,10 @@ export class NumberCoder extends Coder {
this.signed = signed;
}
defaultValue(): number {
return 0;
}
encode(writer: Writer, value: BigNumberish): number {
let v = BigNumber.from(value);

View File

@ -11,6 +11,10 @@ export class StringCoder extends DynamicBytesCoder {
super("string", localName);
}
defaultValue(): string {
return "";
}
encode(writer: Writer, value: any): number {
return super.encode(writer, toUtf8Bytes(value));
}

View File

@ -19,6 +19,37 @@ export class TupleCoder extends Coder {
this.coders = coders;
}
defaultValue(): any {
const values: any = [ ];
this.coders.forEach((coder) => {
values.push(coder.defaultValue());
});
// We only output named properties for uniquely named coders
const uniqueNames = this.coders.reduce((accum, coder) => {
const name = coder.localName;
if (name) {
if (!accum[name]) { accum[name] = 0; }
accum[name]++;
}
return accum;
}, <{ [ name: string ]: number }>{ });
// Add named values
this.coders.forEach((coder: Coder, index: number) => {
let name = coder.localName;
if (!name || uniqueNames[name] !== 1) { return; }
if (name === "length") { name = "_length"; }
if (values[name] != null) { return; }
values[name] = values[index];
});
return Object.freeze(values);
}
encode(writer: Writer, value: Array<any> | { [ name: string ]: any }): number {
return pack(writer, this.coders, value);
}