ethers.js/packages/testcases/generation/src.ts/abi-test.ts
2020-10-18 21:55:12 -04:00

145 lines
4.7 KiB
TypeScript

import { AbstractTest } from "./test";
export interface AbiType {
name: string;
type: string;
struct?: string;
components?: Array<AbiType>;
create(): any;
}
export abstract class AbstractAbiTest<T = any> extends AbstractTest<T> {
_nextNames: Record<string, number>;
constructor(name: string) {
super(name);
this._nextNames = { };
}
nextName(prefix?: string): string {
if (prefix == null) { prefix = "p"; }
if (!this._nextNames[prefix]) { this._nextNames[prefix] = 1; }
return prefix + (this._nextNames[prefix]++);
}
randomType(dynamicOrType?: boolean | string): AbiType {
if (dynamicOrType == null) { dynamicOrType = true; }
let type: number | string = null;
let dynamic = true;
if (typeof(dynamicOrType) === "boolean") {
dynamic = dynamicOrType;
type = this.randomInteger(0, dynamic ? 8: 6);
} else {
type = dynamicOrType;
}
const name = this.nextName();
switch (type) {
// Static
// address
case 0: case "address":
return { name, type: "address", create: () => {
return this.randomAddress();
} };
// bool
case 1: case "bool":
return { name, type: "bool", create: () => {
return this.randomChoice([ false, true ]);
} };
// intXX and uintXX
case 2: case "number": {
const signed = this.randomChoice([ false, true ]);
const width = this.randomInteger(1, 33);
return { name, type: `${ signed ? "": "u" }int${ width * 8 }`, create: () => {
const bytes = this.randomBytes(width);
let value = BigInt("0x" + bytes.toString("hex"));
if (signed && (bytes[0] & 0x80)) {
bytes[0] &= ~0x80;
value = -BigInt("0x" + bytes.toString("hex"));
}
return value.toString();
} };
}
// bytesXX
case 3: case "bytesX": {
const width = this.randomInteger(1, 33);
return { name, type: `bytes${ width }`, create: () => {
return this.randomHexString(width);
} };
}
// Static or dynamic nested types
// Array
case 4: case "array": {
const baseType = this.randomType(dynamic);
let length = this.randomInteger(0, 4);
if (length == 0) { length = null; }
const lengthString = ((length == null) ? "": String(length))
let struct = undefined;
let components = undefined;
if (baseType.struct) {
struct = `${ baseType.struct }[${ lengthString }]`;
components = baseType.components;
}
return { name, components, struct, type: `${ baseType.type }[${ lengthString }]`, create: () => {
let l = length;
if (l == null) { l = this.randomInteger(0, 4); }
const result = [ ];
for (let i = 0; i < l; i++) {
result.push(baseType.create());
}
return result;
} };
}
// Tuple
case 5: case "tuple": {
const components: Array<AbiType> = [ ];
const length = this.randomInteger(1, 5);
for (let i = 0; i < length; i++) {
components.push(this.randomType(dynamic));
}
const struct = this.nextName("Struct");
const type = `tuple(${ components.map(c => c.type).join(",") })`
return { name, struct, type, components, create: () => {
const result: Record<string, any> = { };
components.forEach((type) => {
result[type.name] = type.create();
});
return result;
} };
}
// Dynamic
// string
case 6: case "string":
return { name, type: "string", create: () => {
return this.randomString(0, 64);
} };
// bytes
case 7: case "bytes":
return { name, type: "bytes", create: () => {
return this.randomHexString(0, 64);
} };
}
throw new Error("should not be reached");
}
}