nodejs與以太坊進行交互

  • nodejs與以太坊交互的庫用的是ethers.js,它比web3.js容易使用。
  • ethers.js自帶了鏈接主網(wǎng)和測試網(wǎng)的功能,不用在本地啟用geth客戶端。(文檔地址)
  • 合約編譯用到的工具庫都是solc-js(文檔地址)
  • 本項目所有代碼

安裝ethers.js

  • 只要運行一條簡單的命令
$ npm install  ethers

創(chuàng)建錢包

  • random方法會隨機生成錢包,并且錢包中包含了助記詞,這里把助記詞提出來,以便每次都生成同樣都錢包。
// demo1.js
const ethers = require('ethers');
const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);

// const randomWallet = ethers.Wallet.createRandom();
// console.log( randomWallet.signingKey.mnemonic);

// 助記詞由以上隨機函數(shù)生成,為了保持賬號統(tǒng)一,這里記錄了兩組助記詞
const mnemonic1 = 'utility opinion husband upset finger side round exhaust arm allow pilot hospital';
const mnemonic2 = 'method expand rule tool impact wedding just body slogan offer rate pass';

// 根據(jù)兩組助記詞生成兩個錢包對象
const _wallet1 = ethers.Wallet.fromMnemonic(mnemonic1);

// address1 0xBe255696870b84C69F6e2b902177Cf2a2cB57B58
// privateKey1 0x056ef7c6a165f877a5aedb3cfe24b2bbcdd6c680d12df9a82092705fc03ce37f

const _wallet2 = ethers.Wallet.fromMnemonic(mnemonic2);
// address2 0xbe79D5B66A5D44607F91E312ec5E35b8c92db5bf
// privateKey2 0x8544e404dea9123dd6fe1b6b35702a738284e055223c0e2afd41ec7694a2bfda

給賬號充值

  • 現(xiàn)在給賬號充值一點ETH測試轉(zhuǎn)賬,
  • 這里選擇測試網(wǎng)rinkeby
  • 水龍頭地址
  • 發(fā)一條帶有自己地址推文,粘貼在水龍頭頁面的輸入框即可,如下圖所示。
  • 這里都鏈接只支持Facebook、Twitter、google puls,請科學(xué)上網(wǎng)。
屏幕快照 2018-12-05 下午10.45.29.png

錢包對象鏈接網(wǎng)絡(luò)

  • 設(shè)定一個provider,參數(shù)填入rinkeby即可,如果鏈接正式網(wǎng),就不需要填寫參數(shù)。
let provider = ethers.getDefaultProvider('rinkeby');
const wallet1 = _wallet1.connect(provider);
const wallet2 = _wallet2.connect(provider);

查詢一下賬號余額

  • 一切正常的話可以顯示余額
const getBalance = async () => {
  // 返回的余額單位是ether,要轉(zhuǎn)換成ETH
  const _balance1 =  await wallet1.getBalance();
  // 第一次獲取是18.75, 總之這里是非零就說明水龍頭轉(zhuǎn)賬成功,并且連接測試網(wǎng)成功了
  const balance1 =  ethers.utils.formatEther(_balance1);

  const _balance2 =  await wallet2.getBalance();
  const balance2 =  ethers.utils.formatEther(_balance2);
  console.log(balance1, balance2);
};

getBalance();

轉(zhuǎn)賬測試

  • 填寫接收者的地址,發(fā)出對象就可以了,非常簡單,
  • 有一點注意都是,金額需要用 parseEther進行轉(zhuǎn)行,因為在區(qū)塊鏈網(wǎng)絡(luò)中都用ether這個單位。
  • 一切順利都話,會打印出交易結(jié)果。
const transfer = async () => {
  let tx = {
    // 這里寫一個接收人的地址,就寫我們的wallet2吧
    to: "0xbe79D5B66A5D44607F91E312ec5E35b8c92db5bf",
    // 填寫一個金額
    value: ethers.utils.parseEther('2.33')
  };

  // 廣播轉(zhuǎn)賬信息
  const result =  await wallet1.sendTransaction(tx);
  console.log(result);
};

transfer();

// 轉(zhuǎn)賬結(jié)束后,查詢一下余額,數(shù)正確,沒毛病
getBalance().then();

合約部署

  • 在部署合約之前,需要把一個智能合約進行編譯。
  • 準備好一份solidty合約,這里用到的是一份在contracts目錄下,名為SimpleStorage.sol的合約。
  • 分別編譯完bytecodeabi后,會在contracts生成__SimpleStorage_sol_SimpleStorage.abi__SimpleStorage_sol_SimpleStorage.bin文件。
  • 在node讀取兩個編譯后的文件,上傳區(qū)塊就可以了。
//  ./contracts/SimpleStorage.sol
// 這個合約只有簡單寫入與讀取功能,還有一個通知前端的事件。
pragma solidity ^0.5.1;

contract SimpleStorage {

    event ValueChanged(address indexed author, string oldValue, string newValue);

    string _value;

    constructor(string memory value) public {
        emit ValueChanged(msg.sender, _value, value);
        _value = value;
    }

    function getValue() view public returns (string memory) {
        return _value;
    }

    function setValue(string memory value) public {
        emit ValueChanged(msg.sender, _value, value);
        _value = value;
    }
}
  • 運行命令
$ npm install solc
// 編譯bytecode
../node_modules/solc/solcjs ./SimpleStorage.sol --bin
// 編譯abi
../node_modules/solc/solcjs ./SimpleStorage.sol --abi

如果命令出錯,提示丟失smtchecker.js,運行cp ./fixBug/smtchecker.js ./node_modules/solc/

  • 設(shè)置好abi bytecode錢包 把對象發(fā)到區(qū)塊鏈上,
  • 如果一切順利,會打印出部署成后的地址,以及交易哈希。
// demo4.js
const ethers = require('ethers');
const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);
const  bytecodePath = './contracts/__SimpleStorage_sol_SimpleStorage.bin';
const abiPath = './contracts/__SimpleStorage_sol_SimpleStorage.abi';

const mnemonic1 = 'utility opinion husband upset finger side round exhaust arm allow pilot hospital';
const _wallet1 = ethers.Wallet.fromMnemonic(mnemonic1);
let provider = ethers.getDefaultProvider('rinkeby');
const wallet1 = _wallet1.connect(provider);

const deploy = async () => {
  const bytecode = await readFile(bytecodePath,{encoding: 'utf8'});
  const abi = await readFile(abiPath,{encoding: 'utf8'});
  let factory = new ethers.ContractFactory(abi, bytecode, wallet1);
  let contract = await factory.deploy("Hello World");
  console.log('contract.address',contract.address);
  await contract.deployed();
  console.log('hash',contract.deployTransaction.hash);
};

deploy();

合約執(zhí)行

  • 合約執(zhí)行分兩類,收費和免費,
    收費的一般都是修改數(shù)據(jù),免費的一般都是查看數(shù)據(jù)。
  • 合約的調(diào)用是通過api,部署了那些abi就可以在node中調(diào)用那些函數(shù)
  • 可以查看__SimpleStorage_sol_SimpleStorage.abi文件都有具體有那些方法,目前的例子有getValue /setValue
  • 順利運行后,會看到打印當前值和設(shè)置值'KKKK'
// 以上代碼運行后,合約地址為0x5Dbcdb3d61Bf83d5Fb6C926F23717A0138f536d9
const contractAddress = '0x5Dbcdb3d61Bf83d5Fb6C926F23717A0138f536d9';

const getContractValue = async () => {
  const abi = await readFile(abiPath,{encoding: 'utf8'});
  const contract = new ethers.Contract(contractAddress, abi, provider);
  const currentValue = await contract.getValue();
  console.log(currentValue);
};

getContractValue();

const setContractValue = async (value) => {
  const abi = await readFile(abiPath,{encoding: 'utf8'});
  const contract = new ethers.Contract(contractAddress, abi, provider);

  const currentValue = await contract.getValue();
  console.log('currentValue', currentValue);

  const contractWithSigner = contract.connect(wallet1);
  const tx = await contractWithSigner.setValue(value);
  console.log('tx.hash',tx.hash);
  await tx.wait();

  const netValue = await contract.getValue();
  console.log('netValue', netValue);

};

setContractValue('KKKK').catch();

合約監(jiān)聽

  • 在合約中還有一個 ValueChanged事件,提供監(jiān)聽合約的變化
  • 運行監(jiān)聽代碼后,只要合約的值產(chǎn)生變化,會返回到前端。
// demo3.js
const ethers = require('ethers');
const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);

const provider = ethers.getDefaultProvider('rinkeby');
const contractAddress = '0x5Dbcdb3d61Bf83d5Fb6C926F23717A0138f536d9';

const listening = async ()=> {
  const abi = await readFile('./contracts/SimpleStorage_sol_SimpleStorage.abi',{encoding: 'utf8'});
  const contract = new ethers.Contract(contractAddress, abi, provider);

// 監(jiān)聽合約的事件,
  contract.on("ValueChanged", (author, oldValue, newValue, event) => {
    // Called when anyone changes the value

    console.log('author', author);
    // "0x14791697260E4c9A71f18484C9f997B308e59325"

    console.log('oldValue', oldValue);
    // "Hello World"

    console.log('newValue', newValue);
    // "Ilike turtles."

    // See Event Emitter below for all properties on Event
    console.log('blockNumber', event.blockNumber);
    // 4115004
  });
};

listening().catch(console.log);

本項目所有代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容