Dapp. Vue.js + ethers.js

Introducción

En este artículo, intentaré analizar todos los puntos de la creación de una aplicación descentralizada completa en la red Ethereum de la forma más breve e informativa posible utilizando el marco de JavaScript: Vue para crear una aplicación web y la biblioteca ethers.js para comunicarse. con un contrato inteligente. También consideraremos algunos puntos sobre cómo instalar y conectar una billetera, cómo implementar un contrato a la red usando trufa , etc.





Algunos puntos pueden parecer muy obvios y no difíciles, por lo que puede omitirlos.





Lo que necesita saber antes de comenzar:





  • Buen conocimiento de js, en particular Vue.js





  • Comprender los principios de Blockchain (contratos inteligentes)





  • Conocimientos básicos del idioma pr. Solidez





Instalar una billetera

Usaremos la billetera Metamask para confirmar transacciones, comunicarnos con el contrato y verificar algunos parámetros de estas transacciones . Es bastante bueno y casi nunca tuve problemas con su trabajo. No mostraré el proceso de instalación, todo es obvio allí.





Cuando se instale la extensión, se le pedirá que cree una billetera o que importe si está disponible. Después de crear una cuenta, se generará una frase mnemotécnica para usted. NUNCA se lo revele a NADIE. Y listo, tenemos una dirección de billetera preparada)





- , . Ropsten.





, .





-. . README ( ). - -. - , . , . IDE Remix.





Truffle.js. node.js. npm ( ): npm install -g truffle



npm install -g @truffle/hdwallet-provider



, Truffle truffle init.







truffle-config.js. . Infura Ethereum -> Create New Project (UI) dApps - Ethereum. Infura , Ethereum , . -> Settings -> Endpoints Ropsten .





, , truffle-config.js. -, .secret . dotenv, , truffle. -, :





const HDWalletProvider = require('@truffle/hdwallet-provider');

const fs = require('fs');
const mnemonic = fs.readFileSync(".secret").toString().trim();
      
      







ropsten: {
    provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
    network_id: 3,       // Ropsten's id
    gas: 5500000,        // Ropsten has a lower block limit than mainnet
    confirmations: 2,    // # of confs to wait between deployments. (default: 0)
    timeoutBlocks: 200,  // # of blocks before a deployment times out  (minimum/default: 50)
    skipDryRun: true     // Skip dry run before migrations? (default: false for public nets )
    },
      
      



HDWalletProvider



.





-, .sol contracts, . migrations js :





const SimpleMarketplace = artifacts.require("SimpleMarketplace");

module.exports = function (deployer) {
    deployer.deploy(SimpleMarketplace, 'description', 1000);
};
      
      



, migrations 1_initial_migrations.js, 2.





- deploy : description price . , , SimpleMarketplace.







truffle compile



. : , @truffle/hdwallet-provider,(Error: Cannot find module '@truffle/hdwallet-provider')



, npm install @truffle/hdwallet-provider



. -g



. Compiled successfully using...



.





truffle migrate --network ropsten --reset



. ropsten



- , truffle-config.js, --reset , ( ).: : var e = new Error('ETIMEDOUT')



, truffle-config.js HDWalletProvider



, Infura - wss://ropsten.infura.io/ws/v3/YOUR-PROJECT-ID.





Etherscan , - , blockchain .





Vue.js

, , - . vue/cli , Vue.js. . .





. assets ( Vue.js, ), HelloWorld.vue components App.vue ( HelloWorld.vue, components <div id="app">



).





<template>
  <div id="app">

  </div>
</template>

<script>

export default {
  name: 'App',
  components: {
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
      
      



npm run serve



, , , .





- UI -. components , , Marketplace.vue. core core.js , , ./build/contracts JSON - ( ). - - , . , . . - npm install ethers



.





core.js , JSON - :





const { ethers } = require('ethers')
const ContractArtifact = require('./SimpleMarketplace.json')
const CONTRACT_ADDRESS = ContractArtifact.networks['3'].address
      
      



ABI, , ( . public , public view returns ethers ):





const ABI = [
    'function InstanceOwner () public view returns(address)',
    'function Description () public view returns(string)',
    'function AskingPrice () public view returns(int)',

    'function InstanceBuyer () public view returns(address)',
    'function OfferPrice () public view returns(int)',

    'function MakeOffer(int offerPrice) public',
    'function Reject() public',
    'function AcceptOffer() public'
]
      
      



:





let provider = new ethers.providers.Web3Provider(window.ethereum)
//       Blockcain
let readOnlyContract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider)
//    view  
let signer = provider.getSigner()
//   
let contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer)
let contractSigner = contract.connect(signer)
//   
      
      



Vue :





export default {
    async getDescription() {
        const description = await readOnlyContract.Description()
        return {description: description}
    }
}
      
      



, . - :





const { ethers } = require('ethers')
const ContractArtifact = require('./SimpleMarketplace.json')
const CONTRACT_ADDRESS = ContractArtifact.networks['3'].address

const ABI = [
    'function InstanceOwner () public view returns(address)',
    'function Description () public view returns(string)',
    'function AskingPrice () public view returns(int)',

    'function InstanceBuyer () public view returns(address)',
    'function OfferPrice () public view returns(int)',

    'function MakeOffer(int offerPrice) public',
    'function Reject() public',
    'function AcceptOffer() public'
]

let provider = new ethers.providers.Web3Provider(window.ethereum)
let readOnlyContract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider)
let signer = provider.getSigner()
let contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer)
let contractSigner = contract.connect(signer)

export default {
    async getInstanceOwner() {
        const instanceOwner = await readOnlyContract.InstanceOwner()
        return {instanceOwner: instanceOwner}
    },
    async getDescription() {
        const description = await readOnlyContract.Description()
        return {description: description}
    },
    async getAskingPrice() {
        const askingPrice = await readOnlyContract.AskingPrice()
        return {askingPrice: askingPrice}
    },
    async getInstanceBuyer() {
        const instanceBuyer = await readOnlyContract.InstanceBuyer()
        return {instanceBuyer: instanceBuyer}
    },
    async getOfferPrice() {
        const offerPrice = await readOnlyContract.OfferPrice()
        return {offerPrice: offerPrice}
    },

    async makeOffer(offerPrice) {
        const txResponse = await contractSigner.MakeOffer(offerPrice, {gasLimit: 300000})
        const txReceipt = await txResponse.wait()
        return {transaction: txReceipt.transactionHash}
    },
    async reject() {
        const txResponse = await contractSigner.Reject({gasLimit: 300000})
        const txReceipt = await txResponse.wait()
        return {transaction: txReceipt.transactionHash}
    },
    async acceptOffer() {
        const txResponse = await contractSigner.AcceptOffer({gasLimit: 300000})
        const txReceipt = await txResponse.wait()
        return {transaction: txReceipt.transactionHash}
    }
}
      
      



App.vue mounted $root core.js:





const core = require('./core/core')
/*
-  
*/
mounted() {
    window.ethereum.request({ method: 'eth_requestAccounts' })
    this.$root.core = core.default
  }
      
      



, data details methods :





data() {
    return {
      instanceOwner: '',
      description: '',
      askingPrice: '',

      instanceBuyer: '',
      offerPrice: ''
    }
  },
  methods: {
    async details() {
      this.instanceOwner = (await this.$root.core.getInstanceOwner()).instanceOwner
      this.description = (await this.$root.core.getDescription()).description
      this.askingPrice = (await this.$root.core.getAskingPrice()).askingPrice
      this.instanceBuyer = (await this.$root.core.getInstanceBuyer()).instanceBuyer
      this.offerPrice = (await this.$root.core.getOfferPrice()).offerPrice
    }
  },
      
      



:





<button v-on:click="details">Get details</button>
<h3>Instance owner: {{ instanceOwner }}</h3>
<h3>Description: {{ description }}</h3>
<h3>Asking price: {{ askingPrice }}</h3>
<h3>Instance buyer: {{ instanceBuyer }}</h3>
<h3>Offer price: {{ offerPrice }}</h3>
      
      



Puede verificar el código del componente Marketplace.vue en mi repositorio para no saturar el artículo con código innecesario.





Después de comenzar el proyecto, debería ver una ventana de conexión de billetera como esta:





Y después de eso, cuando hagamos clic en el botón Obtener detalles , recibiremos los datos que ingresamos al implementar el contrato.





Y así es como se ve la salida si realiza una transacción:





Conclusión

Este es mi primer artículo. Me encantaría tener algunas preguntas e incluso críticas, ya que yo mismo todavía no soy un profesional en todo esto.





Enlace al contrato donde puede consultar las transacciones.








All Articles