主页 > imtoken钱包 官方网站 > 构建一个简单的 Ethereum+IPFS+React.js DApp

构建一个简单的 Ethereum+IPFS+React.js DApp

imtoken钱包 官方网站 2023-01-26 06:48:12

我们为什么要建造这个? 在以太坊区块链上存储大量数据非常昂贵。 根据以太坊黄皮书,大约是20,0000 gas,256bit/8 bytes (1 word)。 基于 02/28/2018,gas 价格为 4gwei/gas。 请参阅:当前价格。

每笔交易 8 字节 20,000gas*4gwei/gas=80,000gwei。

8,000 字节 80,000gwei*1000bytes/8=10,000,000gwei/kB=0.01`ether。

0.01 Ether/kB*1000kB=10 Ether 存储 1Mb,价格为 $860/Ether=$8600.00! 在以太坊区块链上存储一个 1GB 的文件需要 8,600,000.00 美元!

存储以太坊的 38 页 PDF 黄皮书 (520Kb) = 4472 美元。 请参阅:进行转换计算。

如果我们只能在区块链上存储几Kb的数据,那么我们仍然需要依赖中心化服务器来存储数据。 值得庆幸的是,可以使用一种名为 InterPlanetary 文件系统 IPFS 的去中心化网络存储数据的解决方案。 请参阅:了解更多信息。 在 IPFS 中查找文件时,您是在要求网络查找将内容存储在唯一哈希后面的节点。 来自 IPFS 自己的网站:

“IPFS 和区块链是绝配!您可以使用 IPFS 处理大量数据,并将不可变的永久 IPFS 链接放入区块链交易中。这种时间戳和保护您的内容,而无需将数据放在链本身上。”

我们建造什么?

一个简单的 DApp,用于将文档上传到 IPFS,然后将 IPFS 哈希存储在以太坊区块链上。 一旦 IPFS 哈希号被发送到以太坊区块链,用户将收到交易收据。 我们将使用 Create-React-App 框架来构建前端。 任何在浏览器中安装了 MetaMask 的用户都可以使用此 Dapp。

这是我们完成后 DApp 的样子:

sitehqz.com 以太坊和以太坊贸易的关系_以太坊dapp_以太坊dapp开发入门

图片

如何构建它:

注意:如果您只想要代码,请参阅我的 github。

在我们开始之前,这些是我所做的假设:

npm i create-react-app
npm install react-bootstrap
npm install fs-extra
npm install ipfs-api
npm install web3@^1.0.0-beta.26

进入 eth-ipfs 目录,键入 npm start,Create-React-App 应该会自动呈现在 :3000/ 上。

注意:如果到目前为止你还没有使用过 create-react-app,你可能需要先全局安装它

在 Rinkeby 测试网上使用 Remix 部署以下 Solidity 代码。 看。 如果您还没有从 Rinkeby 水龙头获得一些免费的测试以太币,您将需要一些 Rinkeby 测试以太币。 .

pragma solidity ^0.4.17;
contract Contract {
 string ipfsHash;
 
 function sendHash(string x) public {
   ipfsHash = x;
 }
 function getHash() public view returns (string x) {
   return ipfsHash;
 }
}

保存部署的合约地址和应用程序二进制接口(ABI)。 要获取合约的 ABI,请转到您在 Remix 中的合约地址:

单击 Compile 选项卡,然后单击灰色的 Details 按钮。

sitehqz.com 以太坊和以太坊贸易的关系_以太坊dapp_以太坊dapp开发入门

图片

这将打开“详细信息”窗口。 复制“ABI”,这是一个 JSON 文件。

sitehqz.com 以太坊和以太坊贸易的关系_以太坊dapp_以太坊dapp开发入门

图片

例如,我个人更喜欢将 ABI JSON 放入格式化程序中,并在我的 javascript 代码中使用它之前检查它是否有效。 保存合约地址和 ABI 以备后用。

在我们的“eth-ipfs/src”目录中,创建以下文件:web3.js、ipfs.js 和 storehash.js。 我们的大部分代码都在 App.js 中。

sitehqz.com 以太坊和以太坊贸易的关系_以太坊dapp_以太坊dapp开发入门

图片

web3.js

我们想使用 web3.js 的 1.0 版,因为与 0.20 版不同,1.0 版允许我们在 javascript 中使用 async 和 await 而不是 promises。 目前,MetaMask 的默认 web3.js 提供程序是 0.20 版。 因此以太坊dapp,让我们确保覆盖 Metamask 的 web3 版本 0.20 的默认版本并使用我们的 1.0. 这是代码:

//为我们的1.0版本覆盖metamask v0.2。 
//1.0让我们使用async和await而不是promises 
import Web3 from ‘web3’;
const web3 = new Web3(window.web3.currentProvider);
export default web3;

存储哈希.js

为了让 web3.js 能够访问我们之前部署到以太坊的 Rinkeby 测试网的合约,您需要以下内容:1) 合约地址和 2) 合约中的 ABI。 请务必从 /src 目录导入 web3.js 文件。 这是代码:

import web3 from './web3';
//access our local copy to contract deployed on rinkeby testnet
//use your own contract address
const address = '0xb84b12e953f5bcf01b05f926728e855f2d4a67a9';
//use the ABI from your contract
const abi = [
  {
    "constant": true,
    "inputs": [],
    "name": "getHash",
    "outputs": [
      {
        "name": "x",
        "type": "string"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "x",
        "type": "string"
      }
    ],
    "name": "sendHash",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  }
]
export default new web3.eth.Contract(abi, address);

ipfs.js

在本教程中,我们将运行一个 ipfs.infura.io 节点来连接到 IPFS,而不是在我们自己的计算机上运行 IPFS 守护进程。 在代码注释中,如果您将 IPFS 安装为全局依赖项,您还可以选择运行自己的 IPFS 守护进程。 有关使用其节点的更多信息以太坊dapp,请参阅 。 这是代码:

//using the infura.io node, otherwise ipfs requires you to run a //daemon on your own computer/server.
const IPFS = require(‘ipfs-api’);
const ipfs = new IPFS({ host: ‘ipfs.infura.io’, port: 5001, protocol: ‘https’ });
//run with local daemon
// const ipfsApi = require(‘ipfs-api’);
// const ipfs = new ipfsApi(‘localhost’, ‘5001’, {protocol:‘http’});
export default ipfs;

应用程序.js

下面是 App.js 中的操作顺序:

sitehqz.com 以太坊和以太坊贸易的关系_以太坊dapp_以太坊dapp开发入门

图片

sitehqz.com 以太坊和以太坊贸易的关系_以太坊dapp_以太坊dapp开发入门

图片

sitehqz.com 以太坊和以太坊贸易的关系_以太坊dapp_以太坊dapp开发入门

图片

最后,这是 App.js 代码:

import React, { Component } from ‘react’;
//import logo from ‘./logo.svg’;
import ‘./App.css’;
import web3 from ‘./web3’;
import ipfs from ‘./ipfs’;
import storehash from ‘./storehash’;
class App extends Component {
 
    state = {
      ipfsHash:null,
      buffer:'',
      ethAddress:'',
      blockNumber:'',
      transactionHash:'',
      gasUsed:'',
      txReceipt: ''   
    };
captureFile =(event) => {
        event.stopPropagation()
        event.preventDefault()
        const file = event.target.files[0]
        let reader = new window.FileReader()
        reader.readAsArrayBuffer(file)
        reader.onloadend = () => this.convertToBuffer(reader)    
      };
 convertToBuffer = async(reader) => {
      //file is converted to a buffer for upload to IPFS
        const buffer = await Buffer.from(reader.result);
      //set this buffer -using es6 syntax
        this.setState({buffer});
    };
onClick = async () => {
try{
        this.setState({blockNumber:"waiting.."});
        this.setState({gasUsed:"waiting..."});
//get Transaction Receipt in console on click
//See: https://web3js.readthedocs.io/en/1.0/web3-eth.html#gettransactionreceipt
await web3.eth.getTransactionReceipt(this.state.transactionHash, (err, txReceipt)=>{
          console.log(err,txReceipt);
          this.setState({txReceipt});
        }); //await for getTransactionReceipt
await this.setState({blockNumber: this.state.txReceipt.blockNumber});
        await this.setState({gasUsed: this.state.txReceipt.gasUsed});    
      } //try
    catch(error){
        console.log(error);
      } //catch
  } //onClick
onSubmit = async (event) => {
      event.preventDefault();
     //bring in user's metamask account address
      const accounts = await web3.eth.getAccounts();
     
      console.log('Sending from Metamask account: ' + accounts[0]);
    //obtain contract address from storehash.js
      const ethAddress= await storehash.options.address;
      this.setState({ethAddress});
    //save document to IPFS,return its hash#, and set hash# to state
    //https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#add 
      await ipfs.add(this.state.buffer, (err, ipfsHash) => {
        console.log(err,ipfsHash);
        //setState by setting ipfsHash to ipfsHash[0].hash 
        this.setState({ ipfsHash:ipfsHash[0].hash });
   // call Ethereum contract method "sendHash" and .send IPFS hash to etheruem contract 
  //return the transaction hash from the ethereum contract
 //see, this https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#methods-mymethod-send
        
        storehash.methods.sendHash(this.state.ipfsHash).send({
          from: accounts[0] 
        }, (error, transactionHash) => {
          console.log(transactionHash);
          this.setState({transactionHash});
        }); //storehash 
      }) //await ipfs.add 
    }; //onSubmit
render() {
      
      return (
        

Ethereum and IPFS with Create React App


Choose file to send to IPFS


Tx Receipt Category Values
IPFS Hash # stored on Eth Contract {this.state.ipfsHash}
Ethereum Contract Address {this.state.ethAddress}
Tx Hash # {this.state.transactionHash}
Block Number # {this.state.blockNumber}
Gas Used {this.state.gasUsed}
); } //render } //App export default App;

我在 src/App.css 中添加了一些 CSS 以使其看起来更简单一些:

/*some css I added*/
input[type=”file”] {
 display: inline-block;
}
.table {
 max-width: 90%;
 margin: 10px;
}
.table th {
 text-align: center;
}
/*end of my css*/

并向 src/index.js 添加一些导入:

/*https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-a-stylesheet*/
import ‘bootstrap/dist/css/bootstrap.css’;
import ‘bootstrap/dist/css/bootstrap-theme.css’;

现在是对的! 你的 DApp 应该是完整的。 因此,您需要做的就是选择一个文件、发送它并获得交易收据。 如果您通过 localhost:3000 连接到您的 IPFS 节点,那么您应该能够在其中一个 IPFS 网关上看到您的文件。 + 你的 IPFS 哈希。

例如:

关于 IPFS 的一个注意事项是,除非您的文件被另一个节点拾取或您固定它,否则 IPFS 最终将垃圾收集您的文件。 他们的网站对此有很多话要说。

================================================ == =====================

分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程: