diff --git a/packages/abi/src.ts/coders/array.ts b/packages/abi/src.ts/coders/array.ts index eaf19735dd..e033ad26f9 100644 --- a/packages/abi/src.ts/coders/array.ts +++ b/packages/abi/src.ts/coders/array.ts @@ -4,26 +4,46 @@ import { Logger } from "@ethersproject/logger"; import { version } from "../_version"; const logger = new Logger(version); -import { Coder, Reader, Writer } from "./abstract-coder"; +import { Coder, Reader, Result, Writer } from "./abstract-coder"; import { AnonymousCoder } from "./anonymous"; -export function pack(writer: Writer, coders: Array, values: Array): number { +export function pack(writer: Writer, coders: ReadonlyArray, values: Array | { [ name: string ]: any }): number { + let arrayValues: Array = null; if (Array.isArray(values)) { - // do nothing + arrayValues = values; } else if (values && typeof(values) === "object") { - let arrayValues: Array = []; - coders.forEach(function(coder) { - arrayValues.push((values)[coder.localName]); + let unique: { [ name: string ]: boolean } = { }; + + arrayValues = coders.map((coder) => { + const name = coder.localName; + if (!name) { + logger.throwError("cannot encode object for signature with missing names", Logger.errors.INVALID_ARGUMENT, { + argument: "values", + coder: coder, + value: values + }); + } + + if (unique[name]) { + logger.throwError("cannot encode object for signature with duplicate names", Logger.errors.INVALID_ARGUMENT, { + argument: "values", + coder: coder, + value: values + }); + } + + unique[name] = true; + + return values[name]; }); - values = arrayValues; } else { logger.throwArgumentError("invalid tuple value", "tuple", values); } - if (coders.length !== values.length) { + if (coders.length !== arrayValues.length) { logger.throwArgumentError("types/value length mismatch", "tuple", values); } @@ -32,7 +52,7 @@ export function pack(writer: Writer, coders: Array, values: Array): let updateFuncs: Array<(baseOffset: number) => void> = []; coders.forEach((coder, index) => { - let value = values[index]; + let value = arrayValues[index]; if (coder.dynamic) { // Get current dynamic offset (for the future pointer) @@ -60,7 +80,7 @@ export function pack(writer: Writer, coders: Array, values: Array): return length; } -export function unpack(reader: Reader, coders: Array): Array { +export function unpack(reader: Reader, coders: Array): Result { let values: any = []; // A reader anchored to this base @@ -109,10 +129,20 @@ export function unpack(reader: Reader, coders: Array): Array { // 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; + if (name) { + if (!accum[name]) { accum[name] = 0; } + accum[name]++; + } + return accum; + }, <{ [ name: string ]: number }>{ }); + // Add any named parameters (i.e. tuples) coders.forEach((coder: Coder, index: number) => { - let name: string = coder.localName; - if (!name) { return; } + let name = coder.localName; + if (!name || uniqueNames[name] !== 1) { return; } if (name === "length") { name = "_length"; } diff --git a/packages/abi/src.ts/coders/tuple.ts b/packages/abi/src.ts/coders/tuple.ts index dea643ae43..dfbf3615fb 100644 --- a/packages/abi/src.ts/coders/tuple.ts +++ b/packages/abi/src.ts/coders/tuple.ts @@ -19,7 +19,7 @@ export class TupleCoder extends Coder { this.coders = coders; } - encode(writer: Writer, value: Array): number { + encode(writer: Writer, value: Array | { [ name: string ]: any }): number { return pack(writer, this.coders, value); } diff --git a/packages/abi/src.ts/interface.ts b/packages/abi/src.ts/interface.ts index 32868ffa73..044b014c03 100644 --- a/packages/abi/src.ts/interface.ts +++ b/packages/abi/src.ts/interface.ts @@ -49,6 +49,7 @@ function wrapAccessError(property: string, error: Error): Error { return wrap; } +/* function checkNames(fragment: Fragment, type: "input" | "output", params: Array): void { params.reduce((accum, param) => { if (param.name) { @@ -60,7 +61,7 @@ function checkNames(fragment: Fragment, type: "input" | "output", params: Array< return accum; }, <{ [ name: string ]: boolean }>{ }); } - +*/ export class Interface { readonly fragments: Array; @@ -105,16 +106,16 @@ export class Interface { logger.warn("duplicate definition - constructor"); return; } - checkNames(fragment, "input", fragment.inputs); + //checkNames(fragment, "input", fragment.inputs); defineReadOnly(this, "deploy", fragment); return; case "function": - checkNames(fragment, "input", fragment.inputs); - checkNames(fragment, "output", (fragment).outputs); + //checkNames(fragment, "input", fragment.inputs); + //checkNames(fragment, "output", (fragment).outputs); bucket = this.functions; break; case "event": - checkNames(fragment, "input", fragment.inputs); + //checkNames(fragment, "input", fragment.inputs); bucket = this.events; break; default: