WEB3DEV

Cover image for Gerador de Contratos Inteligentes Solidity Alimentado pelo ChatGPT-3 da OpenAI
Paulo Gio
Paulo Gio

Posted on

Gerador de Contratos Inteligentes Solidity Alimentado pelo ChatGPT-3 da OpenAI

Eu criei um Gerador de Contratos Inteligentes Solidity usando as APIs da OpenAI. Neste artigo, vou explicar como eu fiz.

Você pode experimentar o gerador aqui.

https://solidity-smart-contract-using-chatgpt.vercel.app/

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*dmizICKdjnm1Q1NfPM6DCg.jpeg

Primeiro, crie um aplicativo do Next.js.

yarn create next-app
Enter fullscreen mode Exit fullscreen mode

https://miro.medium.com/v2/resize:fit:750/format:webp/1*i2hSZYIHU1kBB1UQV2y0AQ.png

O comando acima fez algumas perguntas e eu nomeei o diretório do meu projeto como my_test_project. Agora, vá para o aplicativo do Next.js que você criou, neste caso, ele está dentro da pasta my_test_project.

cd my_test_project
Enter fullscreen mode Exit fullscreen mode

Depois de entrar na pasta, execute o aplicativo.

yarn dev
Enter fullscreen mode Exit fullscreen mode

Você verá a tela de boas-vindas do aplicativo Next.js.

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*rKF88fWJRD5aUrpIvQAVoA.png

Abra um novo terminal ou pressione CTRL + C para encerrar o comando run dev. Depois disso, adicione os pacotes necessários. Como eu usei o Bootstrap, adicionei também o pacote do Bootstrap.

yarn add bootstrap
yarn add next-base64
yarn add openai
yarn add sanitize-html
Enter fullscreen mode Exit fullscreen mode

Adicione o Bootstrap no arquivo _app.js.

// Adicione o CSS do Bootstrap
import 'bootstrap/dist/css/bootstrap.css'
export default function App({ Component, pageProps }) {
 return <Component {...pageProps} />
}
Enter fullscreen mode Exit fullscreen mode

Crie um arquivo .env e cole a chave de API da OpenAI.

OPENAI_API_KEY=
Enter fullscreen mode Exit fullscreen mode

Você obterá esta chave de API daqui:

https://beta.openai.com/account/api-keys

Remova todo o código do arquivo index.js e cole este código.

import Head from "next/head";
import { useState } from "react";
import sanitizeHtml from "sanitize-html";

import Link from 'next/link';
import nextBase64 from 'next-base64';



export default function Home() {
   const [userInput, setUserInput] = useState("");
   const [apiOutput, setApiOutput] = useState("You will see output here");
   const [userInputSelect, setUserInputSelect] = useState("");
   const [checked, setChecked] = useState(false);
   const [apiOutputForRemix, setApiOutputForRemix] = useState("");

   const onUserChangedText = (event) => {
       console.log(event.target.value);
       setUserInput(event.target.value);
   };

   const onSelectOption = (event) => {
       console.log(event.target.value);
       setUserInput(event.target.value);
   };

   const handleChange = (event) => {
       setChecked(event.target.checked);
   };

   const OpenInRemixButton = ({ fileUrl, fileCode }) => {

       const fileCodeBase64 = nextBase64.encode(fileCode);

       return (
         <Link
           href={fileUrl}
           as={`https://remix.ethereum.org?code=${fileCodeBase64}`}
           prefetch={false}
           passHref
           legacyBehavior
         >
           <a data-url={fileUrl} target="_blank" className="btn btn-outline-success my-2 my-sm-0 float-right" data-content={fileCodeBase64}>Open in Remix</a>
         </Link>
       );
     };

   const callGenerateEndpoint = async () => {
       setApiOutput(`Please Wait ....`);

       console.log("Calling OpenAI...");
       const response = await fetch("/api/chatgpt", {
           method: "POST",
           headers: {
               "Content-Type": "application/json",
           },
           body: JSON.stringify({ userInput, checked }),
       });

       const data = await response.json();
       const { output } = data;
       console.log("OpenAI replied...", output.text);

       setApiOutputForRemix(output.text)

       const formattedText = output.text.replace(/\n/g, "<br>");
       const sanitizedOutput = sanitizeHtml(formattedText);

       setApiOutput(`${sanitizedOutput}`);
   };

   return (
       <>
           <main
               className="px-2 py-3 my-3 container"
               style={{ border: "0px solid #ccc", background:"#fff" }}
           >
               <Head>
                   <title>Create Your Smart Contract</title>
                   <meta name="description" content="Create Smart Contract" />
                   <link rel="icon" href="/favicon.ico" />
               </Head>
               <div className="row">
                   <div className="col-md-6 text-left" style={{ border: "1px solid #ccc", background:"#eee",marginLeft:"0px" }} >
                       <div className="mx-4 px-4 py-5 my-5 text-left flex-grow-1" >
                           <h2 className="">
                               Create Solidity Smart Contract
                           </h2>
                           <br></br>
                           <div className="">
                               <p className="lead mb-4">
                                   <select
                                       className="form-control form-control-lg"
                                       value={userInputSelect}
                                       onChange={onSelectOption}
                                   >
                                       <option>Select Smart Contract</option>
                                       <option value="NFT">NFT</option>
                                       <option value="NFT with Royalty">
                                           NFT with Royalty
                                       </option>
                                       <option value="PaymentSplitter">
                                           PaymentSplitter
                                       </option>
                                       <option value="VestingWallet">
                                           VestingWallet
                                       </option>
                                       <option value="Timelock">
                                           Timelock
                                       </option>
                                       <option value="Pausable">
                                           Pausable
                                       </option>
                                       <option value="ReentrancyGuard">
                                           ReentrancyGuard
                                       </option>
                                       <option value="Uniswap">Uniswap</option>
                                       <option value="Twitter">Twitter</option>
                                       <option value="Hospital OPD">
                                           Hospital OPD
                                       </option>
                                       <option value="Hospital Labtest">
                                           Hospital Labtest
                                       </option>
                                       <option value="Cricket Records">
                                           Cricket Records
                                       </option>
                                       <option value="Football World Cup">
                                           Football World Cup
                                       </option>

                                   </select>
                                   <br></br>

                               <div className="d-grid gap-2 d-sm-flex justify-content-sm-center">
                                   or
                                   </div>
                                   <br></br>
                                   <textarea
                                       name=""
                                       className="form-control"
                                       rows={4}
                                       placeholder="e.g Supply Chain Management, Property ownership, Renting, Clinic Records, NFTs"
                                       value={userInput}
                                       onChange={onUserChangedText}
                                   ></textarea>
                                   <br></br>
                                   <input
                                       type="checkbox"
                                       checked={checked}
                                       onChange={handleChange}
                                   ></input>{" "}
                                   <i>Use OpenZeppelin</i>
                               </p>
                               <div className="d-grid gap-2 d-sm-flex justify-content-sm-center">
                                   <button
                                       type="button"
                                       className="btn btn-primary btn-lg px-4 gap-3"
                                       onClick={callGenerateEndpoint}
                                   >
                                       Create Smart Contract
                                   </button>
                               </div>
                               <div className="d-grid gap-2 d-sm-flex justify-content-sm-center">
                                   Powered by OpenAI GPT-3
                               </div>
                           </div>
                       </div>
                   </div>
                   <div className="col-md-6" style={{ border: "0px solid #000", background:"#fff",marginLeft:"0px" }} >
                       <nav className="navbar navbar-light" style={{ background:"#000", padding: "13px",paddingBottom:"0px"}}>


                               <OpenInRemixButton
                               fileUrl="https://remix.ethereum.org/#version=soljson-v0.7.7+commit.9e61f92b.js&optimize=false&gist=e2e5c1b5e6fb5c6f9b8d5f6b5d2ebeb1"
                               fileCode={apiOutputForRemix}
                               />


                       </nav>

                       <div className="text-left flex-grow-1" style={{ border: "1px solid #000", background:"#000", color:"#39FF14" }}>
                           <div>
                               {apiOutput && (
                                   <div className="output">

                                       <div className="output-content">
                                           <pre>
                                               <code>
                                                   <div style={{ padding:"10px" }}
                                                       dangerouslySetInnerHTML={{
                                                           __html: apiOutput,
                                                       }}
                                                   ></div>
                                               </code>
                                           </pre>
                                       </div>
                                   </div>
                               )}
                           </div>
                       </div>
                   </div>
               </div>
           </main>
       </>
   );
}
Enter fullscreen mode Exit fullscreen mode

Deixe-me explicar o código acima:

Primeiro, importei todos os pacotes necessários.

import Head from "next/head";
import { useState } from "react";
import sanitizeHtml from "sanitize-html";

import Link from 'next/link';
import nextBase64 from 'next-base64';
Enter fullscreen mode Exit fullscreen mode

A importação sanitize-html é usada para limpar o HTML, next/link é usado para links e next-base64 é usado para codificar o código gerado e passá-lo para o URL.

export default function Home() {
   const [userInput, setUserInput] = useState("");
   const [apiOutput, setApiOutput] = useState("You will see output here");
   const [userInputSelect, setUserInputSelect] = useState("");
   const [checked, setChecked] = useState(false);
   const [apiOutputForRemix, setApiOutputForRemix] = useState("");

   const onUserChangedText = (event) => {
       console.log(event.target.value);
       setUserInput(event.target.value);
   };

   const onSelectOption = (event) => {
       console.log(event.target.value);
       setUserInput(event.target.value);
   };

   const handleChange = (event) => {
       setChecked(event.target.checked);
   };

   const OpenInRemixButton = ({ fileUrl, fileCode }) => {

       const fileCodeBase64 = nextBase64.encode(fileCode);

       return (
         <Link
           href={fileUrl}
           as={`https://remix.ethereum.org?code=${fileCodeBase64}`}
           prefetch={false}
           passHref
           legacyBehavior
         >
           <a data-url={fileUrl} target="_blank" className="btn btn-outline-success my-2 my-sm-0 float-right" data-content={fileCodeBase64}>Open in Remix</a>
         </Link>
       );
     };

   const callGenerateEndpoint = async () => {
       setApiOutput(`Please Wait ....`);

       console.log("Calling OpenAI...");
       const response = await fetch("/api/chatgpt", {
           method: "POST",
           headers: {
               "Content-Type": "application/json",
           },
           body: JSON.stringify({ userInput, checked }),
       });

       const data = await response.json();
       const { output } = data;
       console.log("OpenAI replied...", output.text);

       setApiOutputForRemix(output.text)

       const formattedText = output.text.replace(/\n/g, "<br>");
       const sanitizedOutput = sanitizeHtml(formattedText);

       setApiOutput(`${sanitizedOutput}`);
   };
Enter fullscreen mode Exit fullscreen mode

As funções onUserChangedText, onSelectOption e handleChange são usadas para coletar os campos de entrada. O OpenInRemixButton é usado para abrir o código no IDE Ethereum do Remix e, finalmente, a função callGenerateEndpoint chama a API.

const callGenerateEndpoint = async () => {
       setApiOutput(`Please Wait ....`);

       console.log("Calling OpenAI...");
       const response = await fetch("/api/chatgpt", {
           method: "POST",
           headers: {
               "Content-Type": "application/json",
           },
           body: JSON.stringify({ userInput, checked }),
       });

       const data = await response.json();
       const { output } = data;
       console.log("OpenAI replied...", output.text);

       setApiOutputForRemix(output.text)

       const formattedText = output.text.replace(/\n/g, "<br>");
       const sanitizedOutput = sanitizeHtml(formattedText);

       setApiOutput(`${sanitizedOutput}`);
   };
Enter fullscreen mode Exit fullscreen mode

A função callGenerateEndpoint chama a rota api/chatgpt que criei e que veremos mais tarde no artigo. A API retorna o output.text. Eu formatei isso para exibi-lo e também salvá-lo como dado bruto para passar no URL.

<select className="form-control form-control-lg" value={userInputSelect} onChange={onSelectOption}>
<option>Select Smart Contract</option>
<option value="NFT">NFT</option>
<option value="NFT with Royalty">NFT with Royalty</option>
<option value="PaymentSplitter">PaymentSplitter</option>
<option value="VestingWallet">VestingWallet</option>
<option value="Timelock">Timelock</option>
<option value="Pausable">Pausable</option>
<option value="ReentrancyGuard">ReentrancyGuard</option>
<option value="Uniswap">Uniswap</option>
<option value="Twitter">Twitter</option>
<option value="Hospital OPD">Hospital OPD</option>
<option value="Hospital Labtest">Hospital Labtest</option>
<option value="Cricket Records">Cricket Records</option>
<option value="Football World Cup">Football World Cup</option>
</select>
Enter fullscreen mode Exit fullscreen mode

Eu criei um dropdown e também uma área de texto. O usuário pode selecionar a opção ou escrever na área de texto.

{apiOutput && (
   <div className="output">

       <div className="output-content">
           <pre>
               <code>
                   <div style={{ padding:"10px" }}
                       dangerouslySetInnerHTML={{
                           __html: apiOutput,
                       }}
                   ></div>
               </code>
           </pre>
       </div>
   </div>
)}
Enter fullscreen mode Exit fullscreen mode

Nesse bloco do contrato inteligente, o HTML será gerado.

Agora vamos para a parte mais importante, que é a API.

Crie uma nova pasta api dentro da pasta pages e, dentro da pasta api, crie um novo arquivo chamado chatgpt.js.

import { Configuration, OpenAIApi } from 'openai';

const configuration = new Configuration({
 apiKey: process.env.OPENAI_API_KEY,
});

const openai = new OpenAIApi(configuration);

var basePromptPrefix = `Create a solidity smart contract for `;
var selectOpenZeppelin = "";
var versionUser = " use solidity version 0.8.17";
const chatGPT = async (req, res) => {

   if(req.body.checked == true) {
       selectOpenZeppelin = ` Using OpenZeppelin`;
   }

   const baseCompletion = await openai.createCompletion({
     model: 'text-davinci-003',
     prompt: `${basePromptPrefix}${req.body.userInput}${selectOpenZeppelin}${versionUser}`,
     temperature: 0.6,
     max_tokens: 4000,
   });

   const data_output = baseCompletion.data.choices.pop();

 // Enviar a saída do Prompt #2 para nossa interface do usuário, em vez do Prompt #1.
 res.status(200).json({ output: data_output });
};

export default chatGPT;
Enter fullscreen mode Exit fullscreen mode

A parte mais importante desta API é o "Prompt". As ferramentas de IA exigem prompts adequados do usuário para responder. Então, neste código, criamos este prompt como um prefixo.

var basePromptPrefix = `Create a solidity smart contract for `;
Enter fullscreen mode Exit fullscreen mode

Esse prompt é sempre adicionado à entrada do usuário para gerar o código.

const baseCompletion = await openai.createCompletion({
     model: 'text-davinci-003',
     prompt: `${basePromptPrefix}${req.body.userInput}${selectOpenZeppelin}${versionUser}`,
     temperature: 0.6,
     max_tokens: 4000,
   });
Enter fullscreen mode Exit fullscreen mode

Aqui, na linha número 3, o prompt tem o prefixo basePromptPrefix + entrada do usuário + selectOpenZeppelin (seleção do OpenZeppelin) + versão do Solidity. Digamos que o usuário tenha selecionado a opção "NFT" e também a opção "Use OpenZeppelin", então o valor final do prompt será:

Crie um contrato inteligente Solidity para um NFT, usando o OpenZeppelin, com a versão 0.8.17 do Solidity.

Existem diferentes modelos. Eu usei, o text-davinci-003, que é o mais recente e preciso.

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*fBl53FyroqegsyC-41eWEA.png

A temperatura controla a aleatoriedade da saída. Valores mais altos significam que o modelo assumirá mais riscos.

O parâmetro max_tokens define um limite máximo para o número de tokens que a API pode usar.

Essa é a explicação do código para que qualquer um possa contribuir com ele. Deixe-me saber nos comentários sobre o que você achou do código.

Você pode acessar o código aqui e também pode contribuir com ele.

https://github.com/ismailbangee/solidity_smart_contract_using_chatgpt

Saiba mais sobre o Solidity e o ChatGPT.

https://ismailsaleem.gumroad.com/

Artigo publicado por Ismail. Traduzido por Paulinho Giovannini.

Oldest comments (0)