GithubHelp home page GithubHelp logo

seyaji / adelante Goto Github PK

View Code? Open in Web Editor NEW
9.0 2.0 3.0 423 KB

Frontend code generator for Solidity Smart Contracts

TypeScript 94.06% JavaScript 1.56% CSS 4.16% HTML 0.22%
code-generation javascript metamask react solidity solidity-contracts solidity-web typescript

adelante's Introduction

Adelante

what is adelante?

https://www.npmjs.com/package/adelante

This is a code generation tool to aid in the rapid prototyping of Smart Contracts built with Solidity.

See it in action at: https://adelante-test-app.vercel.app/

As of version 1.1.7 you can:

  • Generate inline functions or extract them to their own files
  • Generate inline components or extract them to their own files
  • Use a combination of the two, e.g inline functions and extracted components
  • Generate javascript files
  • Define the output path in the adelante.json

It automatically:

  • Generates metamask connect fucntionality and component
  • Generates a css file
  • Generates a html file
  • Generates an App.*sx file populated with the generated components

Other functionality:

It logs function calls and the return data from them (if any)

Screenshot 2022-07-18 at 11 37 47

Features in development before 1.2.0

  • Generate test files for the generated files - in development
  • Make html, index and app file generation optional (components and functions only, formatted for general use)

Planned features:

  • User defined themes

If you like it, send me a coffee :)


Eth address: 0x4A079D4417b522762C72dB9643234FCC4683a40E



how can I make it work??

Make sure that you have a package.json file before you install adelante. You can make one with the following command:

npm init

or

yarn init


To install adelante and start generating follow the instructions below:


You will be asked some questions to configure adelante.json when you use the Setup command.

If you want to use a default configuration and edit the json manually pick 'No' for the first question



For yarn users:


  1. Install using:
yarn add adelante
  1. Setup using:
yarn adelante --init
  1. Run using:
yarn adelante


For npm users:


  1. Install using:
npm install adelante
  1. Setup using:
npx adelante --init
  1. Run using:
npx adelante


it's reccomended that you

  • Copy your contracts .json file into the root project directory
  • Define the path to your contract.json file in the adelante.json file (if you didnt do it when you generated the adelante file)
  • It will genrate without a contract address but one should be addded if you want to it to work properly
{
  "useTypescript": true,
  "inlineFunctions": false,
  "inlineComponents": false,
  "contractPath": "/Contract.json", // Path to the compiled contract (found in your artifacts folder if using hardhat, it must be in your project directory)
  "generateTests": true,
  "contractAddress": "ENTER_CONTRACT_ADDRESS_HERE",
  "projectPath": "/my-app/src", // Path to app directory, if you want to use create-react-app the path should be to the src file
  "testDirectory": "./my-app/src" // Path to where you want the test file to be generated 
}

Generating files


Generating functions to interact with the Smart Contract

For each callable function on the Smart Contract a function will be generated. Most of these functions will work straight away, payable functions will need to be customised to work correctly.

  • With payable functions an ethers.utils.parseEther() is generated in the function file but an amount is not provided

How come?

Payable functions dont require the ether amount to de defined as an argument in the function inputs (these are recorded in the contract abi which is the source for the code generation)

The functions will return data if it is expected, otherwise a return statement will not generate.



import { getContract } from '../utils/utils'
/* 
if the function is payable it will use: 
import { ethers } from 'ethers';

and modify the function call to include it:
await contract.getRemainingUnits({ value: ethers.utils.parseEther("0.00")});
*/

export default async function getRemainingUnits() {
  try {
    const { ethereum } = window;
    const contract = getContract(ethereum);
    const data = await contract.getRemainingUnits();
    return data;
  }
  catch (error) {
    console.log(error);
    return "getRemainingUnits failed";
  }
}

Generating React components to call the functions

These components have inputs and a button to call the smart contract function along with state and a handleChange function

Screenshot 2022-07-12 at 16 10 54

Screenshot 2022-07-12 at 16 12 04

import React, { useState } from 'react';
import purchaseUnit from '../functions/purchaseUnit';

type Props = {
  handleMasterLogsChange: (data: any) => void;
};

type State = {
  [key: string]: string
};

export default function PurchaseUnit(props : Props) {
  const { handleMasterLogsChange } = props
  const [state, setState] = useState<State>({});
    
  const handleStateChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = event.target
    setState((prevState) => {
      return {
        ...prevState,
        [name]: value
      }
    })
    
  }
  
  const handleLogsClick = async (event: any) => {
    const data = await purchaseUnit(state?._amount)
    const outcome = data === "purchaseUnit failed" ? "failed" : `success: ${JSON.stringify(data)}`
    handleMasterLogsChange(["purchaseUnit", outcome])
  }

  return (
    <div className="function-box">
      <div className="box-heading">
        <h1>Purchase Unit</h1>
        <span className="text-extra"><p>purchaseUnit</p></span><p>Function inputs:</p>
          <p>(<span className="text-extra">uint16 _amount:</span> number)</p>
      </div>
          
      <div className="box-inputs">
        <p>_amount</p>
        <input name="_amount" onChange={handleStateChange} type="number" placeholder="_amount"/>
      </div>

        <button className="box-button" onClick={handleLogsClick} value="" >purchaseUnit</button>
    </div>
  )
}

Populate the appfile with the components

It will create the imports and pass props down to the child components for the masterLogsState The example below is from the hosted priview at: https://adelante-test-app.vercel.app/

import React, { useState } from 'react';
import './App.css';
import Footer from './Footer';
import Details from './Details';
import changeTheme from './changeTheme';
import Nav from './Nav';

import ReturnSum from './components/returnSum';
import StoreNumber from './components/storeNumber';
import WithdrawBalance from './components/withdrawBalance';
import AddBalance from './components/addBalance';
import GetBalance from './components/getBalance';
import GetNumber from './components/getNumber';
import Hello from './components/hello';

type MasterLogs = any[];

export default function App() {
  const [theme, setTheme] = useState("dark")
  const [masterLogs, setMasterLogs] = useState<MasterLogs>([]);

  const handleMasterLogsChange = (data: any) => {
    setMasterLogs(prevState => [data, ...prevState])
  }

  const handleTheme = () => {
    setTheme(changeTheme(theme))
  }
  return (
    <div className="App">
      <Nav handleTheme={handleTheme} />
      <Details masterLogs={masterLogs} />
        <div className="components">
          <ReturnSum handleMasterLogsChange={handleMasterLogsChange} />
          <StoreNumber handleMasterLogsChange={handleMasterLogsChange} />
          <WithdrawBalance handleMasterLogsChange={handleMasterLogsChange} />
          <AddBalance handleMasterLogsChange={handleMasterLogsChange} />
          <GetBalance handleMasterLogsChange={handleMasterLogsChange} />
          <GetNumber handleMasterLogsChange={handleMasterLogsChange} />
          <Hello handleMasterLogsChange={handleMasterLogsChange} />
        </div>
      <Footer />
    </div>
  )
}

Generating tests for the React Components


Tests for components

You have the option to generate tests for the react components created by adelante. If you choose this option adelante will read the ABI to determine some basic tests to be created.

An example of the generated test is below:

import '@testing-library/jest-dom'
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';

import ReturnSum from "../../test-app/components/ReturnSum";

type Props = {
  handleMasterLogsChange: (data: any) => void;
};

const setup = (propOverrides?: Partial<Props>) => {
  const props: Props = {
    handleMasterLogsChange: jest.fn(),
    ...propOverrides,
  }
  return render(<ReturnSum { ...props} />);
}

describe('Test for returnSum component', () => {
  it('should render without exploding, () => {}', () => {
    expect(() => setup()).not.toThrow();
  })
  it('should render ReturnSum inputs', () => {
    setup();
    expect(screen.getAllByRole("spinbutton").length).toBe(2);
    expect(screen.getByRole("spinbutton", {name: "_a"})).toBeInTheDocument()
    expect(screen.getByRole("spinbutton", {name: "_b"})).toBeInTheDocument()
  })


  it('should render the button to call the contract function', () => {
    setup();
    expect(screen.getByRole("button", { name: "returnSum" })).toBeInTheDocument()
  })

  it('should render the component heading correctly', () => {
    setup();
    expect(screen.getByRole("heading", { name: "Return Sum" })).toBeInTheDocument()
  })

  it('should call handleMasterLogsChange on button click', async () => {
    const consoleSpy = jest.spyOn(console, 'log').mockImplementation()
    const handleMock = jest.fn();
    setup({ handleMasterLogsChange: handleMock });

    const button = screen.getByRole("button", { name: "returnSum" });
    await userEvent.click(button);

    expect(handleMock).toHaveBeenCalled();
    expect(consoleSpy.mock.calls.length).toBe(1);
  })

  it('should handle input change correctly', async () => {
    setup();
    
    const _a = screen.getByRole("spinbutton", {name: "_a"})
    const _b = screen.getByRole("spinbutton", {name: "_b"})
    await userEvent.type(_a, "150") 
    await userEvent.type(_b, "150") 
    expect(screen.getByRole("spinbutton", {name: "_a"})).toHaveValue(150)
    expect(screen.getByRole("spinbutton", {name: "_b"})).toHaveValue(150)
  })
  
})

Tests for Functions

Adelante will generate tests for the functions, they are very basic, more improvements coming soon.

import getBalance from "../../test-app/functions/getBalance"

describe('Test for getBalance function', () => {
  it('should call the function', () => {
    const consoleSpy = jest.spyOn(console, 'log').mockImplementation()
    getBalance()
    expect(consoleSpy.mock.calls.length).toBe(1);
  })

})

adelante's People

Contributors

seyaji avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

adelante's Issues

Args Options: Dev mode and Production mode

Make Details page optional and / or only generate components and functions
If people want to use it as a quick start instead of a prototype with logging there should be an option to generate a minimal template without the details and theming options -- a 'barebones' version

Script: Auto install of dependancies

Script: Auto install of dependancies
When generating files add auto install of dependancies,
examples:

  • jest if generating tests
  • types if using typescript
  • etc

Incorrect file name

When I try to run the test application I get the following error:

Compiled with problems:X

ERROR in ./src/Nav.jsx 5:0-34

Module not found: Error: Cannot find file: 'Metamask.jsx' does not match the corresponding name on disk: './src/metamask.jsx'.

If I replace the filename with a capitalized version "Metamask.jsx", then the problem is fixed.

Logs: improve logs styling

Is your feature request related to a problem? Please describe.
Better spacing for logs & add highlighting
Screenshot 2022-07-16 at 15 50 40

Describe the solution you'd like
This picture:
Screenshot 2022-07-16 at 15 51 35

the above with some form of highlighting

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.