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/
Primero, creé una aplicación Next.js.
yarn create next-app
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
Después de ingresar a la carpeta, ejecute la aplicación.
yarn dev
Verá la pantalla de bienvenida de la aplicación Next.js.
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
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} />
}
Cree un archivo .env
y pegue la clave API de OpenAI.
OPENAI_API_KEY=
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>
</>
);
}
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';
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}`);
};
Las funciones onUserChangedText
, onSelectOption
y handleChange
se 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}`);
};
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>
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>
)}
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;
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 `;
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,
});
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.
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)