diff --git a/.gitignore b/.gitignore index c2658d7..e22271d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules/ +deps.pdf diff --git a/CHANGELOG.md b/CHANGELOG.md index 50a24f0..3b82d26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## vNext +### New features + +- Rearchitect declaration internals to support UDVTs + ([#114](https://github.com/gnidan/abi-to-sol/pull/114) by + [@gnidan](https://github.com/gnidan)) + ### Project updates - Fix web UI deployment ([#112](https://github.com/gnidan/abi-to-sol/pull/112) diff --git a/packages/abi-to-sol/.madgerc b/packages/abi-to-sol/.madgerc new file mode 100644 index 0000000..5888d3c --- /dev/null +++ b/packages/abi-to-sol/.madgerc @@ -0,0 +1,13 @@ +{ + "excludeRegExp": [ + "\\.test\\.ts$", + "\/test\/" + ], + "fileExtensions": ["js","ts"], + "detectiveOptions": { + "ts": { + "skipTypeImports": true + } + }, + "tsConfig": "tsconfig.json" +} diff --git a/packages/abi-to-sol/README.md b/packages/abi-to-sol/README.md index fdca8ff..c1d3d4e 100644 --- a/packages/abi-to-sol/README.md +++ b/packages/abi-to-sol/README.md @@ -98,10 +98,6 @@ interface ENS { ``` -## Currently unsupported (PRs welcome! :wink:) - -- [User defined value types](https://blog.soliditylang.org/2021/09/27/user-defined-value-types/) - ## Is this project useful to you? Feel free to donate to diff --git a/packages/abi-to-sol/bin/abi-to-sol.ts b/packages/abi-to-sol/bin/abi-to-sol.ts index 9d19e26..8f79bb4 100644 --- a/packages/abi-to-sol/bin/abi-to-sol.ts +++ b/packages/abi-to-sol/bin/abi-to-sol.ts @@ -1,4 +1,5 @@ #!/usr/bin/env node +import "source-map-support/register"; const neodoc = require("neodoc"); import {Abi as SchemaAbi} from "@truffle/contract-schema/spec"; @@ -6,8 +7,11 @@ import * as abiSchema from "@truffle/contract-schema/spec/abi.spec.json"; import betterAjvErrors from "better-ajv-errors"; import Ajv from "ajv"; -import {generateSolidity, GenerateSolidityMode} from "../src"; -import * as defaults from "../src/defaults"; +import { + generateSolidity, + defaults, + GenerateSolidityMode +} from "../src"; const usage = ` abi-to-sol diff --git a/packages/abi-to-sol/package.json b/packages/abi-to-sol/package.json index d9e365f..dc80487 100644 --- a/packages/abi-to-sol/package.json +++ b/packages/abi-to-sol/package.json @@ -1,6 +1,6 @@ { "name": "abi-to-sol", - "version": "0.7.1", + "version": "0.8.0-0", "description": "Compile ABI JSON to Solidity interface", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", @@ -15,6 +15,7 @@ "scripts": { "abi-to-sol": "ts-node ./bin/abi-to-sol.ts", "prepare": "tsc", + "madge": "madge ./src --image deps.pdf", "test": "jest src/**", "test:test": "jest test/**", "test:dist": "yarn prepare && jest dist/src", @@ -34,16 +35,17 @@ "@types/semver": "^7.3.7", "change-case": "^4.1.1", "faker": "^5.1.0", - "fast-check": "^3.1.1", + "fast-check": "3.1.1", "husky": ">=4", "jest": "^26.4.2", "jest-fast-check": "^0.0.1", "jest-json-schema": "^2.1.0", "lint-staged": ">=10", - "solc": "^0.8.6", + "madge": "^5.0.2", + "solc": "^0.8.20", "ts-jest": "^26.4.0", "ts-node": "^9.0.0", - "typescript": "4.5.2" + "typescript": "^4.9.5" }, "dependencies": { "@truffle/abi-utils": "^1.0.0", diff --git a/packages/abi-to-sol/src/declarations.test.ts b/packages/abi-to-sol/src/declarations.test.ts deleted file mode 100644 index 876e074..0000000 --- a/packages/abi-to-sol/src/declarations.test.ts +++ /dev/null @@ -1,151 +0,0 @@ -import * as fc from "fast-check"; -import {testProp} from "jest-fast-check"; -import {Arbitrary} from "@truffle/abi-utils"; -import * as Example from "../test/custom-example"; - -import {collectDeclarations} from "./declarations"; - -describe("collectDeclarations", () => { - describe("arbitrary examples", () => { - describe("for non-tuple parameters / event parameters", () => { - testProp( - "are empty", - [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], - (parameter) => { - fc.pre(!parameter.type.startsWith("tuple")); - - expect(collectDeclarations(parameter)).toEqual({ - signatureDeclarations: {}, - containerSignatures: {} - }); - } - ); - }); - - describe("for tuple parameters with non-tuple components", () => { - testProp( - "have length 1", - [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], - (parameter) => { - fc.pre(parameter.type.startsWith("tuple")); - fc.pre( - parameter.components.every( - (component: any) => !component.type.startsWith("tuple") - ) - ); - - const declarations = collectDeclarations(parameter); - expect(Object.keys(declarations.signatureDeclarations)).toHaveLength(1); - - const [declaration] = Object.values(declarations.signatureDeclarations); - expect(declaration).toHaveProperty("components"); - - const {components} = declaration; - expect(components).toHaveLength(parameter.components.length); - - for (const [index, component] of components.entries()) { - expect(component.name).toEqual(parameter.components[index].name); - } - } - ); - }); - - describe("for tuple parameters with exactly one tuple component", () => { - testProp( - "have length 2", - [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], - (parameter) => { - fc.pre(parameter.type.startsWith("tuple")); - - // find exactly one tuple-based component - const tupleComponents = parameter.components.filter( - (component: any) => component.type.startsWith("tuple") - ); - - fc.pre(tupleComponents.length === 1); - - const [tupleComponent] = tupleComponents; - - fc.pre( - tupleComponent.components.every( - (component: any) => !component.type.startsWith("tuple") - ) - ); - - const declarations = collectDeclarations(parameter); - expect(Object.keys(declarations.signatureDeclarations)).toHaveLength(2); - } - ); - }); - - testProp( - "produce only valid references to each other", - [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], - (parameter) => { - fc.pre(parameter.type.startsWith("tuple")); - - const components = parameter.components || []; - - const declarations = collectDeclarations(parameter); - - for (const {components} of Object.values(declarations.signatureDeclarations)) { - for (const {signature} of components) { - if (signature) { - expect(declarations.signatureDeclarations).toHaveProperty(signature); - } - } - } - } - ); - }); - - describe("custom example", () => { - const declarations = collectDeclarations(Example.abi); - - for (const [structName, signature] of Object.entries( - Example.expectedSignatures - )) { - describe(`struct ${structName}`, () => { - it("exists in declarations", () => { - expect(declarations.signatureDeclarations).toHaveProperty(signature); - }); - - const expectedComponents = (Example.expectedDeclarations as any)[ - structName - ]; - const declaration = declarations.signatureDeclarations[signature]; - - for (const [componentName, component] of Object.entries( - expectedComponents - )) { - describe(`component ${componentName}`, () => { - it("exists in declarations", () => { - const names = declaration.components.map(({name}) => name); - expect(names).toContain(componentName); - }); - - const expectedComponent = (expectedComponents as any)[ - componentName - ]; - - const component: any = declaration.components.find( - ({name}) => name === componentName - ); - - it("has correct type", () => { - expect(component.type).toEqual(expectedComponent.type); - }); - - if (component.signature) { - it("has correct signature", () => { - expect(component.signature).toEqual( - expectedComponent.signature - ); - }); - } - }); - } - }); - } - }); -}); diff --git a/packages/abi-to-sol/src/declarations.ts b/packages/abi-to-sol/src/declarations.ts deleted file mode 100644 index c14b376..0000000 --- a/packages/abi-to-sol/src/declarations.ts +++ /dev/null @@ -1,166 +0,0 @@ -import type {Abi as SchemaAbi} from "@truffle/contract-schema/spec"; -import type * as Abi from "@truffle/abi-utils"; -import { abiTupleSignature } from "@truffle/abi-utils"; - -import {Visitor, VisitOptions, dispatch, Node} from "./visitor"; - -export interface Component { - name: string; - type: string; - signature?: string; -} - -export interface Declaration { - identifier?: string; - components: Component[]; -} - -export interface Declarations { - signatureDeclarations: { - [signature: string]: Declaration; - }; - containerSignatures: { - [container: string]: string[]; - } -} - -export class DeclarationsCollector implements Visitor { - visitAbi({node: nodes}: VisitOptions): Declarations { - return nodes - .map((node) => dispatch({node, visitor: this})) - .reduce(mergeDeclarations, emptyDeclarations()); - } - - visitEventEntry({node: entry}: VisitOptions): Declarations { - return entry.inputs - .map((node) => dispatch({node, visitor: this})) - .reduce(mergeDeclarations, emptyDeclarations()); - } - - visitErrorEntry({node: entry}: VisitOptions): Declarations { - return entry.inputs - .map((node) => dispatch({node, visitor: this})) - .reduce(mergeDeclarations, emptyDeclarations()); - } - - visitFunctionEntry({ - node: entry, - }: VisitOptions): Declarations { - return [...entry.inputs, ...(entry.outputs || [])] - .map((node) => dispatch({node, visitor: this})) - .reduce(mergeDeclarations, emptyDeclarations()); - } - - visitConstructorEntry({ - node: entry, - }: VisitOptions): Declarations { - return entry.inputs - .map((node) => dispatch({node, visitor: this})) - .reduce(mergeDeclarations, emptyDeclarations()); - } - - visitFallbackEntry({ - node: entry, - }: VisitOptions): Declarations { - return emptyDeclarations(); - } - - visitReceiveEntry({ - node: entry, - }: VisitOptions): Declarations { - return emptyDeclarations(); - } - - visitParameter({node: parameter}: VisitOptions): Declarations { - if (!parameter.type.startsWith("tuple")) { - return emptyDeclarations(); - } - - let container = ""; - const components = parameter.components || []; - const signature = abiTupleSignature(components); - const declaration: Declaration = { - components: components.map(({name, type, components}) => - !components - ? {name, type} - : { - name, - type, - signature: abiTupleSignature(components), - } - ), - }; - - if ("internalType" in parameter && parameter.internalType) { - const match = parameter.internalType.match(/struct ([^\[]+).*/); - if (match) { - const possiblyQualifiedIdentifier = match[1]; - const parts = possiblyQualifiedIdentifier.split("."); - if (parts.length === 1) { - declaration.identifier = parts[0]; - } else if (parts.length === 2) { - container = parts[0]; - declaration.identifier = parts[1]; - } - } - } - - const declarations = { - signatureDeclarations: { - [signature]: declaration - }, - containerSignatures: { - [container]: [signature] - } - }; - - const componentDeclarations: Declarations = components - .map((component: Abi.Parameter) => - this.visitParameter({node: component}) - ) - .reduce(mergeDeclarations, emptyDeclarations()) - - - return mergeDeclarations(declarations, componentDeclarations); - } -} - -export const collectDeclarations = (node: SchemaAbi | Node) => - dispatch({ - node, - visitor: new DeclarationsCollector(), - }); - -function mergeDeclarations( - a: Declarations, - b: Declarations -): Declarations { - const declarations: Declarations = { - signatureDeclarations: { - ...a.signatureDeclarations, - ...b.signatureDeclarations - }, - containerSignatures: { - ...a.containerSignatures, - // add b iteratively separately to merge arrays - } - }; - - for (const [container, signatures] of Object.entries(b.containerSignatures)) { - const mergedSignatures = new Set([ - ...(declarations.containerSignatures[container] || []), - ...signatures - ]) - - declarations.containerSignatures[container] = [...mergedSignatures]; - } - - return declarations; -} - -function emptyDeclarations(): Declarations { - return { - signatureDeclarations: {}, - containerSignatures: {} - }; -} diff --git a/packages/abi-to-sol/src/declarations/bind.test.ts b/packages/abi-to-sol/src/declarations/bind.test.ts new file mode 100644 index 0000000..5f99397 --- /dev/null +++ b/packages/abi-to-sol/src/declarations/bind.test.ts @@ -0,0 +1,55 @@ +import * as Abi from "@truffle/abi-utils"; + +import { Identifier } from "./identifier"; +import { fromParameter } from "./fromParameter"; + +import { bind } from "./bind"; + +describe("bind", () => { + it("should re-use the same identifier for the same unnamed struct", () => { + const internalComponent = { + name: "u", + type: "uint256" + }; + + const parameter = { + name: "a", + type: "tuple", + internalType: "struct A", + components: [{ + name: "b1", + type: "tuple", + components: [{ ...internalComponent }] + }, { + name: "b2", + type: "tuple", + components: [{ ...internalComponent }] + }] + }; + + const { declarations, parameterKind } = fromParameter(parameter); + if (!("identifier" in parameterKind) || !parameterKind.identifier) { + throw new Error("Expected parameterKind to have identifier"); + } + + const { identifier } = parameterKind; + + const declarationsWithBindings = bind(declarations); + + const outerStructKind = declarationsWithBindings.byIdentifierReference[ + Identifier.toReference(identifier) + ]; + + if (!("members" in outerStructKind)) { + throw new Error("Expected outer struct to have `members`"); + } + + const { members: [b1, b2] } = outerStructKind; + + if (!("identifier" in b1.kind) || !("identifier" in b2.kind)) { + throw new Error("Inner struct is missing identifier"); + } + + expect(b1.kind.identifier).toEqual(b2.kind.identifier); + }); +}); diff --git a/packages/abi-to-sol/src/declarations/bind.ts b/packages/abi-to-sol/src/declarations/bind.ts new file mode 100644 index 0000000..0b402d1 --- /dev/null +++ b/packages/abi-to-sol/src/declarations/bind.ts @@ -0,0 +1,158 @@ +import { Identifier } from "./identifier"; +import { Kind, Bindings, HasBindings, MissingDeepBindings } from "./kind"; + +import { Declarations, merge } from "./types"; + +export const bind = ( + declarations: Declarations +): Declarations => { + const { + newDeclarations, + identifierBySignature, + }: { + newDeclarations: Declarations; + identifierBySignature: { + [signature: string]: Identifier + } + } = Object.entries(declarations.unnamedBySignature || {}) + .map(([signature, unidentifiedKind], index) => { + const identifier: Identifier = { + class: "struct", + name: `S_${index}` + }; + + const kind = { + ...unidentifiedKind, + identifier + }; + + return { + kind, + signature + }; + }) + .reduce(( + { + newDeclarations: { + byIdentifierReference, + globalIdentifiers + }, + identifierBySignature + }, + { + kind, + signature + } + ) => ({ + newDeclarations: { + byIdentifierReference: { + ...byIdentifierReference, + [Identifier.toReference(kind.identifier)]: kind + }, + unnamedBySignature: {}, + globalIdentifiers: new Set([ + ...globalIdentifiers, + Identifier.toReference(kind.identifier) + ]), + identifiersByContainer: {}, + }, + identifierBySignature: { + ...identifierBySignature, + [signature]: kind.identifier + } + }), { + newDeclarations: { + byIdentifierReference: {}, + unnamedBySignature: {}, + globalIdentifiers: new Set([]), + identifiersByContainer: {} + }, + identifierBySignature: {} + }); + + const declarationsMissingDeepBindings = merge( + declarations as Declarations, + newDeclarations + ); + + return { + byIdentifierReference: Object.entries( + declarationsMissingDeepBindings.byIdentifierReference + ) + .map(([identifierReference, kind]) => ({ + [identifierReference]: Kind.isStruct(kind) + ? bindStruct(kind, identifierBySignature) + : kind + })) + .reduce((a, b) => ({ ...a, ...b }), {}), + unnamedBySignature: {}, + globalIdentifiers: declarationsMissingDeepBindings.globalIdentifiers, + identifiersByContainer: declarationsMissingDeepBindings.identifiersByContainer + }; +}; + +const bindKind = ( + kind: Kind, + identifierBySignature: { + [signature: string]: Identifier; + } +): Kind => { + if (Kind.isElementary(kind)) { + return kind; + } + + if (Kind.isUserDefinedValueType(kind)) { + return kind; + } + + if (Kind.isStruct(kind)) { + return bindStruct(kind, identifierBySignature); + } + + if (Kind.isArray(kind)) { + return bindArray(kind, identifierBySignature); + } + + throw new Error("Could not recognize kind"); +} + +const bindStruct = ( + kind: Kind.Struct, + identifierBySignature: { + [signature: string]: Identifier; + } +): Kind.Struct => { + const { + signature, + identifier = identifierBySignature[signature] + } = kind; + + const members = kind.members.map(({ name, kind }) => ({ + name, + kind: bindKind(kind, identifierBySignature) + })); + + return { + signature, + members, + identifier + } +}; + +const bindArray = ( + kind: Kind.Array, + identifierBySignature: { + [signature: string]: Identifier; + } +): Kind.Array => { + const itemKind = bindKind(kind.itemKind, identifierBySignature); + const length = kind.length; + + return { + itemKind, + ...("length" in kind + ? { length } + : {} + ) + }; +}; diff --git a/packages/abi-to-sol/src/declarations/collect.ts b/packages/abi-to-sol/src/declarations/collect.ts new file mode 100644 index 0000000..88b956e --- /dev/null +++ b/packages/abi-to-sol/src/declarations/collect.ts @@ -0,0 +1,84 @@ +import type {Abi as SchemaAbi} from "@truffle/contract-schema/spec"; +import type * as Abi from "@truffle/abi-utils"; + +import {Visitor, VisitOptions, dispatch, Node} from "../visitor"; + +import { Declarations, empty, merge } from "./types"; +import { fromParameter } from "./fromParameter"; + +export const collectWithoutBindings = (node: Node) => + dispatch({ node, visitor: new DeclarationsCollector() }); + +interface Context { +} + +type Visit = VisitOptions; + + +class DeclarationsCollector + implements Visitor +{ + visitAbi({ + node: abi, + context + }: Visit): Declarations { + return abi + .map(node => dispatch({ node, context, visitor: this })) + .reduce(merge, empty()); + } + + visitEventEntry({ + node: entry, + context + }: Visit): Declarations { + return entry.inputs + .map(node => dispatch({ node, context, visitor: this })) + .reduce(merge, empty()); + } + + visitErrorEntry({ + node: entry, + context + }: Visit): Declarations { + return entry.inputs + .map(node => dispatch({ node, context, visitor: this })) + .reduce(merge, empty()); + } + + visitFunctionEntry({ + node: entry, + context + }: Visit): Declarations { + return [...entry.inputs, ...(entry.outputs || [])] + .map(node => dispatch({ node, context, visitor: this })) + .reduce(merge, empty()); + } + + visitConstructorEntry({ + node: entry, + context + }: Visit): Declarations { + return entry.inputs + .map(node => dispatch({ node, context, visitor: this })) + .reduce(merge, empty()); + } + + visitFallbackEntry({ + node: entry + }: Visit): Declarations { + return empty(); + } + + visitReceiveEntry({ + node: entry, + }: Visit): Declarations { + return empty(); + } + + visitParameter({ + node: parameter + }: Visit): Declarations { + const { declarations } = fromParameter(parameter); + return declarations; + } +} diff --git a/packages/abi-to-sol/src/declarations/find.ts b/packages/abi-to-sol/src/declarations/find.ts new file mode 100644 index 0000000..34b2541 --- /dev/null +++ b/packages/abi-to-sol/src/declarations/find.ts @@ -0,0 +1,132 @@ +import type * as Abi from "@truffle/abi-utils"; + +import { Parameter, isParameter } from "../parameter"; + +import { Identifier } from "./identifier"; +import { Kind, HasBindings } from "./kind"; +import { Declarations } from "./types"; + +export const find = ( + parameter: Abi.Parameter, + declarations: Declarations +): Kind => { + const { type } = parameter; + + if (!isParameter(parameter)) { + throw new Error( + `Parameter type \`${parameter.type}\` is not a valid ABI type` + ); + } + + if (Parameter.isElementary(parameter)) { + return findElementary(parameter, declarations); + } + + if (Parameter.isArray(parameter)) { + return findArray(parameter, declarations); + } + + if (Parameter.isTuple(parameter)) { + return findTuple(parameter, declarations); + } + + throw new Error(`Unknown type ${type}`); +} + +const findElementary = ( + parameter: Parameter.Elementary, + declarations: Declarations +): Kind => { + if (!Parameter.isUserDefinedValueType(parameter)) { + const { type, internalType } = parameter; + return { + type, + ...( + internalType + ? { hints: { internalType } } + : {} + ) + } + } + + const { name, scope } = Parameter.UserDefinedValueType.recognize( + parameter + ); + const identifier = Identifier.UserDefinedValueType.create({ name, scope }); + const reference = Identifier.toReference(identifier); + + const kind = declarations.byIdentifierReference[reference]; + + if (!kind) { + throw new Error( + `Unknown declaration with identifier reference ${reference}` + ); + } + + return kind; +}; + +const findArray = ( + parameter: Parameter.Array, + declarations: Declarations +): Kind => { + const itemParameter = Parameter.Array.item(parameter); + + const itemKind = find(itemParameter, declarations); + + return { + itemKind, + ...( + Parameter.Array.isStatic(parameter) + ? { length: Parameter.Array.Static.length(parameter) } + : {} + ) + }; +} + +const findTuple = ( + parameter: Parameter.Tuple, + declarations: Declarations +): Kind => { + const { + signature, + name, + scope + } = Parameter.Tuple.recognize(parameter); + + const identifier = name + ? Identifier.Struct.create({ name, scope }) + : undefined; + + if (identifier) { + const reference = Identifier.toReference(identifier); + + const kind = declarations.byIdentifierReference[reference]; + + if (!kind) { + throw new Error( + `Unknown declaration with identifier reference ${reference}` + ); + } + + return kind; + } + + // reaching here guarantees no internalType specified for `parameter` + // so only match declarations that also have no internalType + + const kind = Object.values(declarations.byIdentifierReference) + .find(kind => + Kind.isStruct(kind) && + kind.signature === signature && + !kind.hints?.internalType + ) + + if (!kind) { + throw new Error( + `Unknown declaration with tuple signature ${signature}` + ); + } + + return kind; +} diff --git a/packages/abi-to-sol/src/declarations/fromParameter.test.ts b/packages/abi-to-sol/src/declarations/fromParameter.test.ts new file mode 100644 index 0000000..87baef5 --- /dev/null +++ b/packages/abi-to-sol/src/declarations/fromParameter.test.ts @@ -0,0 +1,129 @@ +import type * as Abi from "@truffle/abi-utils"; +import { abiTupleSignature } from "@truffle/abi-utils"; + +import { Identifier } from "./identifier"; + +import { fromParameter } from "./fromParameter"; + +describe("fromParameter", () => { + it("builds declarations from a single elementary type", () => { + const parameter: Abi.Parameter = { + type: "uint256", + name: "u", + internalType: "uint256" + }; + + const { declarations, parameterKind } = fromParameter(parameter); + + expect(declarations.byIdentifierReference).toEqual({}); + expect(declarations.unnamedBySignature).toEqual({}); + }); + + it("builds a reference to an unnamed struct", () => { + const parameter: Abi.Parameter = { + type: "tuple", + name: "s", + components: [ + { + type: "uint256", + name: "u" + } + ] + }; + + const expectedSignature = abiTupleSignature( + // default to satisfy type-checker (Abi.Parameter includes non-tuples) + parameter.components || [] + ); + + const { declarations, parameterKind } = fromParameter(parameter); + + expect(declarations.byIdentifierReference).toEqual({}); + expect(declarations.unnamedBySignature).toHaveProperty("(uint256)"); + + const unnamedDeclaration = declarations.unnamedBySignature["(uint256)"]; + if (!unnamedDeclaration) { + throw new Error("Expected unnamed reference"); + } + + expect(unnamedDeclaration.signature).toEqual(expectedSignature); + expect(unnamedDeclaration.identifier).toEqual(undefined); + expect(unnamedDeclaration.members).toHaveLength(1); + + const [member] = unnamedDeclaration.members; + + expect(member.name).toEqual("u"); + expect(member.kind).toEqual({ + type: "uint256" + }); + }); + + it("should deduplicate unnamed structs", () => { + const internalComponent = { + name: "u", + type: "uint256" + }; + + const parameter = { + name: "a", + type: "tuple", + components: [{ + name: "b1", + type: "tuple", + components: [{ ...internalComponent }] + }, { + name: "b2", + type: "tuple", + components: [{ ...internalComponent }] + }] + }; + + const { declarations, parameterKind } = fromParameter(parameter); + + // outer struct + expect(declarations.unnamedBySignature).toHaveProperty("((uint256),(uint256))"); + const outerStruct = declarations.unnamedBySignature["((uint256),(uint256))"]; + + // inner struct + expect(declarations.unnamedBySignature).toHaveProperty("(uint256)"); + }); + + it("should include identifiers when given internalType", () => { + const parameter: Abi.Parameter = { + name: "a", + type: "tuple", + internalType: "struct A", + components: [{ + name: "u", + type: "uint256" + }, { + name: "f", + type: "address", + internalType: "contract Foo" + }] + }; + + const { declarations, parameterKind } = fromParameter(parameter); + + if (!("identifier" in parameterKind)) { + throw new Error("Expected `identifier` to exist on parameterKind"); + } + + const { identifier } = parameterKind; + + if (!identifier) { + throw new Error("Expected identifier to be defined"); + } + + expect(declarations.byIdentifierReference).toHaveProperty( + Identifier.toReference(identifier) + ); + + const kind = declarations.byIdentifierReference[ + Identifier.toReference(identifier) + ]; + + expect(parameterKind).toEqual(kind); + }); +}); + diff --git a/packages/abi-to-sol/src/declarations/fromParameter.ts b/packages/abi-to-sol/src/declarations/fromParameter.ts new file mode 100644 index 0000000..907df78 --- /dev/null +++ b/packages/abi-to-sol/src/declarations/fromParameter.ts @@ -0,0 +1,174 @@ +import type * as Abi from "@truffle/abi-utils"; + +import { Parameter, isParameter } from "../parameter"; + +import { Identifier } from "./identifier"; +import { Kind, MissingBindings } from "./kind"; +import { Declarations, empty, merge, from } from "./types"; + +export interface FromParameterResult { + parameterKind: Kind; + declarations: Declarations; +} + +export const fromParameter = ( + parameter: Abi.Parameter +): FromParameterResult => { + if (!isParameter(parameter)) { + throw new Error( + `Parameter type \`${parameter.type}\` is not a valid ABI type` + ); + } + + if (Parameter.isElementary(parameter)) { + return fromElementaryParameter(parameter); + } + + if (Parameter.isArray(parameter)) { + return fromArrayParameter(parameter); + } + + if (Parameter.isTuple(parameter)) { + return fromTupleParameter(parameter); + } + + throw new Error(`Unexpectedly could not convert Abi.Parameter to Kind`); +}; + +const fromElementaryParameter = ( + parameter: Parameter.Elementary +): FromParameterResult => { + if (Parameter.isUserDefinedValueType(parameter)) { + const { name, scope } = Parameter.UserDefinedValueType.recognize( + parameter + ); + const identifier = Identifier.UserDefinedValueType.create({ name, scope }); + + const { type, internalType } = parameter; + + const parameterKind: Kind.UserDefinedValueType = { + type, + hints: { internalType }, + identifier + }; + + return { + parameterKind, + declarations: from(parameterKind) + } + } + + const { type, internalType } = parameter; + + const parameterKind: Kind.Elementary = { + type, + ...( + internalType + ? { hints: { internalType } } + : {} + ) + }; + + return { + parameterKind, + declarations: from(parameterKind) + } +} + +const fromArrayParameter = ( + parameter: Parameter.Array +): FromParameterResult => { + const itemParameter = Parameter.Array.item(parameter); + + const { + parameterKind: itemKind, + declarations + } = fromParameter(itemParameter); + + const parameterKind: Kind.Array = { + itemKind, + ...( + Parameter.Array.isStatic(parameter) + ? { length: Parameter.Array.Static.length(parameter) } + : {} + ) + }; + + return { + declarations, + parameterKind + }; +}; + +const fromTupleParameter = ( + parameter: Parameter.Tuple +): FromParameterResult => { + const { + internalType, + components + } = parameter; + + const { + signature, + name, + scope + } = Parameter.Tuple.recognize(parameter); + + const identifier = name + ? Identifier.Struct.create({ name, scope }) + : undefined; + + const memberResults: { + member: Kind.Struct.Member; + declarations: Declarations + }[] = components.map(component => { + const { name } = component; + + const { + parameterKind: kind, + declarations + } = fromParameter(component); + + return { + member: { + kind, + ...( + name + ? { name } + : {} + ) + }, + declarations + } + }); + + const members = memberResults.map(({ member }) => member); + const membersDeclarations = memberResults + .map(({ declarations }) => declarations) + .reduce(merge, empty()); + + const parameterKind = { + signature, + members, + ...( + internalType + ? { hints: { internalType } } + : {} + ), + ...( + identifier + ? { identifier } + : {} + ) + }; + + const declarations = merge( + membersDeclarations, + from(parameterKind) + ); + + return { + declarations, + parameterKind + }; +} diff --git a/packages/abi-to-sol/src/declarations/identifier.ts b/packages/abi-to-sol/src/declarations/identifier.ts new file mode 100644 index 0000000..a2e0eac --- /dev/null +++ b/packages/abi-to-sol/src/declarations/identifier.ts @@ -0,0 +1,110 @@ +export type Identifier = + | Identifier.Interface + | Identifier.Struct + | Identifier.UserDefinedValueType; + +export namespace Identifier { + export interface Properties { + name: string; + scope?: string; + } + + export type Class = + | Struct.Class + | Interface.Class + | UserDefinedValueType.Class; + + export interface Interface { + class: Interface.Class; + name: string; + container?: never; + } + + export namespace Interface { + export type Class = "interface"; + + export const create = ({ + name + }: Omit): Identifier.Interface => ({ + class: "interface", + name + }); + + export type Reference = `${ + Identifier["class"] + }--${ + Identifier["name"] + }`; + } + + export interface Struct { + class: Struct.Class; + name: string; + container?: Interface; + } + + export namespace Struct { + export type Class = "struct"; + + export const create = ({ + name, + scope + }: Properties): Identifier.Struct => ({ + class: "struct", + name, + ...( + scope + ? { container: Identifier.Interface.create({ name: scope }) } + : {} + ) + }); + } + + export interface UserDefinedValueType { + class: UserDefinedValueType.Class; + name: string; + container?: Interface; + } + + + export namespace UserDefinedValueType { + export type Class = "udvt"; + + export const create = ({ + name, + scope + }: Properties): Identifier.UserDefinedValueType => ({ + class: "udvt", + name, + ...( + scope + ? { container: Identifier.Interface.create({ name: scope }) } + : {} + ) + }); + } + + export type Reference = + | `${ + Identifier["class"] + }--${ + Identifier["name"] + }` + | `${ + Identifier["class"] + }--${ + Identifier["name"] + }--${ + Exclude["name"] + }`; + + export const toReference = ( + identifier: Identifier + ): Reference => { + if (identifier.container) { + return `${identifier.class}--${identifier.name}--${identifier.container.name}`; + } + + return `${identifier.class}--${identifier.name}`; + } +} diff --git a/packages/abi-to-sol/src/declarations/index.test.ts b/packages/abi-to-sol/src/declarations/index.test.ts new file mode 100644 index 0000000..9515fd6 --- /dev/null +++ b/packages/abi-to-sol/src/declarations/index.test.ts @@ -0,0 +1,113 @@ +import * as fc from "fast-check"; +import { testProp } from "jest-fast-check"; +import { Arbitrary } from "@truffle/abi-utils"; + +import { Declarations } from "."; +import { Kind } from "./kind"; +import { Identifier } from "./identifier"; + +describe("Declarations.collect", () => { + describe("arbitrary examples", () => { + describe("for non-tuple parameters / event parameters", () => { + testProp( + "are empty", + [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], + (parameter) => { + fc.pre(!parameter.type.startsWith("tuple")); + + expect(Declarations.collect(parameter)).toEqual({ + byIdentifierReference: {}, + unnamedBySignature: {}, + globalIdentifiers: new Set([]), + identifiersByContainer: {}, + }); + } + ); + }); + + describe("for tuple parameters with non-tuple components", () => { + testProp( + "have length 1", + [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], + (parameter) => { + fc.pre(parameter.type.startsWith("tuple")); + fc.pre( + parameter.components.every( + (component: any) => !component.type.startsWith("tuple") + ) + ); + + const declarations = Declarations.collect(parameter); + expect(Object.keys(declarations.byIdentifierReference)).toHaveLength(1); + + const [kind] = Object.values(declarations.byIdentifierReference); + if (!("members" in kind)) { + throw new Error("Expected kind to be a struct with members"); + } + + const { members } = kind; + expect(members).toHaveLength(parameter.components.length); + + for (const [index, member] of members.entries()) { + expect(member.name).toEqual(parameter.components[index].name); + } + } + ); + }); + + describe("for tuple parameters with exactly one tuple component", () => { + testProp( + "have length 2", + [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], + (parameter) => { + fc.pre(parameter.type.startsWith("tuple")); + + // find exactly one tuple-based component + const tupleComponents = parameter.components.filter( + (component: any) => component.type.startsWith("tuple") + ); + + fc.pre(tupleComponents.length === 1); + + const [tupleComponent] = tupleComponents; + + fc.pre( + tupleComponent.components.every( + (component: any) => !component.type.startsWith("tuple") + ) + ); + + const declarations = Declarations.collect(parameter); + expect(Object.keys(declarations.byIdentifierReference)).toHaveLength(2); + } + ); + }); + + testProp( + "produce only valid references to each other", + [fc.oneof(Arbitrary.Parameter(), Arbitrary.EventParameter())], + (parameter) => { + fc.pre(parameter.type.startsWith("tuple")); + + const components = parameter.components || []; + + const declarations = Declarations.collect(parameter); + + for (const kind of Object.values(declarations.byIdentifierReference)) { + if ("members" in kind) { + for (const member of kind.members) { + if (Kind.isStruct(member.kind)) { + if (!("identifier" in member.kind)) { + throw new Error("Expected identifier"); + } + expect(declarations.byIdentifierReference).toHaveProperty( + Identifier.toReference(member.kind.identifier) + ); + } + } + } + } + } + ); + }); +}); diff --git a/packages/abi-to-sol/src/declarations/index.ts b/packages/abi-to-sol/src/declarations/index.ts new file mode 100644 index 0000000..f51651b --- /dev/null +++ b/packages/abi-to-sol/src/declarations/index.ts @@ -0,0 +1,27 @@ +import type {Abi as SchemaAbi} from "@truffle/contract-schema/spec"; +import * as Abi from "@truffle/abi-utils"; + +import type { Declarations as _Declarations } from "./types"; +import { find as _find } from "./find"; +import { bind } from "./bind"; +import { HasBindings } from "./kind"; +import { collectWithoutBindings } from "./collect"; + +export { Identifier } from "./identifier"; +export { Kind } from "./kind"; + +export namespace Declarations { + export const collect = ( + abi: Abi.Abi | SchemaAbi + ): _Declarations => { + const unboundDeclarations = collectWithoutBindings(abi); + + const declarations = bind(unboundDeclarations); + + return declarations; + }; + + export const find = _find; +} + +export type Declarations = _Declarations; diff --git a/packages/abi-to-sol/src/declarations/kind.ts b/packages/abi-to-sol/src/declarations/kind.ts new file mode 100644 index 0000000..5f68068 --- /dev/null +++ b/packages/abi-to-sol/src/declarations/kind.ts @@ -0,0 +1,164 @@ +import type * as Abi from "@truffle/abi-utils"; + +import { Identifier } from "./identifier"; + +import type { Type } from "../type"; + +export type MissingBindings = "missing-bindings"; +export type MissingDeepBindings = "missing-deep-bindings"; +export type HasBindings = "has-bindings"; + +export type Bindings = + | MissingBindings + | MissingDeepBindings + | HasBindings; + +export type Kind = + | Kind.Interface + | Kind.Elementary + | Kind.Array + | Kind.Struct; + +export namespace Kind { + /* + * Elementary + */ + + export interface Elementary { + type: Type.Elementary; + hints?: { + internalType?: string; + } + } + + export const isElementary = ( + kind: Kind + ): kind is Elementary => "type" in kind; + + /* + * UserDefinedValueType (extends Elementary) + */ + + export interface UserDefinedValueType extends Elementary { + // UDVTs are meaningless without identifier (they'd just be elementary) + identifier: Identifier + + // UDVTs are a Solidity feature that depend on `internalType`, so require + // the corresponding hint + hints: { + internalType: string; + } + } + + export const isUserDefinedValueType = ( + kind: Kind + ): kind is UserDefinedValueType => + isElementary(kind) && "identifier" in kind; + + /* + * Array + */ + + export interface Array { + itemKind: Kind; + length?: number; + hints?: {}; // don't use internalType for arrays, for safety + } + + export const isArray = ( + kind: Kind + ): kind is Array => + "itemKind" in kind; + + export namespace Array { + export interface Static< + B extends Bindings + > extends Array { + length: number; + } + + export const isStatic = ( + kind: Kind + ): kind is Static => + isArray(kind) && typeof kind.length === "number"; + + export interface Dynamic< + B extends Bindings + > extends Array { + length: never; + } + + export const isDynamic = ( + kind: Kind + ): kind is Dynamic => + isArray(kind) && typeof kind.length !== "number"; + } + + /* + * Struct + */ + + export type Struct = + & { + signature: string; + hints?: { + internalType?: string; + } + } + & ( + B extends HasBindings + ? { + identifier: Identifier; + members: Struct.Member[]; + } + : B extends MissingDeepBindings + ? { + identifier: Identifier; + members: Struct.Member[]; + } + : B extends MissingBindings + ? { + identifier?: Identifier; + members: Struct.Member[]; + } + : { + identifier?: Identifier; + members: Struct.Member[]; + } + ); + + export const isStruct = ( + kind: Kind + ): kind is Struct => + "members" in kind; + + export namespace Struct { + export interface Member { + name?: string; + kind: Kind; + }; + } + + export type Interface = + B extends HasBindings + ? { + identifier: Identifier; + } + : B extends MissingDeepBindings + ? { + identifier: Identifier; + } + : B extends MissingBindings + ? { + identifier?: Identifier; + } + : { + identifier?: Identifier; + }; + + export const isInterface = ( + kind: Kind + ): kind is Interface => + // only has key identifier + Object.keys(kind).filter(key => key !== "identifier").length === 0 +} diff --git a/packages/abi-to-sol/src/declarations/types.ts b/packages/abi-to-sol/src/declarations/types.ts new file mode 100644 index 0000000..c0d765f --- /dev/null +++ b/packages/abi-to-sol/src/declarations/types.ts @@ -0,0 +1,214 @@ +import { Identifier } from "./identifier"; +import { Bindings, HasBindings, MissingDeepBindings, MissingBindings, Kind } from "./kind"; + +export type Declarations = { + byIdentifierReference: { + [reference: Identifier.Reference]: + | Kind.Interface + | Kind.UserDefinedValueType + | Kind.Struct + }; + unnamedBySignature: UnnamedBySignature; + globalIdentifiers: Set; + identifiersByContainer: { + [reference: Identifier.Interface.Reference]: Set; + }; +} + +export type UnnamedBySignature = + B extends HasBindings + ? { + [signature: string]: never; + } + : B extends MissingDeepBindings + ? { + [signature: string]: undefined; + } + : B extends MissingBindings + ? { + [signature: string]: Kind.Struct; // udvts are inherently named + } + : { + [signature: string]: Kind.Struct; // udvts are inherently named + }; + +/** + * Initialize an empty set of declarations + */ +export const empty = (): Declarations => ({ + byIdentifierReference: {}, + globalIdentifiers: new Set([]), + identifiersByContainer: {}, + unnamedBySignature: {} as UnnamedBySignature +}); + +/** + * Merge two sets of declarations + */ +export const merge = ( + a: Declarations, + b: Declarations +): Declarations => ({ + byIdentifierReference: { + ...a.byIdentifierReference, + ...b.byIdentifierReference + }, + unnamedBySignature: { + ...a.unnamedBySignature, + ...b.unnamedBySignature + }, + globalIdentifiers: new Set([ + ...a.globalIdentifiers, + ...b.globalIdentifiers + ]), + identifiersByContainer: mergeIdentifiersByContainer( + a.identifiersByContainer, + b.identifiersByContainer + ) +}); + +/** + * Generate declarations to include a single Kind. + * Note! This does not recurse; e.g. it returns empty() for arrays always + */ +export const from = ( + kind: Kind +): Declarations => { + if (Kind.isInterface(kind)) { + return fromInterface(kind); + } + + if (Kind.isStruct(kind)) { + return fromStruct(kind); + } + + if (Kind.isUserDefinedValueType(kind)) { + return fromUserDefinedValueType(kind); + }; + + return empty(); +} + +const fromUserDefinedValueType = ( + kind: Kind.UserDefinedValueType +): Declarations => { + const { identifier } = kind; + const reference = Identifier.toReference(identifier); + const { container } = identifier; + + // globally-defined case + if (!container) { + return { + byIdentifierReference: { + [reference]: kind + }, + unnamedBySignature: {} as UnnamedBySignature, + globalIdentifiers: new Set([reference]), + identifiersByContainer: {} + }; + } + + // defined inside containing contract/interface + const containerDeclarations = fromInterface({ identifier: container }); + const containerReference = Identifier.toReference(container); + + return merge(containerDeclarations, { + byIdentifierReference: { + [reference]: kind, + }, + unnamedBySignature: {} as UnnamedBySignature, + globalIdentifiers: new Set([]), + identifiersByContainer: { + [containerReference]: new Set([reference]) + } + }); +} + +const fromStruct = ( + kind: Kind.Struct +): Declarations => { + const { identifier } = kind; + + // unnamed case + if (!identifier) { + const { signature } = kind; + + return { + byIdentifierReference: {}, + unnamedBySignature: { + [signature]: kind + } as UnnamedBySignature, + globalIdentifiers: new Set([]), + identifiersByContainer: {}, + }; + } + + const reference = Identifier.toReference(identifier); + const { container } = identifier; + + // globally-defined case + if (!container) { + return { + byIdentifierReference: { + [reference]: kind + }, + unnamedBySignature: {} as UnnamedBySignature, + globalIdentifiers: new Set([reference]), + identifiersByContainer: {} + }; + } + + // defined inside containing contract/interface + const containerDeclarations = fromInterface({ identifier: container }); + + const containerReference = Identifier.toReference(container); + + return merge(containerDeclarations, { + // defined inside interface case + byIdentifierReference: { + [reference]: kind, + }, + unnamedBySignature: {} as UnnamedBySignature, + globalIdentifiers: new Set([]), + identifiersByContainer: { + [containerReference]: new Set([reference]) + } + }); +} + +const fromInterface = ( + kind: Kind.Interface +): Declarations => { + const { identifier } = kind; + if (!identifier) { + return empty(); + } + + const reference = Identifier.toReference(identifier); + + return { + byIdentifierReference: { + [reference]: kind, + }, + unnamedBySignature: {} as UnnamedBySignature, + globalIdentifiers: new Set([reference]), + identifiersByContainer: {} + }; +}; + +const mergeIdentifiersByContainer = ( + a: Declarations["identifiersByContainer"], + b: Declarations["identifiersByContainer"] +) => + ([...new Set([ + ...Object.keys(a), + ...Object.keys(b) + ])] as Identifier.Interface.Reference[]) + .map((containerReference: Identifier.Interface.Reference) => ({ + [containerReference]: new Set([ + ...(a[containerReference] || []), + ...(b[containerReference] || []) + ]) + })) + .reduce((a, b) => ({ ...a, ...b }), {}) + diff --git a/packages/abi-to-sol/src/index.ts b/packages/abi-to-sol/src/index.ts index 0c1f3c2..f1c5adc 100644 --- a/packages/abi-to-sol/src/index.ts +++ b/packages/abi-to-sol/src/index.ts @@ -1,6 +1,6 @@ -import "source-map-support/register"; - -export * as defaults from "./defaults"; -export {generateSolidity} from "./solidity"; -export {GenerateSolidityOptions, GenerateSolidityMode} from "./options"; - +export { + generateSolidity, + GenerateSolidityOptions, + GenerateSolidityMode, + defaults +} from "./solidity"; diff --git a/packages/abi-to-sol/src/parameter.ts b/packages/abi-to-sol/src/parameter.ts new file mode 100644 index 0000000..a5765d0 --- /dev/null +++ b/packages/abi-to-sol/src/parameter.ts @@ -0,0 +1,188 @@ +import * as Abi from "@truffle/abi-utils"; +import { abiTupleSignature } from "@truffle/abi-utils"; + +import { Type, isType } from "./type"; + +export type Parameter = Abi.Parameter & { + type: Type +}; + +export const isParameter = ( + parameter: Abi.Parameter +): parameter is Parameter => isType(parameter.type); + +export namespace Parameter { + export type Elementary = Abi.Parameter & { + type: Type.Elementary + }; + + export const isElementary = ( + parameter: Parameter + ): parameter is Elementary => Type.isElementary(parameter.type); + + export type UserDefinedValueType = Elementary & { + internalType: string + } + + export const isUserDefinedValueType = ( + parameter: Parameter + ): parameter is UserDefinedValueType => + isElementary(parameter) && + !!parameter.internalType && + parameter.internalType !== parameter.type && + UserDefinedValueType.internalTypePattern.test(parameter.internalType); + + export namespace UserDefinedValueType { + export const internalTypePattern = new RegExp( + /^(([a-zA-Z$_][a-zA-Z0-9$_]*)\.)?([a-zA-Z$_][a-zA-Z0-9$_]*)$/ + ); + + export type RecognizeResult

= ( + P extends Parameter.UserDefinedValueType + ? { + name: string; + scope?: string + } + : { + name: string; + scope?: string + } | undefined + ); + + export const recognize =

( + parameter: P + ): RecognizeResult

=> { + const { type, internalType } = parameter; + + if (!internalType || internalType === type) { + return undefined as RecognizeResult

; + } + + const match = internalType.match(internalTypePattern); + if (!match) { + return undefined as RecognizeResult

; + } + + const scope = match[2]; + const name = match[3]; + + return { + name, + ...( + scope + ? { scope } + : {} + ) + } as RecognizeResult

; + }; + } + + export type Array = Parameter & { + type: Type.Array + }; + + export const isArray = ( + parameter: Parameter + ): parameter is Parameter.Array => Type.isArray(parameter.type); + + export namespace Array { + export const item = ( + parameter: Parameter.Array + ): Parameter => { + const type = Type.Array.underlying(parameter.type); + + let internalType; + { + const match = (parameter.internalType || "").match(/^(.+)\[[^\]]*\]$/); + if (match) { + const [_, underlying] = match; + internalType = underlying; + } + } + + return { + ...parameter, + type, + ...( + internalType && internalType !== "tuple" + ? { internalType } + : {} + ) + }; + }; + + export type Static = Parameter.Array & { + type: Type.Array.Static + }; + + export const isStatic = ( + parameter: Parameter.Array + ): parameter is Parameter.Array.Static => + Type.Array.isStatic(parameter.type); + + export namespace Static { + export const length = ( + parameter: Parameter.Array.Static + ): number => Type.Array.length(parameter.type); + } + } + + export type Tuple = Parameter & { + type: Type.Tuple; + components: Exclude; + } + + export const isTuple = ( + parameter: Parameter + ): parameter is Parameter.Tuple => Type.isTuple(parameter.type); + + + export namespace Tuple { + export const internalTypePattern = new RegExp( + /^struct (([a-zA-Z$_][a-zA-Z0-9$_]*)\.)?([a-zA-Z$_][a-zA-Z0-9$_]*)$/ + ); + + export type TupleRecognizeResult = { + signature: string; + name?: string; + scope?: string + } + + export type RecognizeResult

= + P extends Parameter.Tuple + ? TupleRecognizeResult + : TupleRecognizeResult | undefined; + + export const recognize =

( + parameter: P + ): RecognizeResult

=> { + if (!Parameter.isTuple(parameter)) { + return undefined as RecognizeResult

; + } + + const signature = abiTupleSignature(parameter.components); + + if (!parameter.internalType) { + return { signature }; + } + + const match = parameter.internalType.match(internalTypePattern); + if (!match) { + return { signature }; + } + + const scope = match[2]; + const name = match[3]; + + return { + signature, + name, + ...( + scope + ? { scope } + : {} + ) + }; + }; + } +} diff --git a/packages/abi-to-sol/src/solidity.test.ts b/packages/abi-to-sol/src/solidity.test.ts deleted file mode 100644 index 1af80a2..0000000 --- a/packages/abi-to-sol/src/solidity.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import * as fc from "fast-check"; -import {testProp} from "jest-fast-check"; -import * as Abi from "@truffle/abi-utils"; -import * as Example from "../test/custom-example"; -import {compileAbi} from "../test/compile-abi"; -import {excludesFunctionParameters} from "../test/preflight"; - -import {generateSolidity} from "./solidity"; - -const removeProps = (obj: any, keys: Set) => { - if (obj instanceof Array) { - for (const item of obj) { - removeProps(item, keys); - } - } else if (typeof obj === "object") { - for (const [key, value] of Object.entries(obj)) { - if (keys.has(key)) { - delete obj[key]; - } else { - removeProps(obj[key], keys); - } - } - } - - return obj; -}; - -describe("generateSolidity", () => { - testProp("compiles to input ABI", [Abi.Arbitrary.Abi()], (abi) => { - fc.pre( - abi.every((entry) => "type" in entry && entry.type !== "constructor") - ); - fc.pre(excludesFunctionParameters(abi)); - - fc.pre(abi.length > 0); - - const output = generateSolidity({ - name: "MyInterface", - abi, - solidityVersion: "^0.8.4", - }); - - let resultAbi; - try { - resultAbi = compileAbi(output); - } catch (error) { - console.log("Failed to compile. Solidity:\n%s", output); - throw error; - } - - const compiledAbi = new Set( - removeProps(resultAbi, new Set(["internalType"])) - ); - - const expectedAbi = new Set(Abi.normalize(abi)); - - expect(compiledAbi).toEqual(expectedAbi); - }); - - describe("custom example", () => { - const abiWithoutConstructor = Abi.normalize( - Example.abi.filter(({type}) => type !== "constructor") - ); - - const output = generateSolidity({ - name: "Example", - abi: abiWithoutConstructor, - solidityVersion: "^0.8.4", - }); - - it("generates output", () => { - const compiledAbi = compileAbi(output); - - const expectedAbi = abiWithoutConstructor.map((entry) => ({ - ...entry, - type: entry.type || "function", - })); - - expect(compiledAbi).toEqual(expectedAbi); - }); - }); -}); diff --git a/packages/abi-to-sol/src/solidity.ts b/packages/abi-to-sol/src/solidity.ts deleted file mode 100644 index 352be94..0000000 --- a/packages/abi-to-sol/src/solidity.ts +++ /dev/null @@ -1,554 +0,0 @@ -import type Prettier from "prettier"; -import type * as Abi from "@truffle/abi-utils"; -import { abiTupleSignature } from "@truffle/abi-utils"; -import type {Abi as SchemaAbi} from "@truffle/contract-schema/spec"; -import { version } from "../package.json"; -import {Visitor, VisitOptions, dispatch, Node} from "./visitor"; -import { forRange, VersionFeatures, mixed } from "./version-features"; -import { GenerateSolidityOptions, GenerateSolidityMode } from "./options"; -import * as defaults from "./defaults"; -import { - Component, - Declaration, - Declarations, - collectDeclarations, -} from "./declarations"; -import { collectAbiFeatures, AbiFeatures } from "./abi-features"; - -let prettier: typeof Prettier -try { - prettier = require("prettier"); -} catch { - // no-op -} - -export const generateSolidity = ({ - abi, - name = defaults.name, - solidityVersion = defaults.solidityVersion, - license = defaults.license, - mode = defaults.mode, - outputAttribution = defaults.outputAttribution, - outputSource = defaults.outputSource, - prettifyOutput = prettier && defaults.prettifyOutput, -}: GenerateSolidityOptions) => { - if (!prettier && prettifyOutput) { - throw new Error("Could not require() prettier"); - } - - const versionFeatures = forRange(solidityVersion); - const abiFeatures = collectAbiFeatures(abi); - const declarations = collectDeclarations(abi); - - const generated = dispatch({ - node: abi, - visitor: new SolidityGenerator({ - name, - solidityVersion, - license, - mode, - outputAttribution, - outputSource, - versionFeatures, - abiFeatures, - declarations, - }), - }); - - if (!prettifyOutput) { - return generated; - } - - try { - return prettier.format(generated, { - plugins: ["prettier-plugin-solidity"], - // @ts-ignore - parser: "solidity-parse", - }); - } catch (error) { - return generated; - } -}; - -interface Context { - interfaceName?: string; - parameterModifiers?: (parameter: Abi.Parameter) => string[]; -} - -type Visit = VisitOptions; - -type ConstructorOptions = { - versionFeatures: VersionFeatures; - abiFeatures: AbiFeatures; - declarations: Declarations; -} & Required< - Omit ->; - -const shimGlobalInterfaceName = "__Structs"; - -class SolidityGenerator implements Visitor { - private name: string; - private license: string; - private mode: GenerateSolidityMode; - private solidityVersion: string; - private outputAttribution: boolean; - private outputSource: boolean; - private versionFeatures: VersionFeatures; - private abiFeatures: AbiFeatures; - private declarations: Declarations; - private identifiers: { - [signature: string]: { - identifier: string; - container?: string; - } - }; - - constructor({ - name, - license, - mode, - outputAttribution, - outputSource, - solidityVersion, - versionFeatures, - abiFeatures, - declarations, - }: ConstructorOptions) { - this.name = name; - this.license = license; - this.mode = mode; - this.solidityVersion = solidityVersion; - this.versionFeatures = versionFeatures; - this.abiFeatures = abiFeatures; - this.declarations = declarations; - this.outputAttribution = outputAttribution; - this.outputSource = outputSource; - - this.identifiers = {}; - let index = 0; - for (const [container, signatures] of Object.entries(declarations.containerSignatures)) { - for (const signature of signatures) { - const { - identifier = `S_${index++}` - } = declarations.signatureDeclarations[signature]; - - if (container === "" && this.versionFeatures["global-structs"] !== true) { - this.identifiers[signature] = { - container: shimGlobalInterfaceName, - identifier - }; - } else if (container === "") { - this.identifiers[signature] = { identifier }; - } else { - this.identifiers[signature] = { - container, - identifier - } - } - } - } - } - - visitAbi({node: abi}: Visit) { - switch (this.mode) { - case GenerateSolidityMode.Normal: { - return [ - this.generateHeader(), - this.generateInterface(abi), - this.generateDeclarations(), - this.generateAutogeneratedNotice(abi), - ].join("\n\n"); - } - case GenerateSolidityMode.Embedded: { - return [ - this.generateInterface(abi), - this.generateDeclarations(), - ].join("\n\n"); - } - } - } - - visitFunctionEntry({node: entry, context}: Visit): string { - const {name, inputs, stateMutability} = entry; - - return [ - `function ${name}(`, - entry.inputs.map((node) => - dispatch({ - node, - visitor: this, - context: { - ...context, - parameterModifiers: (parameter: Abi.Parameter) => - parameter.type.startsWith("tuple") || - parameter.type.includes("[") || - parameter.type === "bytes" || - parameter.type === "string" - ? [this.generateArrayParameterLocation(parameter)] - : [], - }, - }) - ), - `) external`, - this.generateStateMutability(entry), - entry.outputs && entry.outputs.length > 0 - ? [ - `returns (`, - entry.outputs - .map((node) => - dispatch({ - node, - visitor: this, - context: { - parameterModifiers: (parameter: Abi.Parameter) => - parameter.type.startsWith("tuple") || - parameter.type.includes("[") || - parameter.type === "bytes" || - parameter.type === "string" - ? ["memory"] - : [], - }, - }) - ) - .join(", "), - `)`, - ].join("") - : ``, - `;`, - ].join(" "); - } - - visitConstructorEntry({node: entry}: Visit): string { - // interfaces don't have constructors - return ""; - } - - visitFallbackEntry({ node: entry }: Visit): string { - const servesAsReceive = this.abiFeatures["defines-receive"] && - this.versionFeatures["receive-keyword"] !== true; - - const { stateMutability } = entry; - return `${this.generateFallbackName()} () external ${ - stateMutability === "payable" || servesAsReceive ? "payable" : "" - };`; - } - - visitReceiveEntry() { - // if version has receive, emit as normal - if (this.versionFeatures["receive-keyword"] === true) { - return `receive () external payable;`; - } - - // if this ABI defines a fallback separately, emit nothing, since - // visitFallbackEntry will cover it - if (this.abiFeatures["defines-fallback"]) { - return ""; - } - - // otherwise, explicitly invoke visitFallbackEntry - return this.visitFallbackEntry({ - node: { type: "fallback", stateMutability: "payable" }, - }); - } - - visitEventEntry({node: entry, context}: Visit): string { - const {name, inputs, anonymous} = entry; - - return [ - `event ${name}(`, - inputs.map((node) => - dispatch({ - node, - visitor: this, - context: { - ...context, - parameterModifiers: (parameter: Abi.Parameter) => - // TODO fix this - (parameter as Abi.EventParameter).indexed ? ["indexed"] : [], - }, - }) - ), - `)`, - `${anonymous ? "anonymous" : ""};`, - ].join(" "); - } - - visitErrorEntry({node: entry, context}: Visit): string { - if (this.versionFeatures["custom-errors"] !== true) { - throw new Error("ABI defines custom errors; use Solidity v0.8.4 or higher"); - } - - const {name, inputs} = entry; - - return [ - `error ${name}(`, - inputs.map((node) => - dispatch({ - node, - visitor: this, - context: { - ...context, - parameterModifiers: (parameter: Abi.Parameter) => [] - }, - }) - ), - `);`, - ].join(" "); - } - - visitParameter({node: parameter, context}: Visit) { - const type = this.generateType(parameter, context); - - // @ts-ignore - const {parameterModifiers} = context; - - return [type, ...parameterModifiers(parameter), parameter.name].join(" "); - } - - private generateHeader(): string { - const includeExperimentalPragma = - this.abiFeatures["needs-abiencoder-v2"] && - this.versionFeatures["abiencoder-v2"] !== "default"; - - const attribution = - !this.outputAttribution - ? [] - : [this.generateAttribution()] - - return [ - `// SPDX-License-Identifier: ${this.license}`, - ...attribution, - `pragma solidity ${this.solidityVersion};`, - ...( - includeExperimentalPragma - ? [`pragma experimental ABIEncoderV2;`] - : [] - ) - ].join("\n"); - } - - private generateAttribution(): string { - const unit = this.mode === GenerateSolidityMode.Normal - ? "FILE" - : "INTERFACE" - return this.outputSource - ? `// !! THIS ${unit} WAS AUTOGENERATED BY abi-to-sol v${version}. SEE SOURCE BELOW. !!` - : `// !! THIS ${unit} WAS AUTOGENERATED BY abi-to-sol v${version}. !!`; - } - - private generateAutogeneratedNotice(abi: Abi.Abi): string { - if (!this.outputSource) { - return ""; - } - - return [ - ``, - `// THIS FILE WAS AUTOGENERATED FROM THE FOLLOWING ABI JSON:`, - `/*`, - JSON.stringify(abi), - `*/`, - ].join("\n"); - } - - private generateDeclarations(): string { - if ( - this.versionFeatures["structs-in-interfaces"] !== true && - Object.keys(this.declarations.signatureDeclarations).length > 0 - ) { - throw new Error( - "abi-to-sol does not support custom struct types for this Solidity version" - ); - } - - const externalContainers = Object.keys(this.declarations.containerSignatures) - .filter(container => container !== "" && container !== this.name); - - const externalDeclarations = externalContainers - .map(container => [ - `interface ${container} {`, - this.generateDeclarationsForContainer(container), - `}` - ].join("\n")) - .join("\n\n"); - - const globalSignatures = this.declarations.containerSignatures[""] || []; - if (globalSignatures.length > 0) { - const declarations = this.versionFeatures["global-structs"] === true - ? this.generateDeclarationsForContainer("") - : [ - `interface ${shimGlobalInterfaceName} {`, - this.generateDeclarationsForContainer(""), - `}` - ].join("\n"); - - return [declarations, externalDeclarations].join("\n\n"); - } - - return externalDeclarations; - } - - private generateDeclarationsForContainer(container: string): string { - const signatures = new Set( - this.declarations.containerSignatures[container] - ); - - if (container === "" && this.versionFeatures["global-structs"] !== true) { - container = shimGlobalInterfaceName; - } - - return Object.entries(this.declarations.signatureDeclarations) - .filter(([signature]) => signatures.has(signature)) - .map(([signature, declaration]) => { - const { identifier } = this.identifiers[signature]; - const components = this.generateComponents(declaration, { interfaceName: container }); - - return `struct ${identifier} { ${components} }`; - }) - .join("\n\n"); - } - - private generateComponents( - declaration: Declaration, - context?: Pick - ): string { - return declaration.components - .map((component) => { - const {name} = component; - - return `${this.generateType(component, context)} ${name};`; - }) - .join("\n"); - } - - private generateType( - variable: Abi.Parameter | Component, - context: Pick = {} - ): string { - const signature = this.generateSignature(variable); - - if (!signature) { - return this.generateElementaryType(variable, context); - } - - const { type } = variable; - - const { container, identifier } = this.identifiers[signature]; - - if (container && container !== context.interfaceName) { - return type.replace("tuple", `${container}.${identifier}`); - } - - if (!container && this.versionFeatures["global-structs"] !== true) { - return type.replace("tuple", `${shimGlobalInterfaceName}.${identifier}`); - } - - return type.replace("tuple", identifier); - } - - private generateElementaryType( - variable: Abi.Parameter | Component, - context: Pick = {} - ): string { - // normally we can return the type itself, but functions are a special case - if (variable.type !== "function") { - return variable.type; - } - - // use just the `internalType` field if it exists - if ("internalType" in variable && variable.internalType) { - return variable.internalType; - } - - // otherwise output minimally syntactically-valid syntax with a warning - return [ - "/* warning: the following type may be incomplete. ", - "the receiving contract may expect additional input or output parameters. */ ", - "function() external" - ].join(""); - } - - - private generateSignature( - variable: Abi.Parameter | Component - ): string | undefined { - if ("signature" in variable && variable.signature) { - return variable.signature; - } - - if ("components" in variable && variable.components) { - return abiTupleSignature(variable.components); - } - } - - private generateStateMutability( - entry: - | Abi.FunctionEntry - | Abi.FallbackEntry - | Abi.ConstructorEntry - | Abi.ReceiveEntry - ): string { - if (entry.stateMutability && entry.stateMutability !== "nonpayable") { - return entry.stateMutability; - } - - return ""; - } - - private generateFallbackName(): string { - switch (this.versionFeatures["fallback-keyword"]) { - case true: { - return "fallback"; - } - case false: { - return "function"; - } - case mixed: { - throw new Error( - `Desired Solidity range lacks unambigious fallback syntax.` - ); - } - } - } - - private generateArrayParameterLocation(parameter: Abi.Parameter): string { - switch (this.versionFeatures["array-parameter-location"]) { - case undefined: { - return ""; - } - case mixed: { - throw new Error( - `Desired Solidity range lacks unambiguous location specifier for ` + - `parameter of type "${parameter.type}".` - ); - } - default: { - return this.versionFeatures["array-parameter-location"]; - } - } - } - - private generateInterface(abi: Abi.Abi): string { - return [ - `interface ${this.name} {`, - ...( - this.mode === GenerateSolidityMode.Embedded && this.outputAttribution - ? [this.generateAttribution()] - : [] - ), - this.generateDeclarationsForContainer(this.name), - ``, - ...abi.map((node) => dispatch({ - node, - context: { interfaceName: this.name }, - visitor: this - })), - ...( - this.mode === GenerateSolidityMode.Embedded - ? [this.generateAutogeneratedNotice(abi)] - : [] - ), - `}`, - ].join("\n"); - } -} diff --git a/packages/abi-to-sol/src/abi-features.ts b/packages/abi-to-sol/src/solidity/analyze.ts similarity index 68% rename from packages/abi-to-sol/src/abi-features.ts rename to packages/abi-to-sol/src/solidity/analyze.ts index 7710453..4193a2f 100644 --- a/packages/abi-to-sol/src/abi-features.ts +++ b/packages/abi-to-sol/src/solidity/analyze.ts @@ -1,40 +1,40 @@ import type { Abi as SchemaAbi } from "@truffle/contract-schema/spec"; import type * as Abi from "@truffle/abi-utils"; -import { Visitor, VisitOptions, dispatch, Node } from "./visitor"; +import { Visitor, VisitOptions, dispatch, Node } from "../visitor"; -export const allFeatures = [ +export const observableProperties = [ "defines-receive", "defines-fallback", "needs-abiencoder-v2", "defines-error", ] as const; -export type AbiFeature = typeof allFeatures[number]; -export type AbiFeatures = Partial<{ - [F in AbiFeature]: true +export type AbiProperty = typeof observableProperties[number]; +export type AbiProperties = Partial<{ + [F in AbiProperty]: true }>; -export const collectAbiFeatures = (node: SchemaAbi | Node) => +export const analyze = (node: SchemaAbi | Node) => dispatch({ node, - visitor: new AbiFeaturesCollector(), + visitor: new AbiPropertiesCollector(), }); -export class AbiFeaturesCollector implements Visitor { - visitAbi({ node: nodes }: VisitOptions): AbiFeatures { +export class AbiPropertiesCollector implements Visitor { + visitAbi({ node: nodes }: VisitOptions): AbiProperties { return nodes .map((node) => dispatch({ node, visitor: this })) .reduce((a, b) => ({ ...a, ...b }), {}); } - visitEventEntry({ node: entry }: VisitOptions): AbiFeatures { + visitEventEntry({ node: entry }: VisitOptions): AbiProperties { return entry.inputs .map((node) => dispatch({ node, visitor: this })) .reduce((a, b) => ({ ...a, ...b }), {}); } - visitErrorEntry({ node: entry }: VisitOptions): AbiFeatures { + visitErrorEntry({ node: entry }: VisitOptions): AbiProperties { return entry.inputs .map((node) => dispatch({ node, visitor: this })) .reduce((a, b) => ({ ...a, ...b }), {}); @@ -42,7 +42,7 @@ export class AbiFeaturesCollector implements Visitor { visitFunctionEntry({ node: entry, - }: VisitOptions): AbiFeatures { + }: VisitOptions): AbiProperties { return [...entry.inputs, ...(entry.outputs || [])] .map((node) => dispatch({ node, visitor: this })) .reduce((a, b) => ({ ...a, ...b }), {}); @@ -50,7 +50,7 @@ export class AbiFeaturesCollector implements Visitor { visitConstructorEntry({ node: entry, - }: VisitOptions): AbiFeatures { + }: VisitOptions): AbiProperties { return entry.inputs .map((node) => dispatch({ node, visitor: this })) .reduce((a, b) => ({ ...a, ...b }), {}); @@ -58,19 +58,19 @@ export class AbiFeaturesCollector implements Visitor { visitFallbackEntry({ node: entry, - }: VisitOptions): AbiFeatures { + }: VisitOptions): AbiProperties { return { "defines-fallback": true }; } visitReceiveEntry({ node: entry, - }: VisitOptions): AbiFeatures { + }: VisitOptions): AbiProperties { return { "defines-receive": true }; } visitParameter({ node: parameter, - }: VisitOptions): AbiFeatures { + }: VisitOptions): AbiProperties { if ( parameter.type.startsWith("tuple") || // anything with tuples parameter.type.includes("string[") || // arrays of strings diff --git a/packages/abi-to-sol/src/defaults.ts b/packages/abi-to-sol/src/solidity/defaults.ts similarity index 100% rename from packages/abi-to-sol/src/defaults.ts rename to packages/abi-to-sol/src/solidity/defaults.ts diff --git a/packages/abi-to-sol/src/solidity/features.ts b/packages/abi-to-sol/src/solidity/features.ts new file mode 100644 index 0000000..1666572 --- /dev/null +++ b/packages/abi-to-sol/src/solidity/features.ts @@ -0,0 +1,133 @@ +import * as semver from "semver"; + +export const allFeatures = { + "receive-keyword": { + ">=0.6.0": true, + "<0.6.0": false + }, + "fallback-keyword": { + ">=0.6.0": true, + "<0.6.0": false + }, + "array-parameter-location": { + ">=0.7.0": "memory", + "^0.5.0 || ^0.6.0": "calldata", + "<0.5.0": undefined + }, + "abiencoder-v2": { + ">=0.8.0": "default", + "<0.8.0": "experimental", + }, + "global-structs": { + ">=0.6.0": true, + "<0.6.0": false + }, + "structs-in-interfaces": { + ">=0.5.0": true, + "<0.5.0": false + }, + "custom-errors": { + ">=0.8.4": true, + "<0.8.4": false + }, + "user-defined-value-types": { + ">=0.8.8": true, + "<0.8.8": false + } +} as const; + +export type AllFeatures = typeof allFeatures; + +export type Category = keyof AllFeatures; + +export type CategorySpecification = AllFeatures[C]; + +export type CategoryOptionRange = string & { + [K in C]: keyof CategorySpecification +}[C]; + +export type CategoryOption = { + [K in C]: CategorySpecification[CategoryOptionRange] +}[C]; + +export type BooleanCategory = { + [C in Category]: CategoryOption extends boolean + ? C + : never +}[Category]; + +export const isBooleanCategory = ( + category: Category +): category is BooleanCategory => { + return Object.values(allFeatures[category]) + .every(option => option === true || option === false); +}; + +export type VersionsFeatures = { + [C in Category]: VersionsFeature; +}; + +export type VersionsFeature = + C extends BooleanCategory + ? { + supported(): boolean; + missing(): boolean; + varies(): boolean; + } + : { + consistently(option: CategoryOption): boolean; + }; + +export const forRange = (range: string | semver.Range): VersionsFeatures => { + return (Object.keys(allFeatures) as Category[]) + .map((category: C) => { + const specification = allFeatures[category]; + const matchingRanges: CategoryOptionRange[] = + (Object.keys(specification) as CategoryOptionRange[]) + .filter(optionRange => semver.intersects(range, optionRange)); + + const matchingOptions: CategoryOption[] = [...new Set( + matchingRanges.map(range => specification[range]) + )]; + + if (isBooleanCategory(category)) { + return { + [category]: { + supported() { + return ( + matchingOptions.length === 1 && + matchingOptions[0] === true as unknown as CategoryOption + ); + }, + + missing() { + return ( + matchingOptions.indexOf( + true as unknown as CategoryOption + ) === -1 + ) + }, + + varies() { + return matchingOptions.length > 1; + } + } + }; + } + + return { + [category]: { + consistently(option: CategoryOption) { + if (matchingOptions.length !== 1) { + return false; + } + + const [onlyOption] = matchingOptions; + return option === onlyOption; + } + } as VersionsFeature + }; + + }) + .reduce((a, b) => ({ ...a, ...b }), {}) as VersionsFeatures; +} diff --git a/packages/abi-to-sol/src/solidity/generate.ts b/packages/abi-to-sol/src/solidity/generate.ts new file mode 100644 index 0000000..f564a28 --- /dev/null +++ b/packages/abi-to-sol/src/solidity/generate.ts @@ -0,0 +1,505 @@ +import type * as Abi from "@truffle/abi-utils"; + +import { version } from "../../package.json"; +import { Declarations, Identifier, Kind } from "../declarations"; +import { Visitor, VisitOptions, dispatch, Node } from "../visitor"; +import type { VersionsFeatures } from "./features"; +import type { AbiProperties } from "./analyze"; +import { printType } from "./print"; + +import { GenerateSolidityOptions, GenerateSolidityMode } from "./options"; + +interface Context { + interfaceName?: string; + parameterModifiers?: (parameter: Abi.Parameter) => string[]; +} + +type Visit = VisitOptions; + +type ConstructorOptions = { + versionsFeatures: VersionsFeatures; + abiProperties: AbiProperties; + declarations: Declarations; +} & Required< + Omit +>; + +const shimGlobalInterfaceName = "__Structs"; + +export const generateRawSolidity = ( + abi: GenerateSolidityOptions["abi"], + options: ConstructorOptions +) => dispatch({ node: abi, visitor: new SolidityGenerator(options) }); + +class SolidityGenerator implements Visitor { + private name: string; + private license: string; + private mode: GenerateSolidityMode; + private solidityVersion: string; + private outputAttribution: boolean; + private outputSource: boolean; + private versionsFeatures: VersionsFeatures; + private abiProperties: AbiProperties; + private declarations: Declarations; + + constructor({ + name, + license, + mode, + outputAttribution, + outputSource, + solidityVersion, + versionsFeatures, + abiProperties, + declarations + }: ConstructorOptions) { + this.name = name; + this.license = license; + this.mode = mode; + this.solidityVersion = solidityVersion; + this.versionsFeatures = versionsFeatures; + this.abiProperties = abiProperties; + this.declarations = declarations; + this.outputAttribution = outputAttribution; + this.outputSource = outputSource; + } + + visitAbi({node: abi}: Visit) { + switch (this.mode) { + case GenerateSolidityMode.Normal: { + return [ + this.generateHeader(), + this.generateInterface(abi), + this.generateExternals(), + this.generateSourceNotice(abi), + ].join("\n\n"); + } + case GenerateSolidityMode.Embedded: { + return [ + this.generateInterface(abi), + this.generateExternals(), + ].join("\n\n"); + } + } + } + + visitFunctionEntry({node: entry, context}: Visit): string { + const {name, inputs, stateMutability} = entry; + + return [ + `function ${name}(`, + entry.inputs.map((node) => + dispatch({ + node, + visitor: this, + context: { + ...context, + parameterModifiers: (parameter: Abi.Parameter) => + parameter.type.startsWith("tuple") || + parameter.type.includes("[") || + parameter.type === "bytes" || + parameter.type === "string" + ? [this.generateArrayParameterLocation(parameter)] + : [], + }, + }) + ), + `) external`, + this.generateStateMutability(entry), + entry.outputs && entry.outputs.length > 0 + ? [ + `returns (`, + entry.outputs + .map((node) => + dispatch({ + node, + visitor: this, + context: { + parameterModifiers: (parameter: Abi.Parameter) => + parameter.type.startsWith("tuple") || + parameter.type.includes("[") || + parameter.type === "bytes" || + parameter.type === "string" + ? ["memory"] + : [], + }, + }) + ) + .join(", "), + `)`, + ].join("") + : ``, + `;`, + ].join(" "); + } + + visitConstructorEntry({node: entry}: Visit): string { + // interfaces don't have constructors + return ""; + } + + visitFallbackEntry({ node: entry }: Visit): string { + const servesAsReceive = this.abiProperties["defines-receive"] && + !this.versionsFeatures["receive-keyword"].supported(); + + const { stateMutability } = entry; + return `${this.generateFallbackName()} () external ${ + stateMutability === "payable" || servesAsReceive ? "payable" : "" + };`; + } + + visitReceiveEntry() { + // if version has receive, emit as normal + if (this.versionsFeatures["receive-keyword"].supported()) { + return `receive () external payable;`; + } + + // if this ABI defines a fallback separately, emit nothing, since + // visitFallbackEntry will cover it + if (this.abiProperties["defines-fallback"]) { + return ""; + } + + // otherwise, explicitly invoke visitFallbackEntry + return this.visitFallbackEntry({ + node: { type: "fallback", stateMutability: "payable" }, + }); + } + + visitEventEntry({node: entry, context}: Visit): string { + const {name, inputs, anonymous} = entry; + + return [ + `event ${name}(`, + inputs.map((node) => + dispatch({ + node, + visitor: this, + context: { + ...context, + parameterModifiers: (parameter: Abi.Parameter) => + // TODO fix this + (parameter as Abi.EventParameter).indexed ? ["indexed"] : [], + }, + }) + ), + `)`, + `${anonymous ? "anonymous" : ""};`, + ].join(" "); + } + + visitErrorEntry({node: entry, context}: Visit): string { + if (!this.versionsFeatures["custom-errors"].supported()) { + throw new Error("ABI defines custom errors; use Solidity v0.8.4 or higher"); + } + + const {name, inputs} = entry; + + return [ + `error ${name}(`, + inputs.map((node) => + dispatch({ + node, + visitor: this, + context: { + ...context, + parameterModifiers: (parameter: Abi.Parameter) => [] + }, + }) + ), + `);`, + ].join(" "); + } + + visitParameter({ node: parameter, context }: Visit) { + const kind = Declarations.find(parameter, this.declarations); + const type = printType(kind, { + currentInterfaceName: context?.interfaceName, + enableGlobalStructs: + this.versionsFeatures["global-structs"].supported(), + enableUserDefinedValueTypes: + this.versionsFeatures["user-defined-value-types"].supported(), + shimGlobalInterfaceName + }); + + // @ts-ignore + const { parameterModifiers } = context; + + return [type, ...parameterModifiers(parameter), parameter.name].join(" "); + } + + private generateHeader(): string { + const includeExperimentalPragma = + this.abiProperties["needs-abiencoder-v2"] && + !this.versionsFeatures["abiencoder-v2"].consistently("default"); + + const attribution = + !this.outputAttribution + ? [] + : [this.generateAttribution()] + + return [ + `// SPDX-License-Identifier: ${this.license}`, + ...attribution, + `pragma solidity ${this.solidityVersion};`, + ...( + includeExperimentalPragma + ? [`pragma experimental ABIEncoderV2;`] + : [] + ) + ].join("\n"); + } + + private generateAttribution(): string { + const unit = this.mode === GenerateSolidityMode.Normal + ? "FILE" + : "INTERFACE" + return this.outputSource + ? `// !! THIS ${unit} WAS AUTOGENERATED BY abi-to-sol v${version}. SEE SOURCE BELOW. !!` + : `// !! THIS ${unit} WAS AUTOGENERATED BY abi-to-sol v${version}. !!`; + } + + private generateSourceNotice(abi: Abi.Abi): string { + if (!this.outputSource) { + return ""; + } + + return [ + ``, + `// THIS FILE WAS AUTOGENERATED FROM THE FOLLOWING ABI JSON:`, + `/*`, + JSON.stringify(abi), + `*/`, + ].join("\n"); + } + + private generateExternals(): string { + if ( + !this.versionsFeatures["structs-in-interfaces"].supported() && + Object.values(this.declarations.byIdentifierReference).some( + ({ identifier }) => identifier.class === "struct" + ) + ) { + throw new Error( + "abi-to-sol does not support custom struct types for this Solidity version" + ); + } + + const isDeclarable = + this.versionsFeatures["user-defined-value-types"].supported() + ? (kind: Kind): kind is Kind.Struct | Kind.UserDefinedValueType => Kind.isStruct(kind) || Kind.isUserDefinedValueType(kind) + : Kind.isStruct; + + const hasDifferentContainer = ( + kind: Kind.Struct | Kind.UserDefinedValueType + ) => + !!kind.identifier.container && + kind.identifier.container.name !== this.name; + + const externalDeclarations = Object.entries( + this.declarations.identifiersByContainer + ).flatMap((pair) => { + const [ + containerIdentifierReference, + identifierReferences + ] = pair as [ + Identifier.Interface.Reference, + Set + ]; + + const kinds = new Set( + [...identifierReferences] + .map((identifierReference) => + this.declarations.byIdentifierReference[identifierReference] + ).filter( + (kind): kind is ( + & { identifier: { container: Identifier.Interface } } + & ( + | Kind.Struct + | Kind.UserDefinedValueType + ) + ) => { + return isDeclarable(kind) && hasDifferentContainer(kind); + } + ) + ); + + if (!kinds.size) { + return [] + } + + const { identifier: { container } } = [...kinds][0]; + + return [{ + container, + kinds + }]; + }).map(({ container, kinds }) => [ + `interface ${container.name} {`, + this.generateSiblingDeclarations(kinds, { + interfaceName: container.name + }), + `}` + ].join("\n")).join("\n\n"); + + const globalKinds = new Set([ + ...this.declarations.globalIdentifiers + ].flatMap(reference => { + const kind = this.declarations.byIdentifierReference[reference]; + + if (isDeclarable(kind)) { + return [kind]; + } + + return []; + })); + + if (globalKinds.size > 0) { + const globalDeclarations = this.generateSiblingDeclarations(globalKinds); + + return [ + externalDeclarations, + this.versionsFeatures["global-structs"].supported() + ? globalDeclarations + : [ + `interface ${shimGlobalInterfaceName} {`, + globalDeclarations, + `}` + ].join("\n") + ].join("\n\n"); + } + + return externalDeclarations; + } + + private generateSiblingDeclarations( + kinds: Set, + context: Pick = {} + ): string { + return [...kinds] + .map(kind => { + if (Kind.isStruct(kind)) { + return this.generateStructDeclaration(kind, context) + } + + if ( + this.versionsFeatures["user-defined-value-types"].supported() && + Kind.isUserDefinedValueType(kind) + ) { + return this.generateUserDefinedValueTypeDefinition(kind, context); + } + }) + .join("\n\n"); + } + + private generateStructDeclaration( + kind: Kind.Struct, + context: Pick = {} + ): string { + return [ + `struct ${kind.identifier.name} {`, + ...kind.members.map(({ name, kind: memberKind}) => + `${ + printType(memberKind, { + currentInterfaceName: context.interfaceName, + enableGlobalStructs: + this.versionsFeatures["global-structs"].supported(), + enableUserDefinedValueTypes: + this.versionsFeatures["user-defined-value-types"].supported(), + shimGlobalInterfaceName + }) + } ${name};` + ), + `}` + ].join("\n"); + } + + private generateUserDefinedValueTypeDefinition( + kind: Kind.UserDefinedValueType, + context: Pick = {} + ): string { + return [ + `type ${kind.identifier.name} is ${kind.type};` + ].join("\n"); + } + + + private generateStateMutability( + entry: + | Abi.FunctionEntry + | Abi.FallbackEntry + | Abi.ConstructorEntry + | Abi.ReceiveEntry + ): string { + if (entry.stateMutability && entry.stateMutability !== "nonpayable") { + return entry.stateMutability; + } + + return ""; + } + + private generateFallbackName(): string { + if (this.versionsFeatures["fallback-keyword"].supported()) { + return "fallback"; + } + + if (this.versionsFeatures["fallback-keyword"].missing()) { + return "function"; + } + + throw new Error( + `Desired Solidity range lacks unambigious fallback syntax.` + ); + } + + private generateArrayParameterLocation(parameter: Abi.Parameter): string { + const location = this.versionsFeatures["array-parameter-location"]; + + if (location.consistently(undefined)) { + return ""; + } + + if (location.consistently("memory")) { + return "memory"; + } + + if (location.consistently("calldata")) { + return "calldata"; + } + + throw new Error( + `Desired Solidity range lacks unambiguous location specifier for ` + + `parameter of type "${parameter.type}".` + ); + } + + private generateInterface(abi: Abi.Abi): string { + const kinds = new Set([ + ...(this.declarations.identifiersByContainer[ + Identifier.toReference({ class: "interface", name: this.name }) + ] || []) + ].map(reference => this.declarations.byIdentifierReference[reference])); + + return [ + `interface ${this.name} {`, + ...( + this.mode === GenerateSolidityMode.Embedded && this.outputAttribution + ? [this.generateAttribution()] + : [] + ), + this.generateSiblingDeclarations(kinds, { interfaceName: this.name }), + ``, + ...abi.map((node) => dispatch({ + node, + context: { interfaceName: this.name }, + visitor: this + })), + ...( + this.mode === GenerateSolidityMode.Embedded + ? [this.generateSourceNotice(abi)] + : [] + ), + `}`, + ].join("\n"); + } +} diff --git a/packages/abi-to-sol/src/solidity/index.test.ts b/packages/abi-to-sol/src/solidity/index.test.ts new file mode 100644 index 0000000..fea54fe --- /dev/null +++ b/packages/abi-to-sol/src/solidity/index.test.ts @@ -0,0 +1,185 @@ +import * as fc from "fast-check"; +import { testProp } from "jest-fast-check"; +import * as Abi from "@truffle/abi-utils"; +import * as Example from "../../test/custom-example"; +import { compileAbi } from "../../test/compile-abi"; +import { excludesFunctionParameters } from "../../test/preflight"; + +import { generateSolidity } from "."; +import { GenerateSolidityMode } from "./options"; + +const removeProps = (obj: any, keys: Set) => { + if (obj instanceof Array) { + for (const item of obj) { + removeProps(item, keys); + } + } else if (typeof obj === "object") { + for (const [key, value] of Object.entries(obj)) { + if (keys.has(key)) { + delete obj[key]; + } else { + removeProps(obj[key], keys); + } + } + } + + return obj; +}; + +describe("generateSolidity", () => { + testProp("respects output settings", [ + Abi.Arbitrary.Abi(), + fc.constantFrom(GenerateSolidityMode.Normal, GenerateSolidityMode.Embedded), + fc.boolean(), // outputAttribution + fc.boolean(), // outputSource + ], (abi, mode, outputAttribution, outputSource) => { + fc.pre(abi.length > 0); + + const output = generateSolidity({ + name: "MyInterface", + abi, + solidityVersion: "^0.8.20", + mode, + outputAttribution, + outputSource + }); + + const attributionOutput = output.indexOf("abi-to-sol") !== -1; + const sourceOutput = output.indexOf("FROM THE FOLLOWING ABI JSON:") !== -1; + const modeOutput = output.indexOf("SPDX-License-Identifier") === -1 + ? GenerateSolidityMode.Embedded + : GenerateSolidityMode.Normal; + + expect(mode).toEqual(modeOutput); + expect(outputAttribution).toEqual(attributionOutput); + expect(outputSource).toEqual(sourceOutput); + }); + + testProp("compiles to input ABI", [Abi.Arbitrary.Abi()], (abi) => { + fc.pre( + abi.every((entry) => "type" in entry && entry.type !== "constructor") + ); + fc.pre(excludesFunctionParameters(abi)); + + fc.pre(abi.length > 0); + + const output = generateSolidity({ + name: "MyInterface", + abi, + solidityVersion: "^0.8.20", + }); + + let resultAbi; + try { + resultAbi = compileAbi(output); + } catch (error) { + console.log("Failed to compile. Solidity:\n%s", output); + throw error; + } + + const compiledAbi = new Set( + removeProps(resultAbi, new Set(["internalType"])) + ); + + const expectedAbi = new Set(Abi.normalize(abi)); + + expect(compiledAbi).toEqual(expectedAbi); + }, + ); + + describe("custom example", () => { + const abiWithoutConstructor = Abi.normalize( + Example.abi.filter(({type}) => type !== "constructor") + ); + + const output = generateSolidity({ + name: "Example", + abi: abiWithoutConstructor, + solidityVersion: "^0.8.4", + }); + + it("generates output", () => { + const compiledAbi = compileAbi(output); + + const expectedAbi = abiWithoutConstructor.map((entry) => ({ + ...entry, + type: entry.type || "function", + })); + + expect(compiledAbi).toEqual(expectedAbi); + }); + }); + + describe("function pointers", () => { + const abi: Abi.Abi = [ + { + "inputs": [ + { + "internalType": "function (uint256) external returns (uint256)", + "name": "f", + "type": "function" + }, + { + "internalType": "uint256[]", + "name": "l", + "type": "uint256[]" + } + ], + "name": "map", + "outputs": [], + "stateMutability": "pure", + "type": "function" + } + ]; + + const output = generateSolidity({ + name: "Example", + abi, + solidityVersion: "^0.8.13", + }); + + it("generates output", () => { + const compiledAbi = compileAbi(output); + expect(compiledAbi).toEqual(abi); + }); + }); + + describe("UDVT support", () => { + const abi: Abi.Abi = [ + { + "inputs": [ + { + "internalType": "Int", + "name": "i", + "type": "int256" + }, + { + "internalType": "Example.Uint", + "name": "u", + "type": "uint256" + }, + { + "internalType": "Other.Bool", + "name": "b", + "type": "bool" + } + ], + "name": "fnoo", + "outputs": [], + "stateMutability": "pure", + "type": "function" + } + ]; + + const output = generateSolidity({ + name: "Example", + abi, + solidityVersion: "^0.8.13", + }); + + it("generates output", () => { + const compiledAbi = compileAbi(output); + expect(compiledAbi).toEqual(abi); + }); + }); +}); diff --git a/packages/abi-to-sol/src/solidity/index.ts b/packages/abi-to-sol/src/solidity/index.ts new file mode 100644 index 0000000..d92b7a6 --- /dev/null +++ b/packages/abi-to-sol/src/solidity/index.ts @@ -0,0 +1,64 @@ +import type Prettier from "prettier"; + +import { Declarations } from "../declarations"; + +import * as defaults from "./defaults"; +import * as Features from "./features"; +import { GenerateSolidityOptions } from "./options"; +import { generateRawSolidity } from "./generate"; +import { analyze } from "./analyze"; + +export { defaults }; +export { GenerateSolidityOptions, GenerateSolidityMode } from "./options"; + +let prettier: typeof Prettier +try { + prettier = require("prettier"); +} catch { + // no-op +} + +export const generateSolidity = ({ + abi, + name = defaults.name, + solidityVersion = defaults.solidityVersion, + license = defaults.license, + mode = defaults.mode, + outputAttribution = defaults.outputAttribution, + outputSource = defaults.outputSource, + prettifyOutput = prettier && defaults.prettifyOutput, +}: GenerateSolidityOptions) => { + if (!prettier && prettifyOutput) { + throw new Error("Could not require() prettier"); + } + + const versionsFeatures = Features.forRange(solidityVersion); + const abiProperties = analyze(abi); + const declarations = Declarations.collect(abi); + + const raw = generateRawSolidity(abi, { + name, + solidityVersion, + license, + mode, + outputAttribution, + outputSource, + versionsFeatures, + abiProperties, + declarations + }); + + if (!prettifyOutput) { + return raw; + } + + try { + return prettier.format(raw, { + plugins: ["prettier-plugin-solidity"], + // @ts-ignore + parser: "solidity-parse", + }); + } catch (error) { + return raw; + } +}; diff --git a/packages/abi-to-sol/src/options.ts b/packages/abi-to-sol/src/solidity/options.ts similarity index 99% rename from packages/abi-to-sol/src/options.ts rename to packages/abi-to-sol/src/solidity/options.ts index 95d3627..f901ebd 100644 --- a/packages/abi-to-sol/src/options.ts +++ b/packages/abi-to-sol/src/solidity/options.ts @@ -16,4 +16,3 @@ export interface GenerateSolidityOptions { outputSource?: boolean; prettifyOutput?: boolean; } - diff --git a/packages/abi-to-sol/src/solidity/print.ts b/packages/abi-to-sol/src/solidity/print.ts new file mode 100644 index 0000000..207c804 --- /dev/null +++ b/packages/abi-to-sol/src/solidity/print.ts @@ -0,0 +1,150 @@ +import { Kind } from "../declarations"; + +export const printType = ( + kind: Kind, + options: { + currentInterfaceName?: string; + enableUserDefinedValueTypes?: boolean; + enableGlobalStructs?: boolean; + shimGlobalInterfaceName?: string; + } = {} +): string => { + const { + currentInterfaceName, + enableUserDefinedValueTypes = false, + enableGlobalStructs = false, + shimGlobalInterfaceName + } = options; + + if (Kind.isUserDefinedValueType(kind)) { + return printUserDefinedValueTypeType(kind, { + currentInterfaceName, + enableUserDefinedValueTypes + }); + } + + if (Kind.isElementary(kind)) { + return printElementaryType(kind); + } + + if (Kind.isStruct(kind)) { + return printStructType(kind, { + currentInterfaceName, + enableGlobalStructs, + shimGlobalInterfaceName + }); + } + + if (Kind.isArray(kind)) { + return printArrayType(kind, options); + } + + throw new Error(`Unexpectedly unsupported kind: ${JSON.stringify(kind)}`); +} + +const printUserDefinedValueTypeType = ( + kind: Kind.UserDefinedValueType, + options: { + currentInterfaceName?: string; + enableUserDefinedValueTypes?: boolean; + } = {} +): string => { + const { + currentInterfaceName, + enableUserDefinedValueTypes = false + } = options; + + const result = ( + kind.identifier.container && + kind.identifier.container.name !== currentInterfaceName + ) + ? `${kind.identifier.container.name}.${kind.identifier.name}` + : kind.identifier.name; + + if (!enableUserDefinedValueTypes) { + return [ + `/* warning: missing UDVT support in source Solidity version; `, + `parameter is \`${result}\`. */ `, + kind.type + ].join(""); + } + + return result; +}; + +const printElementaryType = ( + kind: Kind.Elementary, + options: {} = {} +): string => { + if (kind.type !== "function") { + return kind.type; + } + + // use just the `internalType` field if it exists + if (kind.hints?.internalType) { + return kind.hints.internalType; + } + + // otherwise output minimally syntactically-valid syntax with a warning + return [ + "/* warning: the following type may be incomplete. ", + "the receiving contract may expect additional input or output parameters. */ ", + "function() external" + ].join(""); +} + +const printStructType = ( + kind: Kind.Struct, + options: { + currentInterfaceName?: string, + enableGlobalStructs?: boolean; + shimGlobalInterfaceName?: string; + } = {} +): string => { + const { + currentInterfaceName, + enableGlobalStructs = false, + shimGlobalInterfaceName + } = options; + + if (!enableGlobalStructs && !shimGlobalInterfaceName) { + throw new Error( + "Option `shimGlobalInterfaceName` is required without global structs" + ) + } + + if ( + kind.identifier.container && + kind.identifier.container.name !== currentInterfaceName + ) { + return `${kind.identifier.container.name}.${kind.identifier.name}`; + } + + if ( + !kind.identifier.container && + currentInterfaceName && + !enableGlobalStructs + ) { + return `${shimGlobalInterfaceName}.${kind.identifier.name}`; + } + + return kind.identifier.name; +} + +const printArrayType = ( + kind: Kind.Array, + options: { + currentInterfaceName?: string; + enableUserDefinedValueTypes?: boolean; + enableGlobalStructs?: boolean; + shimGlobalInterfaceName?: string; + } = {} +): string => { + if (Kind.Array.isDynamic(kind)) { + return `${printType(kind.itemKind, options)}[]`; + } + + // static case + return `${printType(kind.itemKind, options)}[${kind.length}]`; +} + diff --git a/packages/abi-to-sol/src/type.test.ts b/packages/abi-to-sol/src/type.test.ts new file mode 100644 index 0000000..a4d3aa3 --- /dev/null +++ b/packages/abi-to-sol/src/type.test.ts @@ -0,0 +1,15 @@ +import { isType } from "./type"; + +describe("isType", () => { + it("recognizes dynamic arrays", () => { + expect(isType("uint256[]")).toEqual(true); + }); + + it("recognizes static arrays", () => { + expect(isType("uint256[1]")).toEqual(true); + }); + + it("recognizes arrays of arrays", () => { + expect(isType("ufixed256x18[][]")).toEqual(true); + }); +}); diff --git a/packages/abi-to-sol/src/type.ts b/packages/abi-to-sol/src/type.ts new file mode 100644 index 0000000..f628fcc --- /dev/null +++ b/packages/abi-to-sol/src/type.ts @@ -0,0 +1,305 @@ +export type Octal = + | "8" + | "16" + | "24" + | "32" + | "48" + | "56" + | "64" + | "72" + | "80" + | "88" + | "96" + | "104" + | "112" + | "120" + | "128" + | "136" + | "144" + | "152" + | "160" + | "168" + | "176" + | "184" + | "192" + | "200" + | "208" + | "216" + | "224" + | "232" + | "240" + | "248" + | "256"; + +export const isOctal = (expression: string): expression is Octal => { + const integer = parseInt(expression, 10); + if (expression !== `${integer}`) { + return false; + } + + return integer % 8 === 0 && integer >= 8 && integer <= 256; +} + +export type Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"; + +// gotta do some janky stuff to represent the natural / whole numbers +// even still, `Whole` will disagree with `isWhole` at times (shhhh it's ok) +export type Digits = `${bigint}` & `${Digit}${string}`; +export type Natural = + & Digits + & `${Exclude}${string}`; +export type Whole = Natural | "0"; +export const isWhole = ( + expression: string +): expression is Whole => { + const integer = parseInt(expression, 10); + if (expression !== `${integer}`) { + return false; + } + + return integer >= 0; +}; + +export type NaturalLessThanEqualToEighty = + | Exclude // 1-9 + | `${Exclude}${Digit}` // 10-79 + | "80"; + +export const isNaturalLessThanEqualToEighty = ( + expression: string +): expression is NaturalLessThanEqualToEighty => { + const integer = parseInt(expression, 10); + if (expression !== `${integer}`) { + return false; + } + + return integer > 0 && integer <= 80; +} + +export type NaturalLessThanEqualToThirtyTwo = + | Exclude // 1-9 + | `${"1" | "2"}${Digit}` // 10-29 + | "30" + | "31" + | "32"; + +export const isNaturalLessThanEqualToThirtyTwo = ( + expression: string +): expression is NaturalLessThanEqualToThirtyTwo => { + const integer = parseInt(expression, 10); + if (expression !== `${integer}`) { + return false; + } + + return integer > 0 && integer <= 32; +} + +export namespace Type { + export namespace Elementary { + export type Uint = "uint" | `uint${Octal}`; + export const isUint = ( + expression: string + ): expression is Uint => + expression === "uint" || ( + expression.startsWith("uint") && + isOctal(expression.slice(4)) + ); + + export type Int = "int" | `int${Octal}`; + export const isInt = ( + expression: string + ): expression is Int => + expression === "int" || ( + expression.startsWith("int") && + isOctal(expression.slice(3)) + ); + + export type Address = "address"; + export const isAddress = ( + expression: string + ): expression is Address => + expression === "address"; + + export type Bool = "bool"; + export const isBool = ( + expression: string + ): expression is Bool => + expression === "bool"; + + export type Ufixed = "ufixed" | `ufixed${Octal}x${NaturalLessThanEqualToEighty}`; + export const isUfixed = ( + expression: string + ): expression is Ufixed => { + if (expression === "ufixed") { + return true; + } + + const match = expression.match(/^ufixed([^x]+)x([^x]+)$/); + if (!match) { + return false; + } + + const [_, m, n] = match; + return isOctal(m) && isNaturalLessThanEqualToEighty(n); + } + + export type Fixed = "fixed" | `fixed${Octal}x${NaturalLessThanEqualToEighty}`; + export const isFixed = ( + expression: string + ): expression is Fixed => { + if (expression === "fixed") { + return true; + } + + const match = expression.match(/^fixed([^x]+)x([^x]+)$/); + if (!match) { + return false; + } + + const [_, m, n] = match; + return isOctal(m) && isNaturalLessThanEqualToEighty(n); + } + + export type StaticBytes = `bytes${NaturalLessThanEqualToThirtyTwo}`; + export const isStaticBytes = ( + expression: string + ): expression is StaticBytes => + expression.startsWith("bytes") && + isNaturalLessThanEqualToThirtyTwo(expression.slice(5)); + + export type Bytes = "bytes"; + export const isBytes = ( + expression: string + ): expression is Bytes => + expression === "bytes"; + + export type String = "string"; + export const isString = ( + expression: string + ): expression is String => + expression === "string"; + + export type Function = "function"; + export const isFunction = ( + expression: string + ): expression is Function => + expression === "function"; + } + + export type Elementary = + | Elementary.Uint + | Elementary.Int + | Elementary.Address + | Elementary.Bool + | Elementary.Ufixed + | Elementary.Fixed + | Elementary.StaticBytes + | Elementary.Bytes + | Elementary.String + | Elementary.Function; + + export const isElementary = ( + expression: string + ): expression is Elementary => + Elementary.isUint(expression) || + Elementary.isInt(expression) || + Elementary.isAddress(expression) || + Elementary.isBool(expression) || + Elementary.isUfixed(expression) || + Elementary.isFixed(expression) || + Elementary.isStaticBytes(expression) || + Elementary.isBytes(expression) || + Elementary.isString(expression) || + Elementary.isFunction(expression); + + export namespace Array { + export type Static = `${string}[${Whole}]`; + export const isStatic = ( + expression: string + ): expression is Static => { + const match = expression.match(/^(.+)\[([0-9]+)\]$/); + if (!match) { + return false; + } + + const [_, underlying, length] = match; + return isType(underlying) && isWhole(length); + }; + + export const length = ( + type: Static + ): number => { + const match = type.match(/\[([0-9]+)\]$/); + if (!match) { + throw new Error( + `Unexpected mismatch, type \`${type}\` is not a valid static array` + ); + } + + const [_, length] = match; + return parseInt(length, 10); + } + + export type Dynamic = `${string}[]`; + export const isDynamic = ( + expression: string + ): expression is Dynamic => { + const match = expression.match(/^(.+)\[\]$/); + if (!match) { + return false; + } + + const [_, underlying] = match; + return isType(underlying); + } + + export const underlying = ( + type: Array + ): Type => { + const match = type.match(/^(.+)\[[^\]]*\]$/); + if (!match) { + throw new Error( + `Unexpected mismatch, \`${type}\` is not a valid array type` + ); + } + + const [_, underlying] = match; + if (!isType(underlying)) { + throw new Error( + `Underlying type \`${underlying}\` is not a valid type` + ); + + } + + return underlying; + } + } + + export type Array = + | Array.Static + | Array.Dynamic; + + export const isArray = ( + expression: string + ): expression is Array => + Array.isStatic(expression) || + Array.isDynamic(expression); + + export type Tuple = "tuple"; + export const isTuple = ( + expression: string + ): expression is Tuple => + expression === "tuple"; +} + +export type Type = + | Type.Elementary + | Type.Array + | Type.Tuple; + +export const isType = ( + expression: string +): expression is Type => + Type.isElementary(expression) || + Type.isArray(expression) || + Type.isTuple(expression); diff --git a/packages/abi-to-sol/src/version-features.ts b/packages/abi-to-sol/src/version-features.ts deleted file mode 100644 index 40471d2..0000000 --- a/packages/abi-to-sol/src/version-features.ts +++ /dev/null @@ -1,82 +0,0 @@ -import * as semver from "semver"; - -export const mixed: unique symbol = Symbol(); - -export const allFeatures = { - "receive-keyword": { - ">=0.6.0": true, - "<0.6.0": false - }, - "fallback-keyword": { - ">=0.6.0": true, - "<0.6.0": false - }, - "array-parameter-location": { - ">=0.7.0": "memory", - "^0.5.0 || ^0.6.0": "calldata", - "<0.5.0": undefined - }, - "abiencoder-v2": { - ">=0.8.0": "default", - "<0.8.0": "experimental", - }, - "global-structs": { - ">=0.6.0": true, - "<0.6.0": false - }, - "structs-in-interfaces": { - ">=0.5.0": true, - "<0.5.0": false - }, - "custom-errors": { - ">=0.8.4": true, - "<0.8.4": false - }, - "user-defined-value-types": { - ">=0.8.8": true, - "<0.8.8": false - } -} as const; - -export type AllFeatures = typeof allFeatures; - -export type Category = keyof AllFeatures; - -export type CategoryOptions = AllFeatures[C]; - -export type CategoryOptionRange = string & { - [K in C]: keyof CategoryOptions -}[C]; - -export type CategoryOption = { - [K in C]: CategoryOptions[CategoryOptionRange] -}[C]; - -export type VersionFeatures = { - [C in Category]: VersionFeature; -}; - -export type VersionFeature = - CategoryOption | typeof mixed; - -export const forRange = (range: string | semver.Range): VersionFeatures => { - const forCategory = ( - category: C - ): VersionFeature => { - const options = allFeatures[category]; - const matchingRanges: CategoryOptionRange[] = - (Object.keys(options) as CategoryOptionRange[]) - .filter(optionRange => semver.intersects(range, optionRange)); - - if (matchingRanges.length > 1) { - return mixed; - } - - const [matchingRange] = matchingRanges; - return options[matchingRange]; - } - - return (Object.keys(allFeatures) as Category[]) - .map((category: C) => ({ [category]: forCategory(category) })) - .reduce((a, b) => ({ ...a, ...b }), {}) as VersionFeatures; -} diff --git a/packages/abi-to-sol/src/visitor.ts b/packages/abi-to-sol/src/visitor.ts index a3bc07c..aff12b5 100644 --- a/packages/abi-to-sol/src/visitor.ts +++ b/packages/abi-to-sol/src/visitor.ts @@ -18,13 +18,14 @@ export interface Visitor { } export interface DispatchOptions { - node: Node | SchemaAbi; + node: Node; visitor: Visitor; context?: C; } export type Node = | Abi.Abi + | SchemaAbi | Abi.Entry | Abi.FunctionEntry | Abi.ConstructorEntry @@ -70,6 +71,7 @@ const isAbi = (node: Node | SchemaAbi): node is Abi.Abi | SchemaAbi => const isEntry = (node: Node): node is Abi.Entry => typeof node === "object" && "type" in node && + typeof node.type === "string" && ["function", "constructor", "fallback", "receive", "event", "error"].includes( node.type ) && diff --git a/packages/abi-to-sol/test/custom-example.ts b/packages/abi-to-sol/test/custom-example.ts index df70025..14a2fcc 100644 --- a/packages/abi-to-sol/test/custom-example.ts +++ b/packages/abi-to-sol/test/custom-example.ts @@ -99,23 +99,3 @@ export const expectedSignatures: {[name: string]: string} = { Foo: "((uint256,uint256)[],uint256)", Bar: "(uint256,uint256)", }; - -export const expectedDeclarations = { - Foo: { - bars: { - type: "tuple[]", - signature: expectedSignatures.Bar, - }, - c: { - type: "uint256", - }, - }, - Bar: { - a: { - type: "uint256", - }, - b: { - type: "uint256", - }, - }, -}; diff --git a/packages/abi-to-sol/tsconfig.json b/packages/abi-to-sol/tsconfig.json index d29e6e4..90aaa4e 100644 --- a/packages/abi-to-sol/tsconfig.json +++ b/packages/abi-to-sol/tsconfig.json @@ -6,7 +6,7 @@ // "incremental": true, /* Enable incremental compilation */ "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ - "lib": ["es2017"], /* Specify library files to be included in the compilation. */ + "lib": ["es2019"], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ @@ -28,7 +28,7 @@ "strict": true, /* Enable all strict type-checking options. */ "resolveJsonModule": true, // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ + "strictNullChecks": true, /* Enable strict null checks. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ diff --git a/packages/web-ui/package.json b/packages/web-ui/package.json index 43a457e..0c86f04 100644 --- a/packages/web-ui/package.json +++ b/packages/web-ui/package.json @@ -34,7 +34,7 @@ "@types/react-dom": "^17.0.0", "@typescript-eslint/eslint-plugin": "^4.5.0", "@typescript-eslint/parser": "^4.5.0", - "abi-to-sol": "^0.7.1", + "abi-to-sol": "^0.8.0-0", "add": "^2.0.6", "babel-eslint": "^10.1.0", "babel-jest": "^26.6.0", @@ -94,7 +94,7 @@ "style-loader": "1.3.0", "terser-webpack-plugin": "4.2.3", "ts-pnp": "1.2.0", - "typescript": "4.5.2", + "typescript": "^4.9.5", "unstated-next": "^1.1.0", "url-loader": "4.1.1", "web-vitals": "^1.0.1", diff --git a/packages/web-ui/src/abi/Examples.ts b/packages/web-ui/src/abi/Examples.ts index 00bf66b..e4487b2 100644 --- a/packages/web-ui/src/abi/Examples.ts +++ b/packages/web-ui/src/abi/Examples.ts @@ -8,6 +8,7 @@ import DepositContract from "./examples/DepositContract.abi.json"; import UniswapV2RouterO2 from "./examples/UniswapV2Router02.abi.json"; import AirSwap from "./examples/AirSwap.abi.json"; import BunchaStructs from "./examples/BunchaStructs.abi.json"; +import UDVTs from "./examples/UDVTs.abi.json"; export interface Example { name: string; @@ -40,6 +41,11 @@ export const examples: { [exampleName: string]: Example } = { name: "BunchaStructs", license: "", contents: JSON.stringify(BunchaStructs, undefined, 2) + }, + udvts: { + name: "UDVTs", + license: "", + contents: JSON.stringify(UDVTs, undefined, 2) } }; diff --git a/packages/web-ui/src/abi/examples/UDVTs.abi.json b/packages/web-ui/src/abi/examples/UDVTs.abi.json new file mode 100644 index 0000000..1460fbb --- /dev/null +++ b/packages/web-ui/src/abi/examples/UDVTs.abi.json @@ -0,0 +1,25 @@ +[ + { + "inputs": [ + { + "internalType": "Int", + "name": "i", + "type": "int256" + }, + { + "internalType": "UDVTs.Uint", + "name": "u", + "type": "uint256" + }, + { + "internalType": "Other.Bool", + "name": "b", + "type": "bool" + } + ], + "name": "fnoo", + "outputs": [], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/packages/web-ui/src/solidity/OptionsControls.tsx b/packages/web-ui/src/solidity/OptionsControls.tsx index 457eb6e..2788926 100644 --- a/packages/web-ui/src/solidity/OptionsControls.tsx +++ b/packages/web-ui/src/solidity/OptionsControls.tsx @@ -62,6 +62,7 @@ export const OptionsControls = () => { setSolidityVersion(event.target.value) }} > + diff --git a/yarn.lock b/yarn.lock index a8f251a..1151533 100644 --- a/yarn.lock +++ b/yarn.lock @@ -287,6 +287,11 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/parser@^7.0.0": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.5.tgz#821bb520118fd25b982eaf8d37421cf5c64a312b" + integrity sha512-J+IxH2IsxV4HbnTrSWgMAQj0UEo61hDA4Ny8h8PCX0MLXiibqHbqIOVneqdocemSBc22VpBKxt4J6FQzy9HarQ== + "@babel/parser@^7.1.0", "@babel/parser@^7.12.3", "@babel/parser@^7.14.5", "@babel/parser@^7.14.6", "@babel/parser@^7.14.7", "@babel/parser@^7.7.0": version "7.14.7" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.7.tgz#6099720c8839ca865a2637e6c85852ead0bdb595" @@ -3300,9 +3305,9 @@ "@babel/types" "^7.3.0" "@types/bn.js@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" - integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" + integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== dependencies: "@types/node" "*" @@ -3402,6 +3407,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + "@types/lodash.mergewith@4.6.6": version "4.6.6" resolved "https://registry.yarnpkg.com/@types/lodash.mergewith/-/lodash.mergewith-4.6.6.tgz#c4698f5b214a433ff35cb2c75ee6ec7f99d79f10" @@ -3649,6 +3659,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.4.tgz#41acbd79b5816b7c0dd7530a43d97d020d3aeb42" integrity sha512-3eap4QWxGqkYuEmVebUGULMskR6Cuoc/Wii0oSOddleP4EGx1tjLnZQ0ZP33YRoMDCs5O3j56RBV4g14T4jvww== +"@typescript-eslint/types@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" + integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== + "@typescript-eslint/typescript-estree@3.10.1": version "3.10.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz#fd0061cc38add4fad45136d654408569f365b853" @@ -3676,6 +3691,19 @@ semver "^7.3.5" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@^4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" + integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== + dependencies: + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" + "@typescript-eslint/visitor-keys@3.10.1": version "3.10.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz#cd4274773e3eb63b2e870ac602274487ecd1e931" @@ -3691,6 +3719,14 @@ "@typescript-eslint/types" "4.28.4" eslint-visitor-keys "^2.0.0" +"@typescript-eslint/visitor-keys@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" + integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== + dependencies: + "@typescript-eslint/types" "4.33.0" + eslint-visitor-keys "^2.0.0" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -4079,6 +4115,11 @@ anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +app-module-path@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" + integrity sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ== + aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -4266,6 +4307,16 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +ast-module-types@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-2.7.1.tgz#3f7989ef8dfa1fdb82dfe0ab02bdfc7c77a57dd3" + integrity sha512-Rnnx/4Dus6fn7fTqdeLEAn5vUll5w7/vts0RN608yFa6si/rDOUonlIIiwugHBFWjylHjxm9owoSZn71KwG4gw== + +ast-module-types@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-3.0.0.tgz#9a6d8a80f438b6b8fe4995699d700297f398bf81" + integrity sha512-CMxMCOCS+4D+DkOQfuZf+vLrSEmY/7xtORwdxs4wtcC1wVgvk2MqFFTwQCFhvWsI4KPU9lcWXPI8DgRiz+xetQ== + ast-types-flow@^0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" @@ -4541,7 +4592,7 @@ base-x@^3.0.2: dependencies: safe-buffer "^5.0.1" -base64-js@^1.0.2: +base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -4621,6 +4672,15 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + blakejs@^1.1.0: version "1.2.1" resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" @@ -4868,6 +4928,14 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + builtin-modules@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" @@ -5263,6 +5331,11 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" +cli-spinners@^2.5.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.8.0.tgz#e97a3e2bd00e6d85aa0c13d7f9e3ce236f7787fc" + integrity sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ== + cli-truncate@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" @@ -5380,7 +5453,7 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -color-name@^1.0.0, color-name@~1.1.4: +color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== @@ -5426,12 +5499,7 @@ command-exists@^1.2.8: resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== -commander@3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" - integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== - -commander@^2.20.0: +commander@^2.16.0, commander@^2.20.0, commander@^2.20.3, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -5451,6 +5519,11 @@ commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@^8.1.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + common-tags@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" @@ -6193,6 +6266,13 @@ debug@^3.1.1, debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.0.0, debug@^4.3.3: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -6217,9 +6297,9 @@ decimal.js@^10.2.1: integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== decode-uri-component@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" - integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= dedent@^0.7.0: version "0.7.0" @@ -6238,6 +6318,11 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -6325,6 +6410,17 @@ depd@^1.1.2, depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +dependency-tree@^8.1.1: + version "8.1.2" + resolved "https://registry.yarnpkg.com/dependency-tree/-/dependency-tree-8.1.2.tgz#c9e652984f53bd0239bc8a3e50cbd52f05b2e770" + integrity sha512-c4CL1IKxkKng0oT5xrg4uNiiMVFqTGOXqHSFx7XEFdgSsp6nw3AGGruICppzJUrfad/r7GLqt26rmWU4h4j39A== + dependencies: + commander "^2.20.3" + debug "^4.3.1" + filing-cabinet "^3.0.1" + precinct "^8.0.0" + typescript "^3.9.7" + deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" @@ -6376,6 +6472,90 @@ detect-port-alt@1.1.6: address "^1.0.1" debug "^2.6.0" +detective-amd@^3.1.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/detective-amd/-/detective-amd-3.1.2.tgz#bf55eb5291c218b76d6224a3d07932ef13a9a357" + integrity sha512-jffU26dyqJ37JHR/o44La6CxtrDf3Rt9tvd2IbImJYxWKTMdBjctp37qoZ6ZcY80RHg+kzWz4bXn39e4P7cctQ== + dependencies: + ast-module-types "^3.0.0" + escodegen "^2.0.0" + get-amd-module-type "^3.0.0" + node-source-walk "^4.2.0" + +detective-cjs@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/detective-cjs/-/detective-cjs-3.1.3.tgz#50e107d67b37f459b0ec02966ceb7e20a73f268b" + integrity sha512-ljs7P0Yj9MK64B7G0eNl0ThWSYjhAaSYy+fQcpzaKalYl/UoQBOzOeLCSFEY1qEBhziZ3w7l46KG/nH+s+L7BQ== + dependencies: + ast-module-types "^3.0.0" + node-source-walk "^4.0.0" + +detective-es6@^2.2.0, detective-es6@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/detective-es6/-/detective-es6-2.2.2.tgz#ee5f880981d9fecae9a694007029a2f6f26d8d28" + integrity sha512-eZUKCUsbHm8xoeoCM0z6JFwvDfJ5Ww5HANo+jPR7AzkFpW9Mun3t/TqIF2jjeWa2TFbAiGaWESykf2OQp3oeMw== + dependencies: + node-source-walk "^4.0.0" + +detective-less@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/detective-less/-/detective-less-1.0.2.tgz#a68af9ca5f69d74b7d0aa190218b211d83b4f7e3" + integrity sha512-Rps1xDkEEBSq3kLdsdnHZL1x2S4NGDcbrjmd4q+PykK5aJwDdP5MBgrJw1Xo+kyUHuv3JEzPqxr+Dj9ryeDRTA== + dependencies: + debug "^4.0.0" + gonzales-pe "^4.2.3" + node-source-walk "^4.0.0" + +detective-postcss@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detective-postcss/-/detective-postcss-4.0.0.tgz#24e69b465e5fefe7a6afd05f7e894e34595dbf51" + integrity sha512-Fwc/g9VcrowODIAeKRWZfVA/EufxYL7XfuqJQFroBKGikKX83d2G7NFw6kDlSYGG3LNQIyVa+eWv1mqre+v4+A== + dependencies: + debug "^4.1.1" + is-url "^1.2.4" + postcss "^8.1.7" + postcss-values-parser "^2.0.1" + +detective-postcss@^5.0.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/detective-postcss/-/detective-postcss-5.1.3.tgz#773314cd017621b7d382be81331eb0c7abbe8cc3" + integrity sha512-Wo7PUpF6wqeT1aRgajdyIdDRjFFJVxlXPRAlT1aankH/RVOgrJuEZFZ4ABxYXdzaRPO5Lkg8rHxsxpLnxdJIYA== + dependencies: + is-url "^1.2.4" + postcss "^8.4.6" + postcss-values-parser "^5.0.0" + +detective-sass@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/detective-sass/-/detective-sass-3.0.2.tgz#e0f35aac79a4d2f6409c284d95b8f7ecd5973afd" + integrity sha512-DNVYbaSlmti/eztFGSfBw4nZvwsTaVXEQ4NsT/uFckxhJrNRFUh24d76KzoCC3aarvpZP9m8sC2L1XbLej4F7g== + dependencies: + gonzales-pe "^4.3.0" + node-source-walk "^4.0.0" + +detective-scss@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/detective-scss/-/detective-scss-2.0.2.tgz#7d2a642616d44bf677963484fa8754d9558b8235" + integrity sha512-hDWnWh/l0tht/7JQltumpVea/inmkBaanJUcXRB9kEEXVwVUMuZd6z7eusQ6GcBFrfifu3pX/XPyD7StjbAiBg== + dependencies: + gonzales-pe "^4.3.0" + node-source-walk "^4.0.0" + +detective-stylus@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detective-stylus/-/detective-stylus-1.0.3.tgz#20a702936c9fd7d4203fd7a903314b5dd43ac713" + integrity sha512-4/bfIU5kqjwugymoxLXXLltzQNeQfxGoLm2eIaqtnkWxqbhap9puDVpJPVDx96hnptdERzS5Cy6p9N8/08A69Q== + +detective-typescript@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/detective-typescript/-/detective-typescript-7.0.2.tgz#c6e00b4c28764741ef719662250e6b014a5f3c8e" + integrity sha512-unqovnhxzvkCz3m1/W4QW4qGsvXCU06aU2BAm8tkza+xLnp9SOFnob2QsTxUv5PdnQKfDvWcv9YeOeFckWejwA== + dependencies: + "@typescript-eslint/typescript-estree" "^4.33.0" + ast-module-types "^2.7.1" + node-source-walk "^4.2.0" + typescript "^3.9.10" + dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -6677,6 +6857,14 @@ enhanced-resolve@^4.3.0: memory-fs "^0.5.0" tapable "^1.0.0" +enhanced-resolve@^5.8.3: + version "5.13.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz#26d1ecc448c02de997133217b5c1053f34a0a275" + integrity sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + enquirer@^2.3.5, enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -7079,7 +7267,7 @@ esutils@^2.0.2: etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= ethereum-bloom-filters@^1.0.6: version "1.0.10" @@ -7340,7 +7528,7 @@ faker@^5.1.0: resolved "https://registry.yarnpkg.com/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e" integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g== -fast-check@3.1.1, fast-check@^3.1.1: +fast-check@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-3.1.1.tgz#72c5ae7022a4e86504762e773adfb8a5b0b01252" integrity sha512-3vtXinVyuUKCKFKYcwXhGE6NtGWkqF8Yh3rvMZNzmwz8EPrgoc/v4pDdLHyLnCyCI5MZpZZkDEwFyXyEONOxpA== @@ -7441,6 +7629,25 @@ filesize@6.1.0: resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00" integrity sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg== +filing-cabinet@^3.0.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/filing-cabinet/-/filing-cabinet-3.3.1.tgz#45d87bb273a0e0a7dd6ac6bac9111059186e2e9c" + integrity sha512-renEK4Hh6DUl9Vl22Y3cxBq1yh8oNvbAdXnhih0wVpmea+uyKjC9K4QeRjUaybIiIewdzfum+Fg15ZqJ/GyCaA== + dependencies: + app-module-path "^2.2.0" + commander "^2.20.3" + debug "^4.3.3" + enhanced-resolve "^5.8.3" + is-relative-path "^1.0.2" + module-definition "^3.3.1" + module-lookup-amd "^7.0.1" + resolve "^1.21.0" + resolve-dependency-path "^2.0.0" + sass-lookup "^3.0.0" + stylus-lookup "^3.0.1" + tsconfig-paths "^3.10.1" + typescript "^3.9.7" + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -7645,17 +7852,6 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" -fs-extra@^0.30.0: - version "0.30.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" - integrity sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A= - dependencies: - graceful-fs "^4.1.2" - jsonfile "^2.1.0" - klaw "^1.0.0" - path-is-absolute "^1.0.0" - rimraf "^2.2.8" - fs-extra@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -7755,6 +7951,14 @@ gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== +get-amd-module-type@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-amd-module-type/-/get-amd-module-type-3.0.2.tgz#46550cee2b8e1fa4c3f2c8a5753c36990aa49ab0" + integrity sha512-PcuKwB8ouJnKuAPn6Hk3UtdfKoUV3zXRqVEvj8XGIXqjWfgd1j7QGdXy5Z9OdQfzVt1Sk29HVe/P+X74ccOuqw== + dependencies: + ast-module-types "^3.0.0" + node-source-walk "^4.2.2" + get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -7978,7 +8182,14 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4: +gonzales-pe@^4.2.3, gonzales-pe@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3" + integrity sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ== + dependencies: + minimist "^1.2.5" + +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== @@ -7988,6 +8199,13 @@ grapheme-splitter@^1.0.4: resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +graphviz@0.0.9: + version "0.0.9" + resolved "https://registry.yarnpkg.com/graphviz/-/graphviz-0.0.9.tgz#0bbf1df588c6a92259282da35323622528c4bbc4" + integrity sha512-SmoY2pOtcikmMCqCSy2NO1YsRfu9OO0wpTlOYW++giGjfX1a6gax/m1Fo8IdUd0/3H15cTOfR1SMKwohj4LKsg== + dependencies: + temp "~0.4.0" + growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -8423,7 +8641,7 @@ identity-obj-proxy@3.0.0: dependencies: harmony-reflect "^1.4.6" -ieee754@^1.1.4: +ieee754@^1.1.13, ieee754@^1.1.4: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -8544,7 +8762,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.2, ini@^1.3.4, ini@^1.3.5: +ini@^1.3.2, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -8742,6 +8960,13 @@ is-core-module@^2.0.0, is-core-module@^2.2.0, is-core-module@^2.4.0: dependencies: has "^1.0.3" +is-core-module@^2.11.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" + integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -8847,6 +9072,11 @@ is-hex-prefixed@1.0.0: resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-lambda@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" @@ -8955,6 +9185,11 @@ is-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= +is-relative-path@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-relative-path/-/is-relative-path-1.0.2.tgz#091b46a0d67c1ed0fe85f1f8cfdde006bb251d46" + integrity sha512-i1h+y50g+0hRbBD+dbnInl3JlJ702aar58snAeX+MxBAPvzXGej7sYoPMhlnykabt0ZzCJNBEyzMlekuQZN7fA== + is-resolvable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" @@ -9018,6 +9253,16 @@ is-upper-case@^1.1.0: dependencies: upper-case "^1.1.0" +is-url-superb@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-url-superb/-/is-url-superb-4.0.0.tgz#b54d1d2499bb16792748ac967aa3ecb41a33a8c2" + integrity sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA== + +is-url@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -9712,20 +9957,13 @@ json5@2.x, json5@^2.1.2, json5@^2.2.0: dependencies: minimist "^1.2.5" -json5@^1.0.1: +json5@^1.0.1, json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" -jsonfile@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" - integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= - optionalDependencies: - graceful-fs "^4.1.6" - jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -9771,9 +10009,9 @@ jsprim@^1.2.2: object.assign "^4.1.2" keccak@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" - integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== + version "3.0.3" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.3.tgz#4bc35ad917be1ef54ff246f904c2bbbf9ac61276" + integrity sha512-JZrLIAJWuZxKbCilMpNz5Vj7Vtb4scDG3dMXLOsbzBmQGyjwE61BbW7bJkfKKCShXiQZt3T6sBgALRtmd+nZaQ== dependencies: node-addon-api "^2.0.0" node-gyp-build "^4.2.0" @@ -9808,13 +10046,6 @@ kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -klaw@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" - integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= - optionalDependencies: - graceful-fs "^4.1.9" - kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -10175,6 +10406,34 @@ lz-string@^1.4.4: resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= +madge@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/madge/-/madge-5.0.2.tgz#d34527af7e96de9625e8069902667c4c5a073ada" + integrity sha512-OeqFIgugINbVqh6keLWePD/N3u1EEYS3O9gCTD+EjcuaJa1TH30jcCxr8CEl3+neS1VM8sDCQSYoln/2li3ceg== + dependencies: + chalk "^4.1.1" + commander "^7.2.0" + commondir "^1.0.1" + debug "^4.3.1" + dependency-tree "^8.1.1" + detective-amd "^3.1.0" + detective-cjs "^3.1.1" + detective-es6 "^2.2.0" + detective-less "^1.0.2" + detective-postcss "^5.0.0" + detective-sass "^3.0.1" + detective-scss "^2.0.1" + detective-stylus "^1.0.0" + detective-typescript "^7.0.0" + graphviz "0.0.9" + ora "^5.4.1" + pluralize "^8.0.0" + precinct "^8.1.0" + pretty-ms "^7.0.1" + rc "^1.2.7" + typescript "^3.9.5" + walkdir "^0.4.1" + magic-string@^0.25.0, magic-string@^0.25.7: version "0.25.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" @@ -10521,6 +10780,11 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + minipass-collect@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" @@ -10648,6 +10912,25 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== +module-definition@^3.3.1: + version "3.4.0" + resolved "https://registry.yarnpkg.com/module-definition/-/module-definition-3.4.0.tgz#953a3861f65df5e43e80487df98bb35b70614c2b" + integrity sha512-XxJ88R1v458pifaSkPNLUTdSPNVGMP2SXVncVmApGO+gAfrLANiYe6JofymCzVceGOMwQE2xogxBSc8uB7XegA== + dependencies: + ast-module-types "^3.0.0" + node-source-walk "^4.0.0" + +module-lookup-amd@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/module-lookup-amd/-/module-lookup-amd-7.0.1.tgz#d67c1a93f2ff8e38b8774b99a638e9a4395774b2" + integrity sha512-w9mCNlj0S8qviuHzpakaLVc+/7q50jl9a/kmJ/n8bmXQZgDPkQHnPBb8MUOYh3WpAYkXuNc2c+khsozhIp/amQ== + dependencies: + commander "^2.8.1" + debug "^4.1.0" + glob "^7.1.6" + requirejs "^2.3.5" + requirejs-config-file "^4.0.0" + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -10728,6 +11011,11 @@ nanoid@^3.1.23: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== +nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -10822,9 +11110,9 @@ node-forge@^0.10.0: integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== node-gyp-build@^4.2.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" - integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== + version "4.6.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" + integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== node-gyp@^5.0.2: version "5.1.1" @@ -10915,6 +11203,13 @@ node-releases@^1.1.61, node-releases@^1.1.71: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== +node-source-walk@^4.0.0, node-source-walk@^4.2.0, node-source-walk@^4.2.2: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-4.3.0.tgz#8336b56cfed23ac5180fe98f1e3bb6b11fd5317c" + integrity sha512-8Q1hXew6ETzqKRAs3jjLioSxNfT1cx74ooiF8RlAONwVMcfq+UdzLC2eB5qcPldUxaE5w3ytLkrmV1TGddhZTA== + dependencies: + "@babel/parser" "^7.0.0" + nopt@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" @@ -11329,6 +11624,21 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + original@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" @@ -11567,6 +11877,11 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-ms@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" + integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== + parse-path@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.4.tgz#4bf424e6b743fb080831f03b536af9fc43f0ffea" @@ -11673,7 +11988,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -11711,6 +12026,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" @@ -11797,6 +12117,11 @@ please-upgrade-node@^3.2.0: dependencies: semver-compare "^1.0.0" +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + pnp-webpack-plugin@1.6.4: version "1.6.4" resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" @@ -12468,6 +12793,15 @@ postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: indexes-of "^1.0.1" uniq "^1.0.1" +postcss-values-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-5.0.0.tgz#10c61ac3f488e4de25746b829ea8d8894e9ac3d2" + integrity sha512-2viDDjMMrt21W2izbeiJxl3kFuD/+asgB0CBwPEgSyhCmBnDIa/y+pLaoyX+q3I3DHH0oPPL3cgjVTQvlS1Maw== + dependencies: + color-name "^1.1.4" + is-url-superb "^4.0.0" + quote-unquote "^1.0.0" + postcss@7.0.36, postcss@^7, postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.26, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: version "7.0.36" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.36.tgz#056f8cffa939662a8f5905950c07d5285644dfcb" @@ -12486,6 +12820,43 @@ postcss@^8.1.0: nanoid "^3.1.23" source-map-js "^0.6.2" +postcss@^8.1.7: + version "8.4.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab" + integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +postcss@^8.4.6: + version "8.4.24" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.24.tgz#f714dba9b2284be3cc07dbd2fc57ee4dc972d2df" + integrity sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +precinct@^8.0.0, precinct@^8.1.0: + version "8.3.1" + resolved "https://registry.yarnpkg.com/precinct/-/precinct-8.3.1.tgz#94b99b623df144eed1ce40e0801c86078466f0dc" + integrity sha512-pVppfMWLp2wF68rwHqBIpPBYY8Kd12lDhk8LVQzOwqllifVR15qNFyod43YLyFpurKRZQKnE7E4pofAagDOm2Q== + dependencies: + commander "^2.20.3" + debug "^4.3.3" + detective-amd "^3.1.0" + detective-cjs "^3.1.1" + detective-es6 "^2.2.1" + detective-less "^1.0.2" + detective-postcss "^4.0.0" + detective-sass "^3.0.1" + detective-scss "^2.0.1" + detective-stylus "^1.0.0" + detective-typescript "^7.0.0" + module-definition "^3.3.1" + node-source-walk "^4.2.0" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -12556,6 +12927,13 @@ pretty-hrtime@^1.0.3: resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= +pretty-ms@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" + integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== + dependencies: + parse-ms "^2.1.0" + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -12709,9 +13087,9 @@ punycode@^2.1.0, punycode@^2.1.1: integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== pure-rand@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-5.0.1.tgz#97a287b4b4960b2a3448c0932bf28f2405cac51d" - integrity sha512-ksWccjmXOHU2gJBnH0cK1lSYdvSZ0zLoCMSz/nTGh6hDvCSgcRxDyIcOBD6KNxFz3xhMPm/T267Tbe2JRymKEQ== + version "5.0.5" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-5.0.5.tgz#bda2a7f6a1fc0f284d78d78ca5902f26f2ad35cf" + integrity sha512-BwQpbqxSCBJVpamI6ydzcKqyFmnd5msMWUGvzXLm1aXvusbbgkbOto/EUPM00hjveJEaJtdbhUjKSzWRhQVkaw== q@^1.1.2, q@^1.5.1: version "1.5.1" @@ -12778,6 +13156,11 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== +quote-unquote@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/quote-unquote/-/quote-unquote-1.0.0.tgz#67a9a77148effeaf81a4d428404a710baaac8a0b" + integrity sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg== + raf@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" @@ -12815,6 +13198,16 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-app-polyfill@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-2.0.0.tgz#a0bea50f078b8a082970a9d853dc34b6dcc6a3cf" @@ -13088,6 +13481,15 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stre string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readdir-scoped-modules@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" @@ -13270,7 +13672,7 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-from-string@^2.0.0, require-from-string@^2.0.2: +require-from-string@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== @@ -13280,6 +13682,19 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +requirejs-config-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz#4244da5dd1f59874038cc1091d078d620abb6ebc" + integrity sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw== + dependencies: + esprima "^4.0.0" + stringify-object "^3.2.1" + +requirejs@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.6.tgz#e5093d9601c2829251258c0b9445d4d19fa9e7c9" + integrity sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg== + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -13304,6 +13719,11 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" +resolve-dependency-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-dependency-path/-/resolve-dependency-path-2.0.0.tgz#11700e340717b865d216c66cabeb4a2a3c696736" + integrity sha512-DIgu+0Dv+6v2XwRaNWnumKu7GPufBBOr5I1gRPJHkvghrfCGOooJODFvgFimX/KRxk9j0whD2MnKHzM1jYvk9w== + resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" @@ -13356,6 +13776,15 @@ resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.1 is-core-module "^2.2.0" path-parse "^1.0.6" +resolve@^1.21.0: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.3: version "2.0.0-next.3" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" @@ -13418,7 +13847,7 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= -rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3: +rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -13573,6 +14002,13 @@ sass-loader@^10.0.5: schema-utils "^3.0.0" semver "^7.3.2" +sass-lookup@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/sass-lookup/-/sass-lookup-3.0.0.tgz#3b395fa40569738ce857bc258e04df2617c48cac" + integrity sha512-TTsus8CfFRn1N44bvdEai1no6PqdmDiQUiqW5DlpmtT+tYnIt1tXtDIph5KA1efC+LmioJXSnCtUVpcK9gaKIg== + dependencies: + commander "^2.16.0" + sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -13684,9 +14120,9 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" + integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== dependencies: lru-cache "^6.0.0" @@ -13998,18 +14434,16 @@ socks@^2.3.3: ip "^1.1.5" smart-buffer "^4.1.0" -solc@^0.8.6: - version "0.8.6" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.6.tgz#e4341fa6780137df97b94a0cfbd59b3f2037d0e0" - integrity sha512-miiDaWdaUnD7A6Cktb/2ug9f+ajcOCDYRr7vgbPEsMoutSlBtp5rca57oMg8iHSuM7jilwdxePujWI/+rbNftQ== +solc@^0.8.20: + version "0.8.20" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.20.tgz#b49151cf5ecc8de088d3d32b0afb607b3522ba8d" + integrity sha512-fPRnGspIEqmhu63RFO3pc79sLA7ZmzO0Uy0L5l6hEt2wAsq0o7UV6pXkAp3Mfv9IBhg7Px/oTu3a+y4gs3BWrQ== dependencies: command-exists "^1.2.8" - commander "3.0.2" + commander "^8.1.0" follow-redirects "^1.12.1" - fs-extra "^0.30.0" js-sha3 "0.8.0" memorystream "^0.3.1" - require-from-string "^2.0.0" semver "^5.5.0" tmp "0.0.33" @@ -14049,6 +14483,11 @@ source-map-js@^0.6.2: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -14422,7 +14861,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-object@^3.3.0: +stringify-object@^3.2.1, stringify-object@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== @@ -14513,6 +14952,11 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + strong-log-transformer@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -14552,6 +14996,14 @@ stylis@^4.0.3, stylis@^4.0.6: resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240" integrity sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg== +stylus-lookup@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/stylus-lookup/-/stylus-lookup-3.0.2.tgz#c9eca3ff799691020f30b382260a67355fefdddd" + integrity sha512-oEQGHSjg/AMaWlKe7gqsnYzan8DLcGIHe0dUaFkucZZ14z4zjENRlQMCHT4FNsiWnJf17YN9OvrCfCoi7VvOyg== + dependencies: + commander "^2.8.1" + debug "^4.1.0" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -14581,6 +15033,11 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + svg-parser@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" @@ -14635,6 +15092,11 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + tar@^4.4.12: version "4.4.19" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" @@ -14676,6 +15138,11 @@ temp-write@^4.0.0: temp-dir "^1.0.0" uuid "^3.3.2" +temp@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.4.0.tgz#671ad63d57be0fe9d7294664b3fc400636678a60" + integrity sha512-IsFisGgDKk7qzK9erMIkQe/XwiSUdac7z3wYOsjcLkhPBy3k1SlvLoIh2dAHIlEpgA971CgguMrx9z8fFg7tSA== + tempy@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/tempy/-/tempy-0.3.0.tgz#6f6c5b295695a16130996ad5ab01a8bd726e8bf8" @@ -14976,6 +15443,16 @@ ts-pnp@1.2.0, ts-pnp@^1.1.6: resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== +tsconfig-paths@^3.10.1: + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + tsconfig-paths@^3.9.0: version "3.10.1" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz#79ae67a68c15289fdf5c51cb74f397522d795ed7" @@ -15108,10 +15585,15 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@4.5.2: - version "4.5.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998" - integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw== +typescript@^3.9.10, typescript@^3.9.5, typescript@^3.9.7: + version "3.9.10" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" + integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== + +typescript@^4.9.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== uglify-js@^3.1.4: version "3.13.10" @@ -15467,6 +15949,11 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" +walkdir@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" + integrity sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ== + walker@^1.0.7, walker@~1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" @@ -15506,7 +15993,7 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -wcwidth@^1.0.0: +wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=