commit
35d94d18a0
@ -0,0 +1,148 @@ |
||||
'use strict' |
||||
|
||||
var $ = require('jquery') |
||||
var txHelper = require('../execution/txHelper') |
||||
|
||||
module.exports = (contractName, contract, compiledSource) => { |
||||
return getDetails(contractName, contract, compiledSource) |
||||
} |
||||
|
||||
var getDetails = function (contractName, contract, source) { |
||||
var detail = {} |
||||
detail.name = contractName |
||||
detail.metadata = contract.metadata |
||||
if (contract.bytecode) { |
||||
detail.bytecode = contract.bytecode |
||||
} |
||||
|
||||
detail.interface = contract.interface |
||||
|
||||
if (contract.bytecode) { |
||||
detail.bytecode = contract.bytecode |
||||
detail.web3Deploy = gethDeploy(contractName.toLowerCase(), contract['interface'], contract.bytecode) |
||||
|
||||
detail.metadataHash = retrieveMetadataHash(contract.bytecode) |
||||
if (detail.metadataHash) { |
||||
detail.swarmLocation = 'bzz://' + detail.metadataHash |
||||
} |
||||
} |
||||
|
||||
detail.functionHashes = {} |
||||
for (var fun in contract.functionHashes) { |
||||
detail.functionHashes[contract.functionHashes[fun]] = fun |
||||
} |
||||
|
||||
detail.gasEstimates = formatGasEstimates(contract.gasEstimates) |
||||
|
||||
if (contract.runtimeBytecode && contract.runtimeBytecode.length > 0) { |
||||
detail['Runtime Bytecode'] = contract.runtimeBytecode |
||||
} |
||||
|
||||
if (contract.opcodes !== undefined && contract.opcodes !== '') { |
||||
detail['Opcodes'] = contract.opcodes |
||||
} |
||||
|
||||
if (contract.assembly !== null) { |
||||
detail['Assembly'] = formatAssemblyText(contract.assembly, '', source) |
||||
} |
||||
|
||||
return detail |
||||
} |
||||
|
||||
var retrieveMetadataHash = function (bytecode) { |
||||
var match = /a165627a7a72305820([0-9a-f]{64})0029$/.exec(bytecode) |
||||
if (match) { |
||||
return match[1] |
||||
} |
||||
} |
||||
|
||||
var formatAssemblyText = function (asm, prefix, source) { |
||||
if (typeof asm === typeof '' || asm === null || asm === undefined) { |
||||
return prefix + asm + '\n' |
||||
} |
||||
var text = prefix + '.code\n' |
||||
$.each(asm['.code'], function (i, item) { |
||||
var v = item.value === undefined ? '' : item.value |
||||
var src = '' |
||||
if (item.begin !== undefined && item.end !== undefined) { |
||||
src = source.slice(item.begin, item.end).replace('\n', '\\n', 'g') |
||||
} |
||||
if (src.length > 30) { |
||||
src = src.slice(0, 30) + '...' |
||||
} |
||||
if (item.name !== 'tag') { |
||||
text += ' ' |
||||
} |
||||
text += prefix + item.name + ' ' + v + '\t\t\t' + src + '\n' |
||||
}) |
||||
text += prefix + '.data\n' |
||||
if (asm['.data']) { |
||||
$.each(asm['.data'], function (i, item) { |
||||
text += ' ' + prefix + '' + i + ':\n' |
||||
text += formatAssemblyText(item, prefix + ' ', source) |
||||
}) |
||||
} |
||||
return text |
||||
} |
||||
|
||||
var gethDeploy = function (contractName, jsonInterface, bytecode) { |
||||
var code = '' |
||||
var funABI = txHelper.getConstructorInterface(JSON.parse(jsonInterface)) |
||||
|
||||
funABI.inputs.forEach(function (inp) { |
||||
code += 'var ' + inp.name + ' = /* var of type ' + inp.type + ' here */ ;\n' |
||||
}) |
||||
|
||||
contractName = contractName.replace(/[:./]/g, '_') |
||||
code += 'var ' + contractName + 'Contract = web3.eth.contract(' + jsonInterface.replace('\n', '') + ');' + |
||||
'\nvar ' + contractName + ' = ' + contractName + 'Contract.new(' |
||||
|
||||
funABI.inputs.forEach(function (inp) { |
||||
code += '\n ' + inp.name + ',' |
||||
}) |
||||
|
||||
code += '\n {' + |
||||
'\n from: web3.eth.accounts[0], ' + |
||||
"\n data: '0x" + bytecode + "', " + |
||||
"\n gas: '4700000'" + |
||||
'\n }, function (e, contract){' + |
||||
'\n console.log(e, contract);' + |
||||
"\n if (typeof contract.address !== 'undefined') {" + |
||||
"\n console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);" + |
||||
'\n }' + |
||||
'\n })' |
||||
|
||||
return code |
||||
} |
||||
|
||||
var formatGasEstimates = function (data) { |
||||
// FIXME: the whole gasEstimates object should be nil instead
|
||||
if (data.creation === undefined && data.external === undefined && data.internal === undefined) { |
||||
return |
||||
} |
||||
|
||||
var gasToText = function (g) { |
||||
return g === null ? 'unknown' : g |
||||
} |
||||
|
||||
var ret = {} |
||||
var fun |
||||
if ('creation' in data) { |
||||
ret['Creation'] = gasToText(data.creation[0]) + ' + ' + gasToText(data.creation[1]) + '\n' |
||||
} |
||||
|
||||
if ('external' in data) { |
||||
ret['External'] = {} |
||||
for (fun in data.external) { |
||||
ret['External'][fun] = gasToText(data.external[fun]) |
||||
} |
||||
} |
||||
|
||||
if ('internal' in data) { |
||||
ret['Internal'] = {} |
||||
for (fun in data.internal) { |
||||
ret['Internal'][fun] = gasToText(data.internal[fun]) |
||||
} |
||||
} |
||||
return ret |
||||
} |
@ -0,0 +1,65 @@ |
||||
'use strict' |
||||
|
||||
var async = require('async') |
||||
var swarmgw = require('swarmgw') |
||||
|
||||
module.exports = (contract, appAPI, cb) => { |
||||
// gather list of files to publish
|
||||
var sources = [] |
||||
|
||||
sources.push({ |
||||
content: contract.metadata, |
||||
hash: contract.metadataHash |
||||
}) |
||||
|
||||
var metadata |
||||
try { |
||||
metadata = JSON.parse(contract.metadata) |
||||
} catch (e) { |
||||
return cb(e) |
||||
} |
||||
|
||||
if (metadata === undefined) { |
||||
return cb('No metadata') |
||||
} |
||||
|
||||
async.eachSeries(Object.keys(metadata.sources), function (fileName, cb) { |
||||
// find hash
|
||||
var hash |
||||
try { |
||||
hash = metadata.sources[fileName].urls[0].match('bzzr://(.+)')[1] |
||||
} catch (e) { |
||||
return cb('Metadata inconsistency') |
||||
} |
||||
|
||||
appAPI.fileProviderOf(fileName).get(fileName, (error, content) => { |
||||
if (error) { |
||||
console.log(error) |
||||
} else { |
||||
sources.push({ |
||||
content: content, |
||||
hash: hash |
||||
}) |
||||
} |
||||
cb() |
||||
}) |
||||
}, function () { |
||||
// publish the list of sources in order, fail if any failed
|
||||
async.eachSeries(sources, function (item, cb) { |
||||
swarmVerifiedPublish(item.content, item.hash, cb) |
||||
}, cb) |
||||
}) |
||||
} |
||||
|
||||
|
||||
function swarmVerifiedPublish (content, expectedHash, cb) { |
||||
swarmgw.put(content, function (err, ret) { |
||||
if (err) { |
||||
cb(err) |
||||
} else if (ret !== expectedHash) { |
||||
cb('Hash mismatch') |
||||
} else { |
||||
cb() |
||||
} |
||||
}) |
||||
} |
@ -0,0 +1,33 @@ |
||||
'use strict' |
||||
|
||||
module.exports = { |
||||
/** |
||||
* deploy the given contract |
||||
* |
||||
* @param {String} data - data to send with the transaction ( return of txFormat.buildData(...) ). |
||||
* @param {Object} udap - udapp. |
||||
* @param {Function} callback - callback. |
||||
*/ |
||||
createContract: function (data, udapp, callback) { |
||||
udapp.runTx({data: data, useCall: false}, (error, txResult) => { |
||||
// see universaldapp.js line 660 => 700 to check possible values of txResult (error case)
|
||||
callback(error, txResult) |
||||
}) |
||||
}, |
||||
|
||||
/** |
||||
* call the current given contract |
||||
* |
||||
* @param {String} to - address of the contract to call. |
||||
* @param {String} data - data to send with the transaction ( return of txFormat.buildData(...) ). |
||||
* @param {Object} funAbi - abi definition of the function to call. |
||||
* @param {Object} udap - udapp. |
||||
* @param {Function} callback - callback. |
||||
*/ |
||||
callFunction: function (to, data, funAbi, udapp, callback) { |
||||
udapp.runTx({to: to, data: data, useCall: funAbi.constant}, (error, txResult) => { |
||||
// see universaldapp.js line 660 => 700 to check possible values of txResult (error case)
|
||||
callback(error, txResult) |
||||
}) |
||||
} |
||||
} |
@ -0,0 +1,153 @@ |
||||
'use strict' |
||||
var $ = require('jquery') |
||||
var ethJSABI = require('ethereumjs-abi') |
||||
var helper = require('./txHelper') |
||||
|
||||
module.exports = { |
||||
/** |
||||
* build the transaction data |
||||
* |
||||
* @param {Object} contract - abi definition of the current contract. |
||||
* @param {Object} contracts - map of all compiled contracts. |
||||
* @param {Bool} isConstructor - isConstructor. |
||||
* @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor. |
||||
* @param {Object} params - input paramater of the function to call |
||||
* @param {Object} udapp - udapp |
||||
* @param {Object} executionContext - executionContext |
||||
* @param {Function} callback - callback |
||||
*/ |
||||
buildData: function (contract, contracts, isConstructor, funAbi, params, udapp, executionContext, callback) { |
||||
var funArgs = '' |
||||
try { |
||||
funArgs = $.parseJSON('[' + params + ']') |
||||
} catch (e) { |
||||
callback('Error encoding arguments: ' + e) |
||||
return |
||||
} |
||||
var data = '' |
||||
var dataHex = '' |
||||
if (!isConstructor || funArgs.length > 0) { |
||||
try { |
||||
data = helper.encodeParams(funAbi, funArgs) |
||||
dataHex = data.toString('hex') |
||||
} catch (e) { |
||||
callback('Error encoding arguments: ' + e) |
||||
return |
||||
} |
||||
} |
||||
if (data.slice(0, 9) === 'undefined') { |
||||
dataHex = data.slice(9) |
||||
} |
||||
if (data.slice(0, 2) === '0x') { |
||||
dataHex = data.slice(2) |
||||
} |
||||
if (isConstructor) { |
||||
var bytecodeToDeploy = contract.bytecode |
||||
if (bytecodeToDeploy.indexOf('_') >= 0) { |
||||
this.linkBytecode(contract, contracts, executionContext, udapp, (err, bytecode) => { |
||||
if (err) { |
||||
callback('Error deploying required libraries: ' + err) |
||||
} else { |
||||
bytecodeToDeploy = bytecode + dataHex |
||||
return callback(null, bytecodeToDeploy) |
||||
} |
||||
}) |
||||
return |
||||
} else { |
||||
dataHex = bytecodeToDeploy + dataHex |
||||
} |
||||
} else { |
||||
dataHex = Buffer.concat([helper.encodeFunctionId(funAbi), data]).toString('hex') |
||||
} |
||||
callback(null, dataHex) |
||||
}, |
||||
|
||||
atAddress: function () {}, |
||||
|
||||
linkBytecode: function (contract, contracts, executionContext, udapp, callback) { |
||||
var bytecode = contract.bytecode |
||||
if (bytecode.indexOf('_') < 0) { |
||||
return callback(null, bytecode) |
||||
} |
||||
var m = bytecode.match(/__([^_]{1,36})__/) |
||||
if (!m) { |
||||
return callback('Invalid bytecode format.') |
||||
} |
||||
var libraryName = m[1] |
||||
var libraryabi = helper.getContractByName(libraryName, contracts) |
||||
if (!libraryabi) { |
||||
return callback('Library ' + libraryName + ' not found.') |
||||
} |
||||
this.deployLibrary(libraryabi, executionContext, udapp, (err, address) => { |
||||
if (err) { |
||||
return callback(err) |
||||
} |
||||
var libLabel = '__' + libraryName + Array(39 - libraryName.length).join('_') |
||||
var hexAddress = address.toString('hex') |
||||
if (hexAddress.slice(0, 2) === '0x') { |
||||
hexAddress = hexAddress.slice(2) |
||||
} |
||||
hexAddress = Array(40 - hexAddress.length + 1).join('0') + hexAddress |
||||
while (bytecode.indexOf(libLabel) >= 0) { |
||||
bytecode = bytecode.replace(libLabel, hexAddress) |
||||
} |
||||
contract.bytecode = bytecode |
||||
this.linkBytecode(contract, contracts, executionContext, udapp, callback) |
||||
}) |
||||
}, |
||||
|
||||
deployLibrary: function (libraryName, library, executionContext, udapp, callback) { |
||||
var address = library.address |
||||
if (address) { |
||||
return callback(null, address) |
||||
} |
||||
var bytecode = library.bytecode |
||||
if (bytecode.indexOf('_') >= 0) { |
||||
this.linkBytecode(libraryName, (err, bytecode) => { |
||||
if (err) callback(err) |
||||
else this.deployLibrary(libraryName, callback) |
||||
}) |
||||
} else { |
||||
udapp.runTx({ data: bytecode, useCall: false }, (err, txResult) => { |
||||
if (err) { |
||||
return callback(err) |
||||
} |
||||
var address = executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress |
||||
library.address = address |
||||
callback(err, address) |
||||
}) |
||||
} |
||||
}, |
||||
|
||||
decodeResponse: function (response, fnabi, callback) { |
||||
// Only decode if there supposed to be fields
|
||||
if (fnabi.outputs && fnabi.outputs.length > 0) { |
||||
try { |
||||
var i |
||||
|
||||
var outputTypes = [] |
||||
for (i = 0; i < fnabi.outputs.length; i++) { |
||||
outputTypes.push(fnabi.outputs[i].type) |
||||
} |
||||
|
||||
// decode data
|
||||
var decodedObj = ethJSABI.rawDecode(outputTypes, response) |
||||
|
||||
// format decoded data
|
||||
decodedObj = ethJSABI.stringify(outputTypes, decodedObj) |
||||
for (i = 0; i < outputTypes.length; i++) { |
||||
var name = fnabi.outputs[i].name |
||||
if (name.length > 0) { |
||||
decodedObj[i] = outputTypes[i] + ' ' + name + ': ' + decodedObj[i] |
||||
} else { |
||||
decodedObj[i] = outputTypes[i] + ': ' + decodedObj[i] |
||||
} |
||||
} |
||||
|
||||
return callback(null, decodedObj) |
||||
} catch (e) { |
||||
return callback('Failed to decode output: ' + e) |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,106 @@ |
||||
'use strict' |
||||
var ethJSABI = require('ethereumjs-abi') |
||||
var $ = require('jquery') |
||||
|
||||
module.exports = { |
||||
encodeParams: function (funABI, args) { |
||||
var types = [] |
||||
if (funABI.inputs && funABI.inputs.length) { |
||||
for (var i = 0; i < funABI.inputs.length; i++) { |
||||
types.push(funABI.inputs[i].type) |
||||
} |
||||
} |
||||
|
||||
// NOTE: the caller will concatenate the bytecode and this
|
||||
// it could be done here too for consistency
|
||||
return ethJSABI.rawEncode(types, args) |
||||
}, |
||||
|
||||
encodeFunctionId: function (funABI) { |
||||
var types = [] |
||||
if (funABI.inputs && funABI.inputs.length) { |
||||
for (var i = 0; i < funABI.inputs.length; i++) { |
||||
types.push(funABI.inputs[i].type) |
||||
} |
||||
} |
||||
|
||||
return ethJSABI.methodID(funABI.name, types) |
||||
}, |
||||
|
||||
sortAbiFunction: function (contract) { |
||||
var abi = JSON.parse(contract.interface).sort(function (a, b) { |
||||
if (a.name > b.name) { |
||||
return -1 |
||||
} else { |
||||
return 1 |
||||
} |
||||
}).sort(function (a, b) { |
||||
if (a.constant === true) { |
||||
return -1 |
||||
} else { |
||||
return 1 |
||||
} |
||||
}) |
||||
return abi |
||||
}, |
||||
|
||||
getConstructorInterface: function (abi) { |
||||
var funABI = { 'name': '', 'inputs': [], 'type': 'constructor', 'outputs': [] } |
||||
if (typeof abi === 'string') { |
||||
try { |
||||
abi = JSON.parse(abi) |
||||
} catch (e) { |
||||
console.log('exception retrieving ctor abi ' + abi) |
||||
return funABI |
||||
} |
||||
} |
||||
|
||||
for (var i = 0; i < abi.length; i++) { |
||||
if (abi[i].type === 'constructor') { |
||||
funABI.inputs = abi[i].inputs || [] |
||||
break |
||||
} |
||||
} |
||||
|
||||
return funABI |
||||
}, |
||||
|
||||
getFunction: function (abi, fnName) { |
||||
for (var i = 0; i < abi.length; i++) { |
||||
if (abi[i].name === fnName) { |
||||
return abi[i] |
||||
} |
||||
} |
||||
return null |
||||
}, |
||||
|
||||
getFallbackInterface: function (abi) { |
||||
for (var i = 0; i < abi.length; i++) { |
||||
if (abi[i].type === 'fallback') { |
||||
return abi[i] |
||||
} |
||||
} |
||||
}, |
||||
|
||||
getContractByName: function (contractName, contracts) { |
||||
for (var c in contracts) { |
||||
if (contracts[c].name === contractName) { |
||||
return contracts[c] |
||||
} |
||||
} |
||||
return null |
||||
}, |
||||
|
||||
inputParametersDeclarationToString: function (abiinputs) { |
||||
var inputs = '' |
||||
if (abiinputs) { |
||||
$.each(abiinputs, function (i, inp) { |
||||
if (inputs !== '') { |
||||
inputs += ', ' |
||||
} |
||||
inputs += inp.type + ' ' + inp.name |
||||
}) |
||||
} |
||||
return inputs |
||||
} |
||||
} |
Loading…
Reference in new issue