trufflesuite/truffle-hdwallet-provider


https://github.com/trufflesuite/truffle-hdwallet-provider/blob/master/index.js

實現代碼

truffle-hdwallet-provider/index.js

const bip39 = require("bip39");
const ethJSWallet = require('ethereumjs-wallet');
const hdkey = require('ethereumjs-wallet/hdkey');
const debug = require('debug')('truffle-hdwallet-provider')
const ProviderEngine = require("web3-provider-engine");
const FiltersSubprovider = require('web3-provider-engine/subproviders/filters.js');
const NonceSubProvider = require('web3-provider-engine/subproviders/nonce-tracker.js');
const HookedSubprovider = require('web3-provider-engine/subproviders/hooked-wallet.js');
const ProviderSubprovider = require("web3-provider-engine/subproviders/provider.js");
const Web3 = require("web3");
const Transaction = require('ethereumjs-tx');
const ethUtil = require('ethereumjs-util');

// This line shares nonce state across multiple provider instances. Necessary
// because within truffle the wallet is repeatedly newed if it's declared in the config within a
// function, resetting nonce from tx to tx. An instance can opt out
// of this behavior by passing `shareNonce=false` to the constructor.
// See issue #65 for more
const singletonNonceSubProvider = new NonceSubProvider();

function HDWalletProvider(
  mnemonic,
  provider,
  address_index=0,//從給的mnemonic數組的第幾下標開始取
  num_addresses=1,//取里面的幾個mnemonic,即生成幾個address
  shareNonce=true, //共享nonce狀態,這樣錢包進行刷新時nonce值還是原來的值
  wallet_hdpath="m/44'/60'/0'/0/"//指明到第四層,即上面的操作是要在該賬戶的外鏈上生成num_addresses個公鑰
) {

  if (mnemonic && mnemonic.indexOf(' ') === -1 || Array.isArray(mnemonic)) {//傳入的是privateKey,一個或多個(數組)

    const privateKeys = Array.isArray(mnemonic) ? mnemonic : [mnemonic];//如果是一個也要將其轉成數組形式
    this.wallets = {};
    this.addresses = [];

    for (let i = address_index; i < address_index + num_addresses; i++){
      const privateKey = Buffer.from(privateKeys[i].replace('0x', ''), 'hex');//將privateKey轉成Buffer格式
      if (ethUtil.isValidPrivate(privateKey)) {//查看privateKey是否正確
        const wallet = ethJSWallet.fromPrivateKey(privateKey);//正確則能從主私鑰得到wallet
        const address = wallet.getAddressString();//得到wallet的address
        this.addresses.push(address);//並記錄到wallets和addresses中
        this.wallets[address] = wallet;
      }
    }
  } else {//否則傳入的就是一個mnemonic
    this.mnemonic = mnemonic;
    this.hdwallet = hdkey.fromMasterSeed(bip39.mnemonicToSeed(mnemonic));//根據mnemonic生成hdwallet
    this.wallet_hdpath = wallet_hdpath;
    this.wallets = {};
    this.addresses = [];

    if (!bip39.validateMnemonic(mnemonic)) {//如果mnemonic不對則報錯
      throw new Error("Mnemonic invalid or undefined")
    }

    for (let i = address_index; i < address_index + num_addresses; i++){
      const wallet = this.hdwallet.derivePath(this.wallet_hdpath + i).getWallet();// getWallet()-return a Wallet instance,這樣這個m/44'/60'/0'/0/i就能夠使用Wallet的API了
      const addr = '0x' + wallet.getAddress().toString('hex');//得到m/44'/60'/0'/0/i的address
      this.addresses.push(addr);//並記錄下來
      this.wallets[addr] = wallet;
    }
  }
  //得到上面根據privateKey和mnemonic生成的賬戶address和wallet
  const tmp_accounts = this.addresses;
  const tmp_wallets = this.wallets;

  this.engine = new ProviderEngine();
  this.engine.addProvider(new HookedSubprovider({ //先將HookedSubprovider添加進engine,並定義能夠進行的rpc操作
    getAccounts: function(cb) { cb(null, tmp_accounts) },
    getPrivateKey: function(address, cb) {
      if (!tmp_wallets[address]) { return cb('Account not found'); }
      else { cb(null, tmp_wallets[address].getPrivateKey().toString('hex')); }
    },
    signTransaction: function(txParams, cb) {
      let pkey;
      const from = txParams.from.toLowerCase()
      if (tmp_wallets[from]) { pkey = tmp_wallets[from].getPrivateKey(); }
      else { cb('Account not found'); }
      const tx = new Transaction(txParams);
      tx.sign(pkey);
      const rawTx = '0x' + tx.serialize().toString('hex');
      cb(null, rawTx);
    },
    signMessage(message, cb) {
      const dataIfExists = message.data;
      if (!dataIfExists) {
        cb('No data to sign');
      }
      if (!tmp_wallets[message.from]) {
        cb('Account not found');
      }
      let pkey = tmp_wallets[message.from].getPrivateKey();
      const dataBuff = ethUtil.toBuffer(dataIfExists);
      const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff);
      const sig = ethUtil.ecsign(msgHashBuff, pkey);
      const rpcSig = ethUtil.toRpcSig(sig.v, sig.r, sig.s);
      cb(null, rpcSig);
      }             
  }));

  (!shareNonce)//NonceSubProvider在HookedSubprovider和ProviderSubprovider之間被添加,以免多個錢包處理同一類數字資產的情況下同時生成交易時會導致nonce值相同造成沖突
    ? this.engine.addProvider(new NonceSubProvider())//如果shareNonce = false,不共享nonce值,則添加的NonceSubProvider為局部對象
    : this.engine.addProvider(singletonNonceSubProvider);////如果shareNonce = true,共享nonce值,則添加的singletonNonceSubProvider為全局常量

  this.engine.addProvider(new FiltersSubprovider());//添加FiltersSubprovider
  if (typeof provider === 'string') {//添加ProviderSubprovider
    this.engine.addProvider(new ProviderSubprovider(new Web3.providers.HttpProvider(provider)));
  } else {
    this.engine.addProvider(new ProviderSubprovider(provider));
  }
  this.engine.start(); // Required by the provider engine.
};

HDWalletProvider.prototype.sendAsync = function() {
  this.engine.sendAsync.apply(this.engine, arguments);
};

HDWalletProvider.prototype.send = function() {
  return this.engine.send.apply(this.engine, arguments);
};

// returns the address of the given address_index, first checking the cache
HDWalletProvider.prototype.getAddress = function(idx) {
  debug('getting addresses', this.addresses[0], idx)
  if (!idx) { return this.addresses[0]; }
  else { return this.addresses[idx]; }
}

// returns the addresses cache
HDWalletProvider.prototype.getAddresses = function() {
  return this.addresses;
}

module.exports = HDWalletProvider;

 

使用:

安裝:

npm install truffle-hdwallet-provider
//+ truffle-hdwallet-provider@0.0.6

 

var HDWalletProvider = require("truffle-hdwallet-provider");
var Web3 = require("web3");
var web3 = new Web3();
const mnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat';
var provider = new HDWalletProvider(mnemonic, "http://localhost:8202", 0, 1);
console.log(provider.getAddresses());
web3.setProvider(provider);
web3.eth.getBlockNumber((err, number) => {
    console.log(number);
});
// At termination, `provider.engine.stop()' should be called to finish the process elegantly.
provider.engine.stop();//一定要記得添加
//不能運行下面的內容,因為Error: Web3ProviderEngine does not support synchronous requests.
// web3.eth.coinbase((err, coinbase) => {
//     console.log(coinbase);
// });
// web3.eth.mining((err, state) => {
//     console.log(state);
// });

 

返回:

就成功生成了m/44'/60'/0'/0/0的address

userdeMacBook-Pro:test-hd-wallet user$ node index.js 
[ '0x627306090abab3a6e1400e9345bc60c78a8bef57' ]
301

 

如果改成:

var provider = new HDWalletProvider(mnemonic, "http://localhost:8202", 0, 5);

則為:

userdeMacBook-Pro:test-hd-wallet user$ node index.js 
[ '0x627306090abab3a6e1400e9345bc60c78a8bef57',
  '0xf17f52151ebef6c7334fad080c5704d77216b732',
  '0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef',
  '0x821aea9a577a9b44299b9c15c88cf3087f3b5544',
  '0x0d1d4e623d10f9fba5db95830f7d3839406c6af2' ]
301

 

在geth中運行sendTransaction后:

> web3.eth.sendTransaction({from:eth.accounts[0],to:'0x627306090abab3a6e1400e9345bc60c78a8bef57',value:web3.toWei(4,'ether')})
"0xd45b223fa40b36b214f178bb2c343d8a9cc7c94a7bbfacd8d69efd297f346786"

再查看賬戶余額:

var addresses = provider.getAddresses();
console.log(addresses);
web3.setProvider(provider);
web3.eth.getBlockNumber((err, number) => {
    console.log(number);
});
web3.eth.getBalance(addresses[0],(err,balance) => {
    if(!err){
        console.log(balance.toString());        
    }
});

返回:

userdeMacBook-Pro:test-hd-wallet user$ node index.js 
[ '0x627306090abab3a6e1400e9345bc60c78a8bef57',
  '0xf17f52151ebef6c7334fad080c5704d77216b732',
  '0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef',
  '0x821aea9a577a9b44299b9c15c88cf3087f3b5544',
  '0x0d1d4e623d10f9fba5db95830f7d3839406c6af2' ]
4000000000000000000
328

 

接下來部署一個合約看看:

var HDWalletProvider = require("truffle-hdwallet-provider");
var Web3 = require("web3");
const fs = require("fs");
const solc = require("solc");

var web3 = new Web3();
const mnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat';
var provider = new HDWalletProvider(mnemonic, "http://localhost:8202", 0, 5);
var addresses = provider.getAddresses();
// console.log(addresses);
web3.setProvider(provider);
web3.eth.getBlockNumber((err, number) => {
    console.log(number);
});
web3.eth.getBalance(addresses[0],(err,balance) => {
    if(!err){
        console.log(balance.toString());        
    }
});


let source = fs.readFileSync("./Adoption.sol",'utf8');//讀取sol智能合約文件
//對智能合約進行編譯,第二個參數設置為1可以激活優化器optimiser
let compiledContract = solc.compile(source,1);
var bytecode,abi;
for (let contractName in compiledContract.contracts) {
    bytecode = compiledContract.contracts[contractName].bytecode;//獲得編譯后合約的bytecode
    console.log(bytecode)
    abi = JSON.parse(compiledContract.contracts[contractName].interface); //獲得編譯后合約的abi並寫成json形式
    console.log(abi)
}

let MyContract = web3.eth.contract(abi);
var instance = MyContract.new({from:addresses[0],data:'0x'+bytecode,gas:30000000},function(e,contract){
         if(typeof contract.address !== 'undefined'){
            console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
        }else{
            console.log(e);
        }
});

setTimeout(()=>{
        web3.eth.getBalance(addresses[0],(err,balance) => {
        if(!err){
            console.log(balance.toString());        
        }
    })
},3000);

 

部署完之后就可以直接使用該合約:

var instance = MyContract.at('0xF12b5dd4EAD5F743C6BaA640B0216200e89B60Da');
instance.adopt(1,{from:addresses[0],gas:3000000},(err,result) => {
    if(!err){
        console.log(result);        
    }else{
        console.log(err);
    }
});

然后再調用:

instance.getAdopters({gas:3000000},(err,result) =>{
    if(!err){
        console.log(result);        
    }else{
        console.log(err);
    }
});

可以得到結果:

[ '0x627306090abab3a6e1400e9345bc60c78a8bef57',
  '0x627306090abab3a6e1400e9345bc60c78a8bef57',
  '0x0000000000000000000000000000000000000000',
  '0x0000000000000000000000000000000000000000',
  '0x0000000000000000000000000000000000000000',
  '0x0000000000000000000000000000000000000000',
  '0x0000000000000000000000000000000000000000',
  '0x0000000000000000000000000000000000000000',
  '0x0000000000000000000000000000000000000000',
  '0x0000000000000000000000000000000000000000',
  '0x0000000000000000000000000000000000000000',
  '0x0000000000000000000000000000000000000000',
  '0x0000000000000000000000000000000000000000',
  '0x0000000000000000000000000000000000000000',
  '0x0000000000000000000000000000000000000000',
  '0x0000000000000000000000000000000000000000' ]

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM