├── .gitignore
├── README.md
├── packages
├── abi-to-sol
│ ├── .gitignore
│ ├── jest.config.js
│ ├── src
│ │ ├── index.ts
│ │ ├── defaults.ts
│ │ ├── options.ts
│ │ ├── solidity.test.ts
│ │ ├── version-features.ts
│ │ ├── visitor.ts
│ │ ├── abi-features.ts
│ │ ├── declarations.ts
│ │ ├── declarations.test.ts
│ │ └── solidity.ts
│ ├── test
│ │ ├── compile-abi.ts
│ │ ├── preflight.ts
│ │ └── custom-example.ts
│ ├── package.json
│ ├── bin
│ │ └── abi-to-sol.ts
│ ├── README.md
│ └── tsconfig.json
└── web-ui
│ ├── src
│ ├── react-app-env.d.ts
│ ├── highlightjs-solidity.d.ts
│ ├── prettier-plugin-solidity.d.ts
│ ├── abi
│ │ ├── index.ts
│ │ ├── Header.tsx
│ │ ├── Editor.tsx
│ │ ├── Status.tsx
│ │ ├── examples
│ │ │ ├── DepositContract.abi.json
│ │ │ ├── BunchaStructs.abi.json
│ │ │ ├── AirSwap.abi.json
│ │ │ ├── ENS.abi.json
│ │ │ └── UniswapV2Router02.abi.json
│ │ ├── Input.ts
│ │ └── Examples.ts
│ ├── solidity
│ │ ├── index.ts
│ │ ├── Header.tsx
│ │ ├── Options.ts
│ │ ├── Status.tsx
│ │ ├── Code.tsx
│ │ ├── Controls.tsx
│ │ ├── OptionsControls.tsx
│ │ └── Output.ts
│ ├── setupTests.ts
│ ├── reportWebVitals.ts
│ ├── index.tsx
│ ├── Layout.tsx
│ ├── CopyButton.tsx
│ ├── makeSet.ts
│ ├── Footer.tsx
│ ├── logo.svg
│ ├── App.tsx
│ └── defaultAbi.json
│ ├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
│ ├── config
│ ├── jest
│ │ ├── cssTransform.js
│ │ ├── babelTransform.js
│ │ └── fileTransform.js
│ ├── pnpTs.js
│ ├── getHttpsConfig.js
│ ├── paths.js
│ ├── modules.js
│ ├── env.js
│ └── webpackDevServer.config.js
│ ├── .gitignore
│ ├── tsconfig.json
│ ├── scripts
│ ├── test.js
│ ├── start.js
│ └── build.js
│ ├── README.md
│ └── package.json
├── lerna.json
├── package.json
├── .github
└── workflows
│ ├── changelog.yml
│ ├── node.js.yml
│ ├── web-ui.yml
│ └── codeql-analysis.yml
├── LICENSE
├── RELEASE.md
└── CHANGELOG.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | packages/abi-to-sol/README.md
--------------------------------------------------------------------------------
/packages/abi-to-sol/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 |
--------------------------------------------------------------------------------
/packages/web-ui/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/packages/web-ui/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/packages/abi-to-sol/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: "ts-jest",
3 | testEnvironment: "node",
4 | };
5 |
--------------------------------------------------------------------------------
/packages/web-ui/src/highlightjs-solidity.d.ts:
--------------------------------------------------------------------------------
1 | declare module "highlightjs-solidity" {
2 | export = {} as any;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/web-ui/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Phonbopit/abi-to-sol/develop/packages/web-ui/public/favicon.ico
--------------------------------------------------------------------------------
/packages/web-ui/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Phonbopit/abi-to-sol/develop/packages/web-ui/public/logo192.png
--------------------------------------------------------------------------------
/packages/web-ui/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Phonbopit/abi-to-sol/develop/packages/web-ui/public/logo512.png
--------------------------------------------------------------------------------
/packages/web-ui/src/prettier-plugin-solidity.d.ts:
--------------------------------------------------------------------------------
1 | declare module "prettier-plugin-solidity" {
2 | export = {} as any;
3 | }
4 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": [
3 | "packages/*"
4 | ],
5 | "version": "independent",
6 | "npmClient": "yarn",
7 | "useWorkspaces": true
8 | }
9 |
--------------------------------------------------------------------------------
/packages/web-ui/src/abi/index.ts:
--------------------------------------------------------------------------------
1 | export { Header } from "./Header";
2 | export * as Input from "./Input";
3 | export { Editor } from "./Editor";
4 | export * as Examples from "./Examples";
5 |
--------------------------------------------------------------------------------
/packages/abi-to-sol/src/index.ts:
--------------------------------------------------------------------------------
1 | import "source-map-support/register";
2 |
3 | export * as defaults from "./defaults";
4 | export {generateSolidity} from "./solidity";
5 | export {GenerateSolidityOptions, GenerateSolidityMode} from "./options";
6 |
7 |
--------------------------------------------------------------------------------
/packages/web-ui/src/solidity/index.ts:
--------------------------------------------------------------------------------
1 | export { Header } from "./Header";
2 | export { Controls } from "./Controls";
3 | export { OptionsControls } from "./OptionsControls";
4 | export { Code } from "./Code";
5 | export * as Options from "./Options";
6 | export * as Output from "./Output";
7 |
--------------------------------------------------------------------------------
/packages/web-ui/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import "@testing-library/jest-dom";
6 |
--------------------------------------------------------------------------------
/packages/abi-to-sol/src/defaults.ts:
--------------------------------------------------------------------------------
1 | import { GenerateSolidityMode } from "./options";
2 |
3 | export const name = "MyInterface";
4 | export const license = "UNLICENSED";
5 | export const solidityVersion = ">=0.7.0 <0.9.0";
6 | export const prettifyOutput = true;
7 | export const outputAttribution = true;
8 | export const outputSource = true;
9 | export const mode = GenerateSolidityMode.Normal;
10 |
--------------------------------------------------------------------------------
/packages/web-ui/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is a custom Jest transformer turning style imports into empty objects.
4 | // http://facebook.github.io/jest/docs/en/webpack.html
5 |
6 | module.exports = {
7 | process() {
8 | return 'module.exports = {};';
9 | },
10 | getCacheKey() {
11 | // The output is always the same.
12 | return 'cssTransform';
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "root",
3 | "private": true,
4 | "scripts": {
5 | "lernaupdate": "lernaupdate",
6 | "prepare": "lerna bootstrap",
7 | "test": "lerna run test --stream --concurrency=1 -- --colors"
8 | },
9 | "devDependencies": {
10 | "lerna": "^4.0.0",
11 | "lerna-update-wizard": "^1.1.0"
12 | },
13 | "workspaces": {
14 | "packages": [
15 | "packages/*"
16 | ]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/web-ui/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/packages/web-ui/src/abi/Header.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import {
4 | Box,
5 | HStack,
6 | Spacer,
7 | Heading,
8 | } from "@chakra-ui/react";
9 |
10 | import { Status } from "./Status";
11 |
12 | export const Header = () => {
13 | return (
14 |
15 | ABI
16 |
17 |
18 |
19 |
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/packages/web-ui/src/solidity/Header.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import {
4 | Box,
5 | HStack,
6 | Spacer,
7 | Heading,
8 | } from "@chakra-ui/react";
9 |
10 | import { Status } from "./Status";
11 |
12 | export const Header = () => {
13 | return (
14 |
15 | Solidity
16 |
17 |
18 |
19 |
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/.github/workflows/changelog.yml:
--------------------------------------------------------------------------------
1 | name: Changelog Enforcer
2 |
3 | on:
4 | pull_request:
5 | branches: [ develop ]
6 |
7 | jobs:
8 | # Enforces the update of a changelog file on every pull request
9 | changelog:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 | - uses: dangoslen/changelog-enforcer@v2
14 | with:
15 | changeLogPath: 'CHANGELOG.md'
16 | skipLabels: 'skip-changelog'
17 |
18 |
--------------------------------------------------------------------------------
/packages/web-ui/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from "web-vitals";
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6 | getCLS(onPerfEntry);
7 | getFID(onPerfEntry);
8 | getFCP(onPerfEntry);
9 | getLCP(onPerfEntry);
10 | getTTFB(onPerfEntry);
11 | });
12 | }
13 | };
14 |
15 | export default reportWebVitals;
16 |
--------------------------------------------------------------------------------
/packages/abi-to-sol/src/options.ts:
--------------------------------------------------------------------------------
1 | import type * as Abi from "@truffle/abi-utils";
2 | import type {Abi as SchemaAbi} from "@truffle/contract-schema/spec";
3 |
4 | export enum GenerateSolidityMode {
5 | Normal = "normal",
6 | Embedded = "embedded"
7 | }
8 |
9 | export interface GenerateSolidityOptions {
10 | abi: Abi.Abi | SchemaAbi;
11 | name?: string;
12 | solidityVersion?: string;
13 | license?: string;
14 | mode?: GenerateSolidityMode;
15 | outputAttribution?: boolean;
16 | outputSource?: boolean;
17 | prettifyOutput?: boolean;
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/packages/web-ui/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import reportWebVitals from './reportWebVitals';
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | );
12 |
13 | // If you want to start measuring performance in your app, pass a function
14 | // to log results (for example: reportWebVitals(console.log))
15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
16 | reportWebVitals();
17 |
--------------------------------------------------------------------------------
/packages/web-ui/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/packages/web-ui/src/Layout.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import{
3 | Box,
4 | Flex,
5 | PropsOf,
6 | } from "@chakra-ui/react";
7 |
8 | export const Page: React.FC> = ({
9 | children,
10 | ...props
11 | }) => {
12 | return (
13 |
14 | {children}
15 |
16 | )
17 | }
18 |
19 |
20 | export const Container: React.FC> = ({
21 | children,
22 | ...props
23 | }) => {
24 | return (
25 |
31 | {children}
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/packages/web-ui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/packages/web-ui/config/jest/babelTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const babelJest = require('babel-jest');
4 |
5 | const hasJsxRuntime = (() => {
6 | if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
7 | return false;
8 | }
9 |
10 | try {
11 | require.resolve('react/jsx-runtime');
12 | return true;
13 | } catch (e) {
14 | return false;
15 | }
16 | })();
17 |
18 | module.exports = babelJest.createTransformer({
19 | presets: [
20 | [
21 | require.resolve('babel-preset-react-app'),
22 | {
23 | runtime: hasJsxRuntime ? 'automatic' : 'classic',
24 | },
25 | ],
26 | ],
27 | babelrc: false,
28 | configFile: false,
29 | });
30 |
--------------------------------------------------------------------------------
/packages/web-ui/config/pnpTs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { resolveModuleName } = require('ts-pnp');
4 |
5 | exports.resolveModuleName = (
6 | typescript,
7 | moduleName,
8 | containingFile,
9 | compilerOptions,
10 | resolutionHost
11 | ) => {
12 | return resolveModuleName(
13 | moduleName,
14 | containingFile,
15 | compilerOptions,
16 | resolutionHost,
17 | typescript.resolveModuleName
18 | );
19 | };
20 |
21 | exports.resolveTypeReferenceDirective = (
22 | typescript,
23 | moduleName,
24 | containingFile,
25 | compilerOptions,
26 | resolutionHost
27 | ) => {
28 | return resolveModuleName(
29 | moduleName,
30 | containingFile,
31 | compilerOptions,
32 | resolutionHost,
33 | typescript.resolveTypeReferenceDirective
34 | );
35 | };
36 |
--------------------------------------------------------------------------------
/packages/web-ui/src/CopyButton.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Text, Button, useClipboard } from "@chakra-ui/react";
3 | import { CopyIcon } from "@chakra-ui/icons";
4 |
5 | export interface CopyButtonOptions {
6 | text?: string;
7 | }
8 |
9 | export const CopyButton = ({ text }: CopyButtonOptions) => {
10 | let buttonText = "Copy to clipboard";
11 |
12 | const { hasCopied, onCopy } = useClipboard(text || "");
13 |
14 | if (!text) {
15 | buttonText = "No result";
16 | } else if (hasCopied) {
17 | buttonText = 'Copied'
18 | }
19 |
20 | return (
21 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [ master, develop ]
9 | pull_request:
10 | branches: [ master, develop ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | strategy:
18 | matrix:
19 | node-version: [14.x, 16.x, 18.x]
20 |
21 | steps:
22 | - uses: actions/checkout@v2
23 | - name: Use Node.js ${{ matrix.node-version }}
24 | uses: actions/setup-node@v1
25 | with:
26 | node-version: ${{ matrix.node-version }}
27 | - run: npm install -g yarn
28 | - run: yarn
29 | - run: yarn test
30 |
--------------------------------------------------------------------------------
/packages/web-ui/src/abi/Editor.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Box } from "@chakra-ui/react";
3 |
4 | import SimpleEditor from "react-simple-code-editor";
5 |
6 | // Highlight.js setup
7 | import "highlight.js/styles/default.css";
8 | import hljs from "highlight.js";
9 |
10 | import * as Input from "./Input";
11 |
12 | export const Editor = () => {
13 | const { contents, setContents } = Input.Container.useContainer();
14 |
15 | return (
16 |
20 | hljs.highlight(contents, { language: "json" }).value}
24 | style={{
25 | fontFamily: "SFMono-Regular,Menlo,Monaco,Consolas,monospace"
26 |
27 | }}
28 | />
29 |
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/packages/web-ui/src/makeSet.ts:
--------------------------------------------------------------------------------
1 | export interface ForStateOptions {
2 | state: State;
3 | setState(newState: State): void;
4 | }
5 |
6 | /**
7 | * Predicate that says whether a value should be interpreted as undefined
8 | */
9 | export type IsEmpty = (
10 | value: State[N]
11 | ) => boolean;
12 |
13 | export type MakeSet = (
14 | propertyName: N,
15 | isEmpty?: IsEmpty
16 | ) => (newValue: State[N]) => void;
17 |
18 | export const forState =
19 | ({ state, setState }: ForStateOptions): MakeSet =>
20 | (propertyName, isEmpty) =>
21 | (value) => {
22 | const newState = {
23 | ...state,
24 | };
25 |
26 | if (isEmpty && isEmpty(value)) {
27 | delete newState[propertyName];
28 | } else {
29 | newState[propertyName] = value;
30 | }
31 |
32 | setState(newState);
33 | };
34 |
--------------------------------------------------------------------------------
/packages/abi-to-sol/test/compile-abi.ts:
--------------------------------------------------------------------------------
1 | import {Abi as SchemaAbi} from "@truffle/contract-schema/spec";
2 |
3 | const solc = require("solc");
4 |
5 | export const compileAbi = (content: string): SchemaAbi => {
6 | const source = "interface.sol";
7 |
8 | const input = {
9 | language: "Solidity",
10 | sources: {
11 | [source]: {
12 | content,
13 | },
14 | },
15 | settings: {
16 | outputSelection: {
17 | "*": {
18 | "*": ["abi"],
19 | },
20 | },
21 | },
22 | };
23 |
24 | const output: any = JSON.parse(solc.compile(JSON.stringify(input)));
25 | const errors = (output.errors || []).filter(
26 | ({type}: any) => type !== "Warning"
27 | );
28 | if (errors.length > 0) {
29 | console.error(errors);
30 | }
31 | const {contracts} = output;
32 | const sourceOutput: any = contracts[source];
33 |
34 | return (Object.values(sourceOutput)[0] as any).abi;
35 | };
36 |
--------------------------------------------------------------------------------
/.github/workflows/web-ui.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Web UI
2 | on:
3 | push:
4 | branches:
5 | - master
6 |
7 | jobs:
8 | build-and-deploy:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout 🛎️
12 | uses: actions/checkout@v2.3.1
13 |
14 | - name: Install and Build 🔧 # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built.
15 | run: |
16 | yarn
17 | cd packages/abi-to-sol
18 | yarn prepare
19 | cd ../web-ui
20 | PUBLIC_URL=https://gnidan.github.io/abi-to-sol/ yarn build
21 |
22 | - name: Deploy 🚀
23 | uses: JamesIves/github-pages-deploy-action@4.1.4
24 | with:
25 | branch: gh-pages # The branch the action should deploy to.
26 | folder: packages/web-ui/build # The folder the action should deploy.
27 |
28 |
--------------------------------------------------------------------------------
/packages/web-ui/src/solidity/Options.ts:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { createContainer } from "unstated-next";
3 |
4 | import type { GenerateSolidityOptions } from "abi-to-sol";
5 | import { forState } from "../makeSet";
6 |
7 | export type State = Pick<
8 | GenerateSolidityOptions,
9 | "name" | "solidityVersion" | "license"
10 | > & {
11 | prettifyOutput: boolean;
12 | };
13 |
14 | export function useOptions() {
15 | const [state, setState] = React.useState({
16 | prettifyOutput: true,
17 | });
18 |
19 | const makeSet = forState({ state, setState });
20 |
21 | return {
22 | ...state,
23 | setName: makeSet("name", (value) => value === ""),
24 | setSolidityVersion: makeSet("solidityVersion", (value) => value === ""),
25 | setLicense: makeSet("license", (value) => value === ""),
26 | setPrettifyOutput: makeSet("prettifyOutput"),
27 | setState,
28 | };
29 | }
30 |
31 | export const Container = createContainer(useOptions);
32 |
--------------------------------------------------------------------------------
/packages/web-ui/src/abi/Status.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | Box,
4 | Center,
5 | Spinner,
6 | Text
7 | } from "@chakra-ui/react";
8 | import {
9 | CheckCircleIcon,
10 | WarningIcon,
11 | } from "@chakra-ui/icons";
12 |
13 | import * as Input from "./Input";
14 |
15 | export const Status = () => {
16 | const {
17 | isReading,
18 | contents: _contents,
19 | ...result
20 | } = Input.Container.useContainer();
21 |
22 | let component;
23 | if (isReading) {
24 | component = (
25 |
26 | Reading ABI{" "}
27 |
28 |
29 | )
30 | } else if ("abi" in result) {
31 | component = (
32 |
33 | Valid{" "}
34 |
35 |
36 | );
37 | } else {
38 | component = (
39 |
40 | Invalid (maybe copy/paste again?){" "}
41 |
42 |
43 | );
44 | }
45 |
46 | return (
47 |
48 | {component}
49 |
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 g. nicholas d'andrea
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/web-ui/src/Footer.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Box,
4 | Link,
5 | Heading,
6 | Text,
7 | } from "@chakra-ui/react";
8 | import { FaGithub } from "react-icons/fa";
9 | import { version } from "abi-to-sol/package.json";
10 |
11 | export const Footer = () => {
12 | return (
13 |
14 |
15 | abi-to-sol v{version}
19 |
20 |
21 |
22 |
23 | Tool & web UI © 2020-2022{" "}
24 |
28 | @gnidan
29 | and distributed under the MIT license.
30 |
31 |
32 |
36 | Having issues with this tool?
37 |
38 |
39 |
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/packages/web-ui/src/solidity/Status.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | Box,
4 | Center,
5 | Spinner,
6 | Text
7 | } from "@chakra-ui/react";
8 | import {
9 | CheckCircleIcon,
10 | QuestionIcon,
11 | WarningIcon,
12 | } from "@chakra-ui/icons";
13 |
14 | import * as Output from "./Output";
15 |
16 | export const Status = () => {
17 | const {
18 | isGenerating,
19 | ...result
20 | } = Output.Container.useContainer();
21 |
22 | let component;
23 | if (isGenerating) {
24 | component = (
25 |
26 | Generating{" "}
27 |
28 |
29 | )
30 | } else if ("contents" in result) {
31 | component = (
32 |
33 | Success{" "}
34 |
35 |
36 | );
37 | } else if ("error" in result) {
38 | component = (
39 |
40 | Error{" "}
41 |
42 |
43 | );
44 | } else {
45 | component = (
46 |
47 | Waiting for input{" "}
48 |
49 |
50 | );
51 | }
52 |
53 | return (
54 |
55 | {component}
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/packages/web-ui/src/abi/examples/DepositContract.abi.json:
--------------------------------------------------------------------------------
1 | [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"pubkey","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"withdrawal_credentials","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"amount","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"signature","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"index","type":"bytes"}],"name":"DepositEvent","type":"event"},{"inputs":[{"internalType":"bytes","name":"pubkey","type":"bytes"},{"internalType":"bytes","name":"withdrawal_credentials","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes32","name":"deposit_data_root","type":"bytes32"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"get_deposit_count","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"get_deposit_root","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}]
2 |
--------------------------------------------------------------------------------
/packages/web-ui/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const camelcase = require('camelcase');
5 |
6 | // This is a custom Jest transformer turning file imports into filenames.
7 | // http://facebook.github.io/jest/docs/en/webpack.html
8 |
9 | module.exports = {
10 | process(src, filename) {
11 | const assetFilename = JSON.stringify(path.basename(filename));
12 |
13 | if (filename.match(/\.svg$/)) {
14 | // Based on how SVGR generates a component name:
15 | // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6
16 | const pascalCaseFilename = camelcase(path.parse(filename).name, {
17 | pascalCase: true,
18 | });
19 | const componentName = `Svg${pascalCaseFilename}`;
20 | return `const React = require('react');
21 | module.exports = {
22 | __esModule: true,
23 | default: ${assetFilename},
24 | ReactComponent: React.forwardRef(function ${componentName}(props, ref) {
25 | return {
26 | $$typeof: Symbol.for('react.element'),
27 | type: 'svg',
28 | ref: ref,
29 | key: null,
30 | props: Object.assign({}, props, {
31 | children: ${assetFilename}
32 | })
33 | };
34 | }),
35 | };`;
36 | }
37 |
38 | return `module.exports = ${assetFilename};`;
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/packages/web-ui/scripts/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'test';
5 | process.env.NODE_ENV = 'test';
6 | process.env.PUBLIC_URL = '';
7 |
8 | // Makes the script crash on unhandled rejections instead of silently
9 | // ignoring them. In the future, promise rejections that are not handled will
10 | // terminate the Node.js process with a non-zero exit code.
11 | process.on('unhandledRejection', err => {
12 | throw err;
13 | });
14 |
15 | // Ensure environment variables are read.
16 | require('../config/env');
17 |
18 |
19 | const jest = require('jest');
20 | const execSync = require('child_process').execSync;
21 | let argv = process.argv.slice(2);
22 |
23 | function isInGitRepository() {
24 | try {
25 | execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
26 | return true;
27 | } catch (e) {
28 | return false;
29 | }
30 | }
31 |
32 | function isInMercurialRepository() {
33 | try {
34 | execSync('hg --cwd . root', { stdio: 'ignore' });
35 | return true;
36 | } catch (e) {
37 | return false;
38 | }
39 | }
40 |
41 | // Watch unless on CI or explicitly running all tests
42 | if (
43 | !process.env.CI &&
44 | argv.indexOf('--watchAll') === -1 &&
45 | argv.indexOf('--watchAll=false') === -1
46 | ) {
47 | // https://github.com/facebook/create-react-app/issues/5210
48 | const hasSourceControl = isInGitRepository() || isInMercurialRepository();
49 | argv.push(hasSourceControl ? '--watch' : '--watchAll');
50 | }
51 |
52 |
53 | jest.run(argv);
54 |
--------------------------------------------------------------------------------
/RELEASE.md:
--------------------------------------------------------------------------------
1 | # abi-to-sol Release Procedure
2 |
3 | 1. Cut release branch from `develop`
4 |
5 | ```console
6 | git checkout -b release
7 | git push -u origin release
8 | ```
9 |
10 | 2. Sanity-check: see what packages changed
11 |
12 | ```console
13 | npx lerna changed
14 | ```
15 |
16 | 3. Update package versions
17 |
18 | ```console
19 | npx lerna version
20 | ```
21 |
22 | 4. Rebuild project
23 |
24 | ```console
25 | yarn
26 | ```
27 |
28 | 5. Perform release
29 |
30 | ```console
31 | npx lerna publish from-package
32 | ```
33 |
34 | 6. Update CHANGELOG.md, replacing `vNext` with corresponding version, and
35 | adding link to release notes page (although URL won't exist yet)
36 |
37 | ```console
38 | vim CHANGELOG.md
39 | git add CHANGELOG.md
40 | git commit -m "Update CHANGELOG"
41 | ```
42 |
43 | 7. PR `release` -> `develop` and then delete branch `release` on GitHub
44 | once merged.
45 |
46 | 8. Delete local `release` branch
47 |
48 | ```console
49 | git checkout develop
50 | git pull
51 | git branch -D release
52 | ```
53 |
54 | 9. Sync `master` with `develop` and such
55 |
56 | ```console
57 | git checkout master
58 | git pull
59 | git merge develop
60 | git push
61 | git checkout develop
62 | git merge master
63 | git push
64 | ```
65 |
66 | 10. Write+publish release notes on GitHub (**don't forget to start
67 | discussion topic for the release**)
68 |
69 | 11. Wait for Web UI to build and visit page to make sure everything's
70 | honkidori
71 |
72 | 12. Install abi-to-sol package for good measure
73 |
--------------------------------------------------------------------------------
/packages/web-ui/src/abi/examples/BunchaStructs.abi.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "anonymous": false,
4 | "inputs": [
5 | {
6 | "components": [
7 | {
8 | "internalType": "uint256",
9 | "name": "a",
10 | "type": "uint256"
11 | }
12 | ],
13 | "indexed": false,
14 | "internalType": "struct BunchaStructs.A[]",
15 | "name": "a",
16 | "type": "tuple[]"
17 | },
18 | {
19 | "components": [
20 | {
21 | "components": [
22 | {
23 | "components": [
24 | {
25 | "components": [
26 | {
27 | "internalType": "address",
28 | "name": "e",
29 | "type": "address"
30 | }
31 | ],
32 | "internalType": "struct Other.E",
33 | "name": "e",
34 | "type": "tuple"
35 | },
36 | {
37 | "internalType": "string",
38 | "name": "d",
39 | "type": "string"
40 | }
41 | ],
42 | "internalType": "struct C[]",
43 | "name": "c",
44 | "type": "tuple[]"
45 | }
46 | ],
47 | "internalType": "struct B",
48 | "name": "b",
49 | "type": "tuple"
50 | },
51 | {
52 | "components": [
53 | {
54 | "internalType": "address",
55 | "name": "e",
56 | "type": "address"
57 | }
58 | ],
59 | "internalType": "struct Other.E",
60 | "name": "e",
61 | "type": "tuple"
62 | }
63 | ],
64 | "indexed": false,
65 | "internalType": "struct Other.D",
66 | "name": "d",
67 | "type": "tuple"
68 | }
69 | ],
70 | "name": "Event",
71 | "type": "event"
72 | }
73 | ]
74 |
--------------------------------------------------------------------------------
/packages/web-ui/src/solidity/Code.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Alert, AlertIcon, AlertTitle, AlertDescription, Box } from "@chakra-ui/react";
3 |
4 | import * as Output from "./Output";
5 |
6 | // Highlight.js setup
7 | import "highlight.js/styles/default.css";
8 | import hljs from "highlight.js";
9 | import hljsDefineSolidity from "highlightjs-solidity";
10 | hljsDefineSolidity(hljs);
11 | hljs.initHighlightingOnLoad();
12 |
13 |
14 | export const Code = () => {
15 | const { isGenerating, ...result } = Output.Container.useContainer();
16 |
17 | const [html, setHtml] = React.useState("");
18 | const [showHtml, setShowHtml] = React.useState(false);
19 |
20 | const error = "error" in result && result.error;
21 |
22 | React.useEffect(() => {
23 | if (isGenerating) {
24 | setShowHtml(false);
25 | return;
26 | }
27 |
28 | if ("contents" in result) {
29 | const { contents } = result;
30 |
31 | try {
32 | setHtml(hljs.highlight(contents, { language: "solidity" }).value);
33 | setShowHtml(true);
34 | } catch {
35 | setHtml(contents);
36 | setShowHtml(true);
37 | }
38 |
39 | return;
40 | }
41 |
42 | setShowHtml(false);
43 |
44 | }, [isGenerating, result]);
45 |
46 | return (
47 |
48 | {error && (
49 |
50 |
51 | Could not generate Solidity!
52 | {error.message}
53 |
54 | )}
55 | {showHtml && (
56 |
59 | )}
60 |
61 | )
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/packages/abi-to-sol/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "abi-to-sol",
3 | "version": "0.6.6",
4 | "description": "Compile ABI JSON to Solidity interface",
5 | "main": "dist/src/index.js",
6 | "types": "dist/src/index.d.ts",
7 | "bin": {
8 | "abi-to-sol": "dist/bin/abi-to-sol.js"
9 | },
10 | "files": [
11 | "dist"
12 | ],
13 | "author": "g. nicholas d'andrea ",
14 | "license": "MIT",
15 | "scripts": {
16 | "abi-to-sol": "ts-node ./bin/abi-to-sol.ts",
17 | "prepare": "tsc",
18 | "test": "jest src/**",
19 | "test:test": "jest test/**",
20 | "test:dist": "yarn prepare && jest dist/src",
21 | "test:dist:test": "yarn prepare && jest dist/test"
22 | },
23 | "homepage": "https://github.com/gnidan/abi-to-sol#readme",
24 | "repository": {
25 | "type": "git",
26 | "url": "https://github.com/gnidan/abi-to-sol.git",
27 | "directory": "packages/abi-to-sol"
28 | },
29 | "devDependencies": {
30 | "@types/faker": "^5.1.2",
31 | "@types/jest": "^26.0.14",
32 | "@types/jest-json-schema": "^2.1.2",
33 | "@types/prettier": "^2.1.1",
34 | "@types/semver": "^7.3.7",
35 | "change-case": "^4.1.1",
36 | "faker": "^5.1.0",
37 | "fast-check": "^3.1.1",
38 | "husky": ">=4",
39 | "jest": "^26.4.2",
40 | "jest-fast-check": "^0.0.1",
41 | "jest-json-schema": "^2.1.0",
42 | "lint-staged": ">=10",
43 | "solc": "^0.8.6",
44 | "ts-jest": "^26.4.0",
45 | "ts-node": "^9.0.0",
46 | "typescript": "4.5.2"
47 | },
48 | "dependencies": {
49 | "@truffle/abi-utils": "^0.3.0",
50 | "@truffle/contract-schema": "^3.3.1",
51 | "ajv": "^6.12.5",
52 | "better-ajv-errors": "^0.8.2",
53 | "neodoc": "^2.0.2",
54 | "semver": "^7.3.5",
55 | "source-map-support": "^0.5.19"
56 | },
57 | "optionalDependencies": {
58 | "prettier": "^2.7.1",
59 | "prettier-plugin-solidity": "^1.0.0-dev.23"
60 | },
61 | "husky": {
62 | "hooks": {
63 | "pre-commit": "lint-staged"
64 | }
65 | },
66 | "lint-staged": {
67 | "*.{ts,js,css,md}": "prettier --write"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/packages/web-ui/config/getHttpsConfig.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const crypto = require('crypto');
6 | const chalk = require('react-dev-utils/chalk');
7 | const paths = require('./paths');
8 |
9 | // Ensure the certificate and key provided are valid and if not
10 | // throw an easy to debug error
11 | function validateKeyAndCerts({ cert, key, keyFile, crtFile }) {
12 | let encrypted;
13 | try {
14 | // publicEncrypt will throw an error with an invalid cert
15 | encrypted = crypto.publicEncrypt(cert, Buffer.from('test'));
16 | } catch (err) {
17 | throw new Error(
18 | `The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}`
19 | );
20 | }
21 |
22 | try {
23 | // privateDecrypt will throw an error with an invalid key
24 | crypto.privateDecrypt(key, encrypted);
25 | } catch (err) {
26 | throw new Error(
27 | `The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${
28 | err.message
29 | }`
30 | );
31 | }
32 | }
33 |
34 | // Read file and throw an error if it doesn't exist
35 | function readEnvFile(file, type) {
36 | if (!fs.existsSync(file)) {
37 | throw new Error(
38 | `You specified ${chalk.cyan(
39 | type
40 | )} in your env, but the file "${chalk.yellow(file)}" can't be found.`
41 | );
42 | }
43 | return fs.readFileSync(file);
44 | }
45 |
46 | // Get the https config
47 | // Return cert files if provided in env, otherwise just true or false
48 | function getHttpsConfig() {
49 | const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env;
50 | const isHttps = HTTPS === 'true';
51 |
52 | if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) {
53 | const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE);
54 | const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE);
55 | const config = {
56 | cert: readEnvFile(crtFile, 'SSL_CRT_FILE'),
57 | key: readEnvFile(keyFile, 'SSL_KEY_FILE'),
58 | };
59 |
60 | validateKeyAndCerts({ ...config, keyFile, crtFile });
61 | return config;
62 | }
63 | return isHttps;
64 | }
65 |
66 | module.exports = getHttpsConfig;
67 |
--------------------------------------------------------------------------------
/packages/web-ui/src/solidity/Controls.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | Box,
4 | HStack,
5 | Stack,
6 | FormLabel,
7 | Center,
8 | Spacer,
9 | Input,
10 | Switch,
11 | InputGroup,
12 | InputLeftElement,
13 | } from "@chakra-ui/react";
14 | import { LockIcon } from "@chakra-ui/icons";
15 |
16 | import * as Output from "./Output";
17 | import * as Options from "./Options";
18 | import { CopyButton } from "../CopyButton";
19 |
20 | export const Controls = () => {
21 | const { isGenerating, ...result } = Output.Container.useContainer();
22 | const {
23 | license,
24 | prettifyOutput,
25 | setLicense,
26 | setPrettifyOutput
27 | } = Options.Container.useContainer();
28 |
29 | return (
30 |
35 |
36 | License
37 |
38 | }
41 | />
42 | ) => {
48 | setLicense(event.target.value)
49 | }}
50 | />
51 |
52 |
53 |
54 | Prettify output?
55 |
56 | {
60 | setPrettifyOutput(!prettifyOutput);
61 | }}
62 | />
63 |
64 |
65 |
66 |
67 |
68 |
75 |
76 |
77 |
78 | );
79 | }
80 |
--------------------------------------------------------------------------------
/packages/web-ui/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `yarn start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `yarn test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `yarn build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `yarn eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
--------------------------------------------------------------------------------
/packages/web-ui/src/abi/Input.ts:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { createContainer } from "unstated-next";
3 | import TruffleContractSchema from "@truffle/contract-schema";
4 |
5 | import type { GenerateSolidityOptions } from "abi-to-sol";
6 |
7 | import { forState } from "../makeSet";
8 |
9 | export interface CommonState {
10 | contents: string;
11 | }
12 |
13 | export interface ReadingState {
14 | isReading: true;
15 | }
16 |
17 | export interface SuccessState {
18 | isReading: false;
19 | abi: GenerateSolidityOptions["abi"];
20 | }
21 |
22 | export interface ErrorState {
23 | isReading: false;
24 | error: Error;
25 | }
26 |
27 | export type State = CommonState & (ReadingState | SuccessState | ErrorState);
28 |
29 | type ReadResult = Pick | Pick;
30 |
31 | export const useInput = (defaultContents: string = "") => {
32 | const [state, setState] = React.useState({
33 | contents: defaultContents,
34 | isReading: true,
35 | });
36 |
37 | const makeSet = forState({ state, setState });
38 |
39 | const setContents = makeSet("contents");
40 |
41 | React.useEffect(() => {
42 | // mark isReading
43 | setState({
44 | contents: state.contents,
45 | isReading: true,
46 | });
47 |
48 | // read
49 | const result = readContents(state.contents);
50 |
51 | // mark result
52 | setState(
53 | "abi" in result
54 | ? ({
55 | contents: state.contents,
56 | isReading: false,
57 | abi: result.abi,
58 | } as const)
59 | : ({
60 | contents: state.contents,
61 | isReading: false,
62 | error: result.error,
63 | } as const)
64 | );
65 | }, [state.contents]);
66 |
67 | return {
68 | ...state,
69 | setContents,
70 | };
71 | };
72 |
73 | function readContents(contents: string): ReadResult {
74 | try {
75 | const abi = JSON.parse(contents);
76 |
77 | try {
78 | TruffleContractSchema.validate({ abi });
79 | } catch (error) {
80 | console.error(error);
81 | throw error;
82 | }
83 |
84 | return { abi };
85 | } catch (error) {
86 | return { error: error as Error };
87 | }
88 | }
89 |
90 | export const Container = createContainer(useInput);
91 |
--------------------------------------------------------------------------------
/packages/abi-to-sol/src/solidity.test.ts:
--------------------------------------------------------------------------------
1 | import * as fc from "fast-check";
2 | import {testProp} from "jest-fast-check";
3 | import * as Abi from "@truffle/abi-utils";
4 | import * as Example from "../test/custom-example";
5 | import {compileAbi} from "../test/compile-abi";
6 | import {excludesFunctionParameters} from "../test/preflight";
7 |
8 | import {generateSolidity} from "./solidity";
9 |
10 | const removeProps = (obj: any, keys: Set) => {
11 | if (obj instanceof Array) {
12 | for (const item of obj) {
13 | removeProps(item, keys);
14 | }
15 | } else if (typeof obj === "object") {
16 | for (const [key, value] of Object.entries(obj)) {
17 | if (keys.has(key)) {
18 | delete obj[key];
19 | } else {
20 | removeProps(obj[key], keys);
21 | }
22 | }
23 | }
24 |
25 | return obj;
26 | };
27 |
28 | describe("generateSolidity", () => {
29 | testProp("compiles to input ABI", [Abi.Arbitrary.Abi()], (abi) => {
30 | fc.pre(
31 | abi.every((entry) => "type" in entry && entry.type !== "constructor")
32 | );
33 | fc.pre(excludesFunctionParameters(abi));
34 |
35 | fc.pre(abi.length > 0);
36 |
37 | const output = generateSolidity({
38 | name: "MyInterface",
39 | abi,
40 | solidityVersion: "^0.8.4",
41 | });
42 |
43 | let resultAbi;
44 | try {
45 | resultAbi = compileAbi(output);
46 | } catch (error) {
47 | console.log("Failed to compile. Solidity:\n%s", output);
48 | throw error;
49 | }
50 |
51 | const compiledAbi = new Set(
52 | removeProps(resultAbi, new Set(["internalType"]))
53 | );
54 |
55 | const expectedAbi = new Set(Abi.normalize(abi));
56 |
57 | expect(compiledAbi).toEqual(expectedAbi);
58 | });
59 |
60 | describe("custom example", () => {
61 | const abiWithoutConstructor = Abi.normalize(
62 | Example.abi.filter(({type}) => type !== "constructor")
63 | );
64 |
65 | const output = generateSolidity({
66 | name: "Example",
67 | abi: abiWithoutConstructor,
68 | solidityVersion: "^0.8.4",
69 | });
70 |
71 | it("generates output", () => {
72 | const compiledAbi = compileAbi(output);
73 |
74 | const expectedAbi = abiWithoutConstructor.map((entry) => ({
75 | ...entry,
76 | type: entry.type || "function",
77 | }));
78 |
79 | expect(compiledAbi).toEqual(expectedAbi);
80 | });
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/packages/abi-to-sol/src/version-features.ts:
--------------------------------------------------------------------------------
1 | import * as semver from "semver";
2 |
3 | export const mixed: unique symbol = Symbol();
4 |
5 | export const allFeatures = {
6 | "receive-keyword": {
7 | ">=0.6.0": true,
8 | "<0.6.0": false
9 | },
10 | "fallback-keyword": {
11 | ">=0.6.0": true,
12 | "<0.6.0": false
13 | },
14 | "array-parameter-location": {
15 | ">=0.7.0": "memory",
16 | "^0.5.0 || ^0.6.0": "calldata",
17 | "<0.5.0": undefined
18 | },
19 | "abiencoder-v2": {
20 | ">=0.8.0": "default",
21 | "<0.8.0": "experimental",
22 | },
23 | "global-structs": {
24 | ">=0.6.0": true,
25 | "<0.6.0": false
26 | },
27 | "structs-in-interfaces": {
28 | ">=0.5.0": true,
29 | "<0.5.0": false
30 | },
31 | "custom-errors": {
32 | ">=0.8.4": true,
33 | "<0.8.4": false
34 | },
35 | "user-defined-value-types": {
36 | ">=0.8.8": true,
37 | "<0.8.8": false
38 | }
39 | } as const;
40 |
41 | export type AllFeatures = typeof allFeatures;
42 |
43 | export type Category = keyof AllFeatures;
44 |
45 | export type CategoryOptions = AllFeatures[C];
46 |
47 | export type CategoryOptionRange = string & {
48 | [K in C]: keyof CategoryOptions
49 | }[C];
50 |
51 | export type CategoryOption = {
52 | [K in C]: CategoryOptions[CategoryOptionRange]
53 | }[C];
54 |
55 | export type VersionFeatures = {
56 | [C in Category]: VersionFeature;
57 | };
58 |
59 | export type VersionFeature =
60 | CategoryOption | typeof mixed;
61 |
62 | export const forRange = (range: string | semver.Range): VersionFeatures => {
63 | const forCategory = (
64 | category: C
65 | ): VersionFeature => {
66 | const options = allFeatures[category];
67 | const matchingRanges: CategoryOptionRange[] =
68 | (Object.keys(options) as CategoryOptionRange[])
69 | .filter(optionRange => semver.intersects(range, optionRange));
70 |
71 | if (matchingRanges.length > 1) {
72 | return mixed;
73 | }
74 |
75 | const [matchingRange] = matchingRanges;
76 | return options[matchingRange];
77 | }
78 |
79 | return (Object.keys(allFeatures) as Category[])
80 | .map((category: C) => ({ [category]: forCategory(category) }))
81 | .reduce((a, b) => ({ ...a, ...b }), {}) as VersionFeatures;
82 | }
83 |
--------------------------------------------------------------------------------
/packages/web-ui/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/abi-to-sol/src/visitor.ts:
--------------------------------------------------------------------------------
1 | import type {Abi as SchemaAbi} from "@truffle/contract-schema/spec";
2 | import * as Abi from "@truffle/abi-utils";
3 |
4 | export interface VisitOptions {
5 | node: N;
6 | context?: C;
7 | }
8 |
9 | export interface Visitor {
10 | visitAbi(options: VisitOptions): T;
11 | visitFunctionEntry(options: VisitOptions): T;
12 | visitConstructorEntry(options: VisitOptions): T;
13 | visitFallbackEntry(options: VisitOptions): T;
14 | visitReceiveEntry(options: VisitOptions): T;
15 | visitEventEntry(options: VisitOptions): T;
16 | visitErrorEntry(options: VisitOptions): T;
17 | visitParameter(options: VisitOptions): T;
18 | }
19 |
20 | export interface DispatchOptions {
21 | node: Node | SchemaAbi;
22 | visitor: Visitor;
23 | context?: C;
24 | }
25 |
26 | export type Node =
27 | | Abi.Abi
28 | | Abi.Entry
29 | | Abi.FunctionEntry
30 | | Abi.ConstructorEntry
31 | | Abi.FallbackEntry
32 | | Abi.ReceiveEntry
33 | | Abi.EventEntry
34 | | Abi.Parameter
35 | | Abi.EventParameter;
36 |
37 | export const dispatch = (options: DispatchOptions): T => {
38 | const {node, visitor, context} = options;
39 |
40 | if (isAbi(node)) {
41 | return visitor.visitAbi({
42 | node: Abi.normalize(node),
43 | context,
44 | });
45 | }
46 |
47 | if (isEntry(node)) {
48 | switch (node.type) {
49 | case "function":
50 | return visitor.visitFunctionEntry({node, context});
51 | case "constructor":
52 | return visitor.visitConstructorEntry({node, context});
53 | case "fallback":
54 | return visitor.visitFallbackEntry({node, context});
55 | case "receive":
56 | return visitor.visitReceiveEntry({node, context});
57 | case "event":
58 | return visitor.visitEventEntry({node, context});
59 | case "error":
60 | return visitor.visitErrorEntry({node, context});
61 | }
62 | }
63 |
64 | return visitor.visitParameter({node, context});
65 | };
66 |
67 | const isAbi = (node: Node | SchemaAbi): node is Abi.Abi | SchemaAbi =>
68 | node instanceof Array;
69 |
70 | const isEntry = (node: Node): node is Abi.Entry =>
71 | typeof node === "object" &&
72 | "type" in node &&
73 | ["function", "constructor", "fallback", "receive", "event", "error"].includes(
74 | node.type
75 | ) &&
76 | (node.type !== "function" || "stateMutability" in node || "constant" in node);
77 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ develop ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ develop ]
20 | schedule:
21 | - cron: '22 11 * * 0'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'javascript' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v2
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v1
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
52 |
53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54 | # If this step fails, then you should remove it and run the build manually (see below)
55 | - name: Autobuild
56 | uses: github/codeql-action/autobuild@v1
57 |
58 | # ℹ️ Command-line programs to run using the OS shell.
59 | # 📚 https://git.io/JvXDl
60 |
61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62 | # and modify them (or add more) to build your code if your project
63 | # uses a compiled language
64 |
65 | #- run: |
66 | # make bootstrap
67 | # make release
68 |
69 | - name: Perform CodeQL Analysis
70 | uses: github/codeql-action/analyze@v1
71 |
--------------------------------------------------------------------------------
/packages/web-ui/config/paths.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 | const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');
6 |
7 | // Make sure any symlinks in the project folder are resolved:
8 | // https://github.com/facebook/create-react-app/issues/637
9 | const appDirectory = fs.realpathSync(process.cwd());
10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11 |
12 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
13 | // "public path" at which the app is served.
14 | // webpack needs to know it to put the right
28 |
31 |
32 |
33 |
46 |
47 |
48 |
49 |
50 |
51 |
61 |
62 |