WEB3DEV

Cover image for Upload de arquivos compactados para um contrato inteligente Solidity
Fatima Lima
Fatima Lima

Posted on

Upload de arquivos compactados para um contrato inteligente Solidity

Image description

A Inovação está chamando

Usando a nova e incrível GPT Chat de AI aberta, uma cabeça cheia de ideias e o conhecimento para unir tudo isso, consegui produzir este guia em tempo recorde.

Se você ainda não conferiu isto, divirta-se aqui.

Crie um Aplicativo React

Este guia assume que você tenha uma configuração de nó. Se não tiver, pode instalá-lo daqui.

Crie um novo diretório para nosso aplicativo frontend em sua área de trabalho, abra uma nova janela de terminal e faça um cd (change directory ou mudar diretório) para esse diretório.

Uma vez neste novo diretório, digite o seguinte no terminal para criar um novo aplicativo react:

npx create-react-app FileCompressor
Enter fullscreen mode Exit fullscreen mode

Uma vez instalada, sua nova estrutura de diretório será construída.

Adicione um Formulário de Carregamento Frontend

Vá para src/app.js e navegue até a função App para inserir esta lógica:

import React from "react";

function App(props) {
 const handleChange = e => {
   const fileReader = new FileReader();
   fileReader.readAsText(e.target.files[0], "UTF-8");
   fileReader.onload = e => {
     if(e.target !== null) {
       console.log("e.target.result", e.target.result);
     }
   };
 };

 return (
   <>
     <h1>Upload file</h1>

     <input type="file" onChange={handleChange} />
   </>
 );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Isto criará uma nova função chamada handleChange que é chamada toda vez que um arquivo é carregado no frontend.

Base64

Nossos arquivos não estão no momento em nenhum formato que a cadeia possa entender. Precisamos, então, transformá-los em um string de base64 que possa ser armazenado on chain.

Volte para seu arquivo app.js onde adicionaremos a lógica base64 ao método handleChange:

import React from "react";

function App(props) {
 const handleChange = e => {
   const fileReader = new FileReader();
   fileReader.readAsDataURL(file)
   fileReader.onload = () => {
     console.log(fileReader.result);
   }
   fileReader.onerror = (error) => {
     console.log(error);
   }
 };

 return (
   <>
     <h1>Upload file</h1>

     <input type="file" onChange={handleChange} />
   </>
 );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Até agora temos um formulário de upload de arquivo que pega o input, converte o arquivo para um string de base64 e console.logs (imprime no console como uma mensagem de log) a saída. Assim, isso é bom, mas é muito mais agradável usar a compactação para reduzir a contagem de bytes.

Compactação de String LZ

Primeiro instale a biblioteca para compactação lz:

npm i lzma-js
Enter fullscreen mode Exit fullscreen mode

Uma vez instalado, modifique o app.js para assemelhar-se ao exemplo abaixo, isto então registrará o string de base64 compactada no terminal:

import React from "react";
import LZMA from "lzma-js";

function App(props) {
 const handleChange = e => {
   const fileReader = new FileReader();
   fileReader.readAsDataURL(file)
   fileReader.onload = () => {
     // Converter o string codificado de base64 em um Uint8Array
     const uint8Array = Uint8Array.from(atob(fileReader.result), c => c.charCodeAt(0));


     // Compactar o Uint8Array usando LZMA-JS
     LZMA.compress(uint8Array, 9, (result, error) => {
       if (error) {
         // Tratar o erro
       } else {
         // Os dados compactados são um Uint8Array, então você precisa convertê-los para um string codificado de base64
         const compressedBase64String = btoa(String.fromCharCode(...result));
         console.log(compressedBase64String); // Outputs the compressed base64 encoded string
       }
     });
   }
   fileReader.onerror = (error) => {
     console.log(error);
   }
 };

 return (
   <>
     <h1>Upload file</h1>

     <input type="file" onChange={handleChange} />
   </>
 );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

O Contrato Inteligente

Este é um contrato inteligente simples que armazena um conjunto de strings de 32 bytes, neste caso nossos 32 bytes são arquivos.

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

contract FileStorage {
 mapping (address => bytes32[]) userFiles;

 function addFile(string calldata _file) public {
   userFiles[msg.sender].push(_file);
 }

 function deleteFile(uint arrayIndex) public {
   delete userFiles[msg.sender][arrayIndex];
 }

 function getFiles() public view returns (bytes32[] memory) {
   return userFiles[msg.sender];
 }
}
Enter fullscreen mode Exit fullscreen mode

Isto é o mínimo necessário. Para os casos de produção você provavelmente precisaria muito mais, mas o contrato acima será suficiente para o propósito deste tutorial. Uma vez implantado, você estará pronto para conectar seu aplicativo react ao seu banco de dados de arquivos descentralizado.

Conectando o Dapp

Primeiramente, pegue o abi (interface binária de aplicação) para seu contrato de implantação e armazene em um novo arquivo chamado abi.json.

[
{
 "inputs": [
  {
   "internalType": "string",
   "name": "_file",
   "type": "string"
  }
 ],
 "name": "addFile",
 "outputs": [],
 "stateMutability": "nonpayable",
 "type": "function"
},
{
 "inputs": [
  {
   "internalType": "uint256",
   "name": "arrayIndex",
   "type": "uint256"
  }
 ],
 "name": "deleteFile",
 "outputs": [],
 "stateMutability": "nonpayable",
 "type": "function"
},
{
 "inputs": [],
 "name": "getFiles",
 "outputs": [
  {
   "internalType": "string[]",
   "name": "",
   "type": "string[]"
  }
 ],
 "stateMutability": "view",
 "type": "function"
}
]
Enter fullscreen mode Exit fullscreen mode

Se você voltar ao arquivo app.js, a etapa final será substituir nosso log de console do string de arquivos por uma chamada de contrato inteligente. Primeiro puxamos o contrato abi, preenchemos o endereço do contrato, conectamos à MetaMask através do frontend e depois permitimos que o arquivo do usuário seja armazenado após a conexão ter sido estabelecida.

import React from "react";
import LZMA from "lzma-js";

function App(props) {
 const abi = require('./abi.json');
 const handleChange = e => {
   const fileReader = new FileReader();
   const CONTRACT_ADDRESS = "0xof";
   fileReader.readAsDataURL(file)
   fileReader.onload = () => {
     // Converter o string de base64 codificado em um Uint8Array
     const uint8Array = Uint8Array.from(atob(fileReader.result), c => c.charCodeAt(0));


     // Compactar o  Uint8Array using LZMA-JS
     LZMA.compress(uint8Array, 9, (result, error) => {
       if (error) {
         console.log(error);
       } else {
           // Os dados compactados são Uint8Array, então você precisa convertê-los para um string de base64 codificado
           const compressedBase64String = btoa(String.fromCharCode(...result));
           const { ethereum } = window;
           const provider = new ethers.providers.Web3Provider(ethereum);
           const signer = provider.getSigner()
           const connectedContract = new ethers.Contract(CONTRACT_ADDRESS, abi, signer);

           let tx = async() => await connectedContract.addFile(compressedBase64String);

           console.log(tx); // Emitir o resultado da chamada do contrato inteligente
       }
     });
   }
   fileReader.onerror = (error) => {
     console.log(error);
   }
 };

 return (
   <>
     <h1>Upload file</h1>

     <input type="file" onChange={handleChange} />
   </>
 );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

É tudo o que você precisa para compactar documentos de palavras e armazená-los on chain. Não apenas documentos de palavras, mas praticamente qualquer tipo de arquivo que possa ser compreendido pelo carregador baseado no navegador.

Para recuperar seus arquivos e descompactar você precisará criar um novo método para consultar a função getFiles e loop dos contratos inteligentes através de cada um deles. Dentro de cada iteração você simplesmente chamaria o método abaixo e armazenaria os dados descompactados de qualquer forma que se adequasse ao seu caso de uso:

LZMA.decompress(properties, inStream, outStream, outSize);
Enter fullscreen mode Exit fullscreen mode

Há tantas possibilidades para esta abordagem, mas como sempre, este é apenas um guia para levá-lo a uma base sólida e compreensão, mas não está, de forma alguma, pronto para a produção.

Crie !

Esse artigo foi escrito por Robert McMenemy e traduzido por Fátima Lima. O original pode ser lido aqui.

Latest comments (0)