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