Dapp. Vue.js + ethers.js

Introduction

In this article, I will try to go through all the points of creating a full-fledged Decentralized application on the Ethereum network as briefly and informatively as possible using the JavaScript framework - Vue for creating a web application and the ethers.js library for communicating with a smart contract. We will also consider some points about how to install and connect a wallet, how to deploy a contract to the network using truffle , etc.





Some points may seem very obvious and not difficult, so you can skip them.





What you need to know before you start:





  • Good knowledge of js, in particular Vue.js





  • Understanding the principles of Blockchain (smart contracts)





  • Basic knowledge of the language pr. Solidity





Installing a wallet

We will use the Metamask wallet to confirm transactions, communicate with the contract and check some parameters of these transactions . He's pretty good and I almost never had any problems with his work. I will not show the installation process, everything is obvious there.





When the extension is installed, you will be asked to create a wallet or import if available. After creating an account, a mnemonic phrase will be generated for you. NEVER disclose it to ANYONE. And voila - we have a ready-made wallet address)





Since we will be developing a sample application, it is better to use a test network, so as not to spend real money on deploying a contract and executing transactions. Ropsten is perfect for our task.





, .





-. . 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>
      
      



You can check the code of the Marketplace.vue component in my repository so as not to clutter up the article with unnecessary code.





After starting the project, you should see a wallet connection window like this:





And after that, when we click on the Get details button, we will receive the data that we entered when deploying the contract.





And this is how the output looks like if you make a transaction:





Conclusion

This is my first article. I would be glad to have some questions and even criticism, since I myself am not a pro in all this yet.





Link to the contract where you can check the transactions.








All Articles