Truffle

在 Ropsten 或任何測試網路上部署應用程序

  • May 13, 2020

我有一個 web3.js 應用程序,它使用松露並與部署在 Ganache 上的智能合約進行互動。如何在測試網路上部署我的智能合約並通過我的應用程序與之互動?

您首先需要安裝 MetaMask 並選擇您願意測試的網路。那麼你需要連接到 MetaMask。它會自動進入您選擇的測試網路。閱讀Metamask 文件並在此處了解如何連接

這是我的做法(使用 web3.js v1.2.1 測試):

const fs   = require("fs");
const Web3 = require("web3");

const MIN_GAS_LIMIT = 100000;
const CFG_FILE_NAME = "SomeFile.txt";
const NODE_ADDRESS  = "YourNodeURL";
const PRIVATE_KEY   = "YourPrivateKey";
const ARTIFACTS_DIR = __dirname + "/RelativePathToYourBinAndAbiFolder";

function get() {
   return JSON.parse(fs.readFileSync(CFG_FILE_NAME, {encoding: "utf8"}));
}

function set(record) {
   fs.writeFileSync(CFG_FILE_NAME, JSON.stringify({...get(), ...record}, null, 4));
}

async function scan(message) {
   process.stdout.write(message);
   return await new Promise(function(resolve, reject) {
       process.stdin.resume();
       process.stdin.once("data", function(data) {
           process.stdin.pause();
           resolve(data.toString().trim());
       });
   });
}

async function getGasPrice(web3) {
   while (true) {
       const nodeGasPrice = await web3.eth.getGasPrice();
       const userGasPrice = await scan(`Enter gas-price or leave empty to use ${nodeGasPrice}: `);
       if (/^\d+$/.test(userGasPrice))
           return userGasPrice;
       if (userGasPrice == "")
           return nodeGasPrice;
       console.log("Illegal gas-price");
   }
}

async function getTransactionReceipt(web3) {
   while (true) {
       const hash = await scan("Enter transaction-hash or leave empty to retry: ");
       if (/^0x([0-9A-Fa-f]{64})$/.test(hash)) {
           const receipt = await web3.eth.getTransactionReceipt(hash);
           if (receipt)
               return receipt;
           console.log("Invalid transaction-hash");
       }
       else if (hash) {
           console.log("Illegal transaction-hash");
       }
       else {
           return null;
       }
   }
}

async function send(web3, account, gasPrice, transaction, value = 0) {
   while (true) {
       try {
           const options = {
               to      : transaction._parent._address,
               data    : transaction.encodeABI(),
               gas     : Math.max(await transaction.estimateGas({from: account.address, value: value}), MIN_GAS_LIMIT),
               gasPrice: gasPrice ? gasPrice : await getGasPrice(web3),
               value   : value,
           };
           const signed  = await web3.eth.accounts.signTransaction(options, account.privateKey);
           const receipt = await web3.eth.sendSignedTransaction(signed.rawTransaction);
           return receipt;
       }
       catch (error) {
           console.log(error.message);
           const receipt = await getTransactionReceipt(web3);
           if (receipt)
               return receipt;
       }
   }
}

async function deploy(web3, account, gasPrice, contractId, contractName, contractArgs) {
   if (get()[contractId] == undefined) {
       const abi = fs.readFileSync(ARTIFACTS_DIR + contractName + ".abi", {encoding: "utf8"});
       const bin = fs.readFileSync(ARTIFACTS_DIR + contractName + ".bin", {encoding: "utf8"});
       const contract = new web3.eth.Contract(JSON.parse(abi));
       const options = {data: "0x" + bin, arguments: contractArgs};
       const transaction = contract.deploy(options);
       const receipt = await send(web3, account, gasPrice, transaction);
       const args = transaction.encodeABI().slice(options.data.length);
       console.log(`${contractId} deployed at ${receipt.contractAddress}`);
       set({[contractId]: {name: contractName, addr: receipt.contractAddress, args: args}});
   }
   return deployed(web3, contractName, get()[contractId].addr);
}

function deployed(web3, contractName, contractAddr) {
   const abi = fs.readFileSync(ARTIFACTS_DIR + contractName + ".abi", {encoding: "utf8"});
   return new web3.eth.Contract(JSON.parse(abi), contractAddr);
}

async function run() {
   const web3 = new Web3(NODE_ADDRESS);

   const gasPrice = await getGasPrice(web3);
   const account  = web3.eth.accounts.privateKeyToAccount(PRIVATE_KEY);
   const web3Func = (func, ...args) => func(web3, account, gasPrice, ...args);

   let phase = 0;
   if (get().phase == undefined)
       set({phase});
   const execute = async (transaction, ...args) => {
       if (get().phase == phase++) {
           await web3Func(send, transaction, ...args);
           console.log(`phase ${phase} executed`);
           set({phase});
       }
   };

   const contract1 = await web3Func(deploy, "contract1", "MyContract1", [arg1, arg2, arg3]);
   const contract2 = await web3Func(deploy, "contract2", "MyContract1", [arg4, arg5, arg6]);
   const contract3 = await web3Func(deploy, "contract3", "MyContract2", []);

   await execute(contract1.methods.setSomeValue(123));
   await execute(contract2.methods.setSomeValue(456));
   await execute(contract3.methods.doSomeStuff(true));

   if (web3.currentProvider.constructor.name == "WebsocketProvider")
       web3.currentProvider.connection.close();
}

run();

請注意,您需要在腳本的開頭定義以下變數:

const MIN_GAS_LIMIT = 100000;
const CFG_FILE_NAME = "SomeFile.txt";
const NODE_ADDRESS  = "YourNodeURL";
const PRIVATE_KEY   = "YourPrivateKey";
const ARTIFACTS_DIR = __dirname + "/RelativePathToYourBinAndAbiFolder";

或者,您可以通過命令行參數將它們傳遞給腳本,並通過以下方式讀取它們:

const MIN_GAS_LIMIT = process.argv[2];
const CFG_FILE_NAME = process.argv[3];
const NODE_ADDRESS  = process.argv[4];
const PRIVATE_KEY   = process.argv[5];
const ARTIFACTS_DIR = process.argv[6];

run然後,在腳本底部的主函式 ( ) 中,您應該實現您的初始化場景,而不是我的編碼範例:

   const contract1 = await web3Func(deploy, "contract1", "MyContract1", [arg1, arg2, arg3]);
   const contract2 = await web3Func(deploy, "contract2", "MyContract1", [arg4, arg5, arg6]);
   const contract3 = await web3Func(deploy, "contract3", "MyContract2", []);

   await execute(contract1.methods.setSomeValue(123));
   await execute(contract2.methods.setSomeValue(456));
   await execute(contract3.methods.doSomeStuff(true));

該腳本設計為從最後一個執行點恢復(即,如果先前中止)。

引用自:https://ethereum.stackexchange.com/questions/83347