const deployutil = require('./deploy-util')
const DeployUtil = new deployutil();
const PrettyPrint = require('../styling/pretty-print');
const pp = new PrettyPrint();
const Logger = require('../logging/logger');
const ora = require('ora');
/** Deployer class. Handles all functionality with using web3 and compiled material to deploy! */
class Deployer {
/**
* Instantiate a Deployer object.
*
* <b>NOTE: This is not inteded to be called by the user; if you are looking to create a Deployer from web3, please use "static async build" instead!</b>
*
* @param {web3_instance} _web3 - Initizalized web3 instance, with provider set
* @param {object} [_compiled=null] - solc compilation output
* @param {string[]} [_accounts=null] - Array of accounts
* @param {Logger.state.ENUM} [_logSetting=Logger.state.NORMAL] - Logger state for deployer to use when running methods.
* @param {object} options - User options
*/
//_compiled as an optional param, a way to sort of... hmmm, should be property?
//NOTE: THIS IS NOT INTENDED TO BE CALLED; ONLY FOR PRIVATE USE. PLEASE USE BUILD INSTEAD!!!
constructor(_web3, _compiled = null, _accounts = null, _logSetting = Logger.state.NORMAL, _options=null) {
this.web3 = _web3;
this.accounts = _accounts;
this.compiled = _compiled;
this.log = new Logger(_logSetting);
this.options = _options;
}
/**
* Construct a Deployer object. <b>The intended point of entry.</b>
*
* @param {web3_instance} _web3 - Initizalized web3 instance, with provider set
* @param {JSON} [_compiled=null] - solc compilation output. Required for using `async deploy()` function!
* @param {Logger.state.ENUM} [_logSetting=Logger.state.NORMAL] - Logger state f
or deployer to use when running methods.
* @param {object} options - User options
*
* @return {Deployer} deployer - a new Deployer object instantiated using the provided parameters.
*/
static async build(_web3, _compiled = null, _logSetting = Logger.state.NORMAL, _options=null) {
let accounts = await _web3.eth.getAccounts();
return new Deployer(_web3, _compiled, accounts, _logSetting, _options);
}
/**
* Deploy a contract by name, by parsing the object's (if not provided) compiled material. Returns the contract deployed as a web3.eth.Contract object (as provided by web3).
*
* @param {string} name - Name of the contract (e.g. Calculator.sol, ERC20.sol)
* @param {array} [args=[]] - Constructor arguments for the contract
* @param {object} [sender={ from: this.accounts[0] }] - The send object to be passed along in deployment. By default, it is passed an object declaring the 1st account as the sender.
* @param {JSON} [compiled=this.compiled] - solc compilation output.
*
* @return {web3.eth.Contract} - Contract web3 object of the deployed contract.
*/
async deploy(name, args = [], sender = { from: this.accounts[0] }, compiled=this.compiled){
if(!compiled) { throw "No compilation material provided to deployer!"; }
this.log.print(Logger.state.SUPER, "Accounts: " + this.accounts + "\n");
const contractInput = DeployUtil.extractContract(compiled, name);
this.log.print(Logger.state.MASTER, contractInput);
let Contract = await this.deployContract(contractInput, args, sender);
return Contract;
}
/**
* Deploy a contract object [defined internally; see return val of DeployUtil.extractContract].
*
* @param {object} contract - Contract object [constructed internally, NOT web3]
* @param {array} args - Constructor arguments for the contract
* @param {object} sendOptions - The send object to be passed along in deployment.
*
* @return {web3.eth.Contract} - Contract web3 object of the deployed contract.
*/
async deployContract(contract, args, sendOptions){
console.log(pp.miniheadline("Deploying " + contract.name + '...'));
const spinner = ora().start();
spinner.clear();
let spinnerConf;
let contractWeb3 = await (new this.web3.eth.Contract(contract.abi)
.deploy({ "data": contract.bytecode.indexOf('0x') === 0 ? contract.bytecode : '0x' + contract.bytecode, "arguments": args })
.send(sendOptions)
.on('receipt', (receipt) => {
spinner.succeed();
console.log(pp.arrow("status: " + receipt.status ? "Success!" : "Failed :("));
console.log(pp.arrow("transaction hash: " + receipt.transactionHash));
console.log(pp.arrow("contract address: " + receipt.contractAddress));
console.log(pp.arrow("from: " + receipt.from));
console.log(pp.arrow("block number: " + receipt.blockNumber));
console.log(pp.arrow("gas used: " + receipt.gasUsed));
console.log(pp.miniheadline("\nPausing for " + this.options.deployment.confirmations + " confirmations..."));
spinnerConf = ora().start()
spinnerConf.clear();
})
.on('confirmation', (num, receipt) => {
console.log("confirmation number: " + num + " (block: " + receipt.blockNumber + ")");
if(num === this.options.deployment.confirmations) {
//if(spinnerConf) spinnerConf.succeed();
spinnerConf.succeed();
console.log("...");
console.log("Confirmed!");
process.exit(0);
console.log("\n\nExtra confirmations:\n")
//resolve();
}
})
.on('error', (err) => { console.log(err); spinner.fail(); }));
return contractWeb3;
}
}
//Deployment logic:
//Takes a Contract object, parameters, and an optional object to pass to send
//contract, args = [], sendobj = {}
//
//NumberContract = await new web3.eth.Contract(JSON.parse(contract.interface))
// .deploy({data: '0x' + contract.bytecode, args: []})
// .send(sendobj)
//deploy function, takes a contract object (name, interface, bytecode)
// Print intro headline
// Deploy, await receipt
// Print receipt info
// Listen for confirmations (likely through .then())
// follow up
// etc., etc.
//Print functions (returns a string, which is then printed):
//
// for "> "
// for headlines (adding ==== below as well)
// for mini-headlines (adding ---- below as well)
/*
Print: "Starting deployment..."
Print: as many '=' chars as above line
Print: "Addresses:"
Print array of addresses
//For each contract:
Print: "Deploying 'CONTRACTNAME'"
Print: as many '-' chars as above line
Print:
"> transaction hash: "
"> contract address: "
"> account: "
"> balance: "
//Other details...
Print: "Pausing for 2 confirmations..."
Print: as many '-' as above line
"> confirmation number: [NUMBER] (block: [BLOCKNUMBER])"
Print: "Summary"
Print: as many '=' chars as above line
"> Total deployments: [NUMBER]"
*/
module.exports = Deployer;