Truffle
在 Ropsten 或任何測試網路上部署應用程序
我有一個 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));
該腳本設計為從最後一個執行點恢復(即,如果先前中止)。