mirror of https://github.com/status-im/web3.js.git
298 lines
8.6 KiB
JavaScript
298 lines
8.6 KiB
JavaScript
/*
|
|
This file is part of web3.js.
|
|
|
|
web3.js is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
web3.js is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
/**
|
|
* @file contract.js
|
|
* @author Marek Kotewicz <marek@ethdev.com>
|
|
* @date 2014
|
|
*/
|
|
|
|
var utils = require('../utils/utils');
|
|
var coder = require('../solidity/coder');
|
|
var SolidityEvent = require('./event');
|
|
var SolidityFunction = require('./function');
|
|
var AllEvents = require('./allevents');
|
|
|
|
/**
|
|
* Should be called to encode constructor params
|
|
*
|
|
* @method encodeConstructorParams
|
|
* @param {Array} abi
|
|
* @param {Array} constructor params
|
|
*/
|
|
var encodeConstructorParams = function (abi, params) {
|
|
return abi.filter(function (json) {
|
|
return json.type === 'constructor' && json.inputs.length === params.length;
|
|
}).map(function (json) {
|
|
return json.inputs.map(function (input) {
|
|
return input.type;
|
|
});
|
|
}).map(function (types) {
|
|
return coder.encodeParams(types, params);
|
|
})[0] || '';
|
|
};
|
|
|
|
/**
|
|
* Should be called to add functions to contract object
|
|
*
|
|
* @method addFunctionsToContract
|
|
* @param {Contract} contract
|
|
* @param {Array} abi
|
|
*/
|
|
var addFunctionsToContract = function (contract) {
|
|
contract.abi.filter(function (json) {
|
|
return json.type === 'function';
|
|
}).map(function (json) {
|
|
return new SolidityFunction(contract._eth, json, contract.address);
|
|
}).forEach(function (f) {
|
|
f.attachToContract(contract);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Should be called to add events to contract object
|
|
*
|
|
* @method addEventsToContract
|
|
* @param {Contract} contract
|
|
* @param {Array} abi
|
|
*/
|
|
var addEventsToContract = function (contract) {
|
|
var events = contract.abi.filter(function (json) {
|
|
return json.type === 'event';
|
|
});
|
|
|
|
var All = new AllEvents(contract._eth._requestManager, events, contract.address);
|
|
All.attachToContract(contract);
|
|
|
|
events.map(function (json) {
|
|
return new SolidityEvent(contract._eth._requestManager, json, contract.address);
|
|
}).forEach(function (e) {
|
|
e.attachToContract(contract);
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Should be called to check if the contract gets properly deployed on the blockchain.
|
|
*
|
|
* @method checkForContractAddress
|
|
* @param {Object} contract
|
|
* @param {Function} callback
|
|
* @returns {Undefined}
|
|
*/
|
|
var checkForContractAddress = function(contract, callback){
|
|
var count = 0,
|
|
callbackFired = false;
|
|
|
|
// wait for receipt
|
|
var filter = contract._eth.filter('latest', function(e){
|
|
if (!e && !callbackFired) {
|
|
count++;
|
|
|
|
// stop watching after 50 blocks (timeout)
|
|
if (count > 50) {
|
|
|
|
filter.stopWatching();
|
|
callbackFired = true;
|
|
|
|
if (callback)
|
|
callback(new Error('Contract transaction couldn\'t be found after 50 blocks'));
|
|
else
|
|
throw new Error('Contract transaction couldn\'t be found after 50 blocks');
|
|
|
|
|
|
} else {
|
|
|
|
contract._eth.getTransactionReceipt(contract.transactionHash, function(e, receipt){
|
|
if(receipt && !callbackFired) {
|
|
|
|
contract._eth.getCode(receipt.contractAddress, function(e, code){
|
|
/*jshint maxcomplexity: 6 */
|
|
|
|
if(callbackFired || !code)
|
|
return;
|
|
|
|
filter.stopWatching();
|
|
callbackFired = true;
|
|
|
|
if(code.length > 2) {
|
|
|
|
// console.log('Contract code deployed!');
|
|
|
|
contract.address = receipt.contractAddress;
|
|
|
|
// attach events and methods again after we have
|
|
addFunctionsToContract(contract);
|
|
addEventsToContract(contract);
|
|
|
|
// call callback for the second time
|
|
if(callback)
|
|
callback(null, contract);
|
|
|
|
} else {
|
|
if(callback)
|
|
callback(new Error('The contract code couldn\'t be stored, please check your gas amount.'));
|
|
else
|
|
throw new Error('The contract code couldn\'t be stored, please check your gas amount.');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Should be called to create new ContractFactory instance
|
|
*
|
|
* @method ContractFactory
|
|
* @param {Array} abi
|
|
*/
|
|
var ContractFactory = function (eth, abi) {
|
|
this.eth = eth;
|
|
this.abi = abi;
|
|
|
|
this.new.getData = this.getData.bind(this);
|
|
};
|
|
|
|
/**
|
|
* Should be called to create new ContractFactory
|
|
*
|
|
* @method contract
|
|
* @param {Array} abi
|
|
* @returns {ContractFactory} new contract factory
|
|
*/
|
|
//var contract = function (abi) {
|
|
//return new ContractFactory(abi);
|
|
//};
|
|
|
|
/**
|
|
* Should be called to create new contract on a blockchain
|
|
*
|
|
* @method new
|
|
* @param {Any} contract constructor param1 (optional)
|
|
* @param {Any} contract constructor param2 (optional)
|
|
* @param {Object} contract transaction object (required)
|
|
* @param {Function} callback
|
|
* @returns {Contract} returns contract instance
|
|
*/
|
|
ContractFactory.prototype.new = function () {
|
|
var contract = new Contract(this.eth, this.abi);
|
|
|
|
// parse arguments
|
|
var options = {}; // required!
|
|
var callback;
|
|
|
|
var args = Array.prototype.slice.call(arguments);
|
|
if (utils.isFunction(args[args.length - 1])) {
|
|
callback = args.pop();
|
|
}
|
|
|
|
var last = args[args.length - 1];
|
|
if (utils.isObject(last) && !utils.isArray(last)) {
|
|
options = args.pop();
|
|
}
|
|
|
|
var bytes = encodeConstructorParams(this.abi, args);
|
|
options.data += bytes;
|
|
|
|
if (callback) {
|
|
|
|
// wait for the contract address adn check if the code was deployed
|
|
this.eth.sendTransaction(options, function (err, hash) {
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
// add the transaction hash
|
|
contract.transactionHash = hash;
|
|
|
|
// call callback for the first time
|
|
callback(null, contract);
|
|
|
|
checkForContractAddress(contract, callback);
|
|
}
|
|
});
|
|
} else {
|
|
var hash = this.eth.sendTransaction(options);
|
|
// add the transaction hash
|
|
contract.transactionHash = hash;
|
|
checkForContractAddress(contract);
|
|
}
|
|
|
|
return contract;
|
|
};
|
|
|
|
/**
|
|
* Should be called to get access to existing contract on a blockchain
|
|
*
|
|
* @method at
|
|
* @param {Address} contract address (required)
|
|
* @param {Function} callback {optional)
|
|
* @returns {Contract} returns contract if no callback was passed,
|
|
* otherwise calls callback function (err, contract)
|
|
*/
|
|
ContractFactory.prototype.at = function (address, callback) {
|
|
var contract = new Contract(this.eth, this.abi, address);
|
|
|
|
// this functions are not part of prototype,
|
|
// because we dont want to spoil the interface
|
|
addFunctionsToContract(contract);
|
|
addEventsToContract(contract);
|
|
|
|
if (callback) {
|
|
callback(null, contract);
|
|
}
|
|
return contract;
|
|
};
|
|
|
|
/**
|
|
* Gets the data, which is data to deploy plus constructor params
|
|
*
|
|
* @method getData
|
|
*/
|
|
ContractFactory.prototype.getData = function () {
|
|
var options = {}; // required!
|
|
var args = Array.prototype.slice.call(arguments);
|
|
|
|
var last = args[args.length - 1];
|
|
if (utils.isObject(last) && !utils.isArray(last)) {
|
|
options = args.pop();
|
|
}
|
|
|
|
var bytes = encodeConstructorParams(this.abi, args);
|
|
options.data += bytes;
|
|
|
|
return options.data;
|
|
};
|
|
|
|
/**
|
|
* Should be called to create new contract instance
|
|
*
|
|
* @method Contract
|
|
* @param {Array} abi
|
|
* @param {Address} contract address
|
|
*/
|
|
var Contract = function (eth, abi, address) {
|
|
this._eth = eth;
|
|
this.transactionHash = null;
|
|
this.address = address;
|
|
this.abi = abi;
|
|
};
|
|
|
|
module.exports = ContractFactory;
|
|
|