WEB3DEV Español

Cover image for Generador de contratos inteligentes Solidity impulsado por ChatGPT-3 de OpenAI
Juliana Cabeza
Juliana Cabeza

Posted on

Generador de contratos inteligentes Solidity impulsado por ChatGPT-3 de OpenAI

Creé un generador de contratos inteligentes de Solidity utilizando las API de OpenAI. En este artículo explicaré cómo lo hice.

Puedes probar el generador aquí.

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

Image description

Primero, creé una aplicación Next.js.

yarn create next-app
Enter fullscreen mode Exit fullscreen mode

Image description

El comando anterior realizó algunas preguntas y nombré el directorio de mi proyecto como my_test_project. Ahora, dirígete a la aplicación de Next.js que creaste, en este caso, está dentro de la carpeta my_test_project.

cd my_test_project
Enter fullscreen mode Exit fullscreen mode

Después de ingresar a la carpeta, ejecute la aplicación.

yarn dev
Enter fullscreen mode Exit fullscreen mode

Verá la pantalla de bienvenida de la aplicación Next.js.

Image description

Abra una nueva terminal o presione CTRL + C para finalizar el comando run dev. Después de eso, agregue los paquetes necesarios. Desde que usé Bootstrap, también agregué el paquete Bootstrap.

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

Agregue Bootstrap en el archivo _app.js.

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

Cree un archivo .env y pegue la clave API de OpenAI.

OPENAI_API_KEY=
Enter fullscreen mode Exit fullscreen mode

Obtendrá esta clave API desde aquí:

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

Elimine todo el código del archivo index.js y pegue 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

Déjame explicarte el código anterior:

Primero, importé todos los paquetes necesarios.

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

La importación sanitize-html se usa para limpiar el HTML, next/link se usa para enlaces y next-base64 se usa para codificar el código generado y pasarlo a la 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

Las funciones onUserChangedText, onSelectOption y handleChangese utilizan para recopilar los campos de entrada. El OpenInRemixButton se utiliza para abrir el código en el IDE Ethereum de Remix y, finalmente, la función callGenerateEndpoint llama a la 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

La función callGenerateEndpoint llama a la ruta api/chatgpt que he creado y que veremos más adelante en el artículo. La API devuelve output.text. He formateado esto para mostrarlo y también para guardarlo como datos sin procesar para pasar en la 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

He creado un dropdown y también un área de texto. El usuario puede seleccionar la opción o escribir en el á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

En este bloque del contrato inteligente, se generará el HTML.

Ahora pasemos a la parte más importante, que es la API.

Crea una nueva carpeta llamada api dentro de la carpeta pages y, dentro de la carpeta api, crea un nuevo archivo llamado 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 la salida del Prompt #2 a nuestra interfaz de usuario, en lugar del Prompt #1.
 res.status(200).json({ output: data_output });
};

export default chatGPT;

Enter fullscreen mode Exit fullscreen mode

La parte más importante de esta API es el "Prompt" (indicación). Las herramientas de IA requieren prompts adecuados por parte del usuario para responder. Por lo tanto, en este código, creamos este prompt como un prefijo.

var basePromptPrefix = `Create a solidity smart contract for `;

Enter fullscreen mode Exit fullscreen mode

Este prompt siempre se agrega a la entrada del usuario para generar el 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

Aquí, en la línea número 3, el prompt tiene el prefijo basePromptPrefix + entrada del usuario + selectOpenZeppelin (selección de OpenZeppelin) + versión de Solidity. Supongamos que el usuario ha seleccionado la opción "NFT" y también la opción "Usar OpenZeppelin", entonces el valor final del prompt será:

_Cree un contrato inteligente de Solidity para un NFT, utilizando OpenZeppelin, con Solidity versión 0.8.17.
_

Hay diferentes modelos. Utilicé text-davinci-003, que es el último y más preciso.

Image description

La temperatura controla la aleatoriedad de la salida. Los valores más altos significan que el modelo asumirá más riesgos.

El parámetro max_tokens establece un límite máximo para la cantidad de tokens que la API puede utilizar.

Esta es la explicación del código para que cualquiera pueda contribuir. Déjame saber en los comentarios qué piensas del código.
Puedes acceder al código aquí y también puedes contribuir a él.

https://github.com/ismailbangee/solidity_smart_contract_using_chatgpt

Obtenga más información sobre Solidity y ChatGPT.

https://ismailsaleem.gumroad.com/

Este artículo fue escrito por Ismail y traducido por Juliana Cabeza. Puedes leer el original aquí

Discussion (0)