').append(second));
+ };
+ var tableRow = function(description, data) {
+ return tableRowItems(
+ $('
').text(description),
+ $('
').val(data));
+ };
+ var textRow = function(description, data, cls) {
+ return tableRowItems(
+ $('
').text(description),
+ $('
').val(data),
+ cls);
+ };
+ var getDetails = function(contract, source, contractName) {
+ var button = $('
');
+ var details = $('
')
+ .append(tableRow('Solidity Interface', contract.solidity_interface))
+ .append(tableRow('Opcodes', contract.opcodes));
+ var funHashes = '';
+ for (var fun in contract.functionHashes)
+ funHashes += contract.functionHashes[fun] + ' ' + fun + '\n';
+ details.append($('
Functions'));
+ details.append($('
').text(funHashes));
+ details.append($('
Gas Estimates'));
+ details.append($('
').text(formatGasEstimates(contract.gasEstimates)));
+ if (contract.runtimeBytecode && contract.runtimeBytecode.length > 0)
+ details.append(tableRow('Runtime Bytecode', contract.runtimeBytecode));
+ if (contract.assembly !== null)
+ {
+ details.append($('
Assembly'));
+ var assembly = $('
').text(formatAssemblyText(contract.assembly, '', source));
+ details.append(assembly);
+ }
+ button.click(function() { detailsOpen[contractName] = !detailsOpen[contractName]; details.toggle(); });
+ if (detailsOpen[contractName])
+ details.show();
+ return $('
').append(button).append(details);
+ };
+ var formatGasEstimates = function(data) {
+ var gasToText = function(g) { return g === null ? 'unknown' : g; }
+ var text = '';
+ if ('creation' in data)
+ text += 'Creation: ' + gasToText(data.creation[0]) + ' + ' + gasToText(data.creation[1]) + '\n';
+ text += 'External:\n';
+ for (var fun in data.external)
+ text += ' ' + fun + ': ' + gasToText(data.external[fun]) + '\n';
+ text += 'Internal:\n';
+ for (var fun in data.internal)
+ text += ' ' + fun + ': ' + gasToText(data.internal[fun]) + '\n';
+ return text;
+ };
+ 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;
+ };
+ $('.asmOutput button').click(function() {$(this).parent().find('pre').toggle(); } )
+
+ // ----------------- VM ----------------------
+
+ var stateTrie = new EthVm.Trie();
+ var vm = new EthVm.VM(stateTrie);
+ //@todo this does not calculate the gas costs correctly but gets the job done.
+ var identityCode = 'return { gasUsed: 1, return: opts.data, exception: 1 };';
+ var identityAddr = ethUtil.pad(new Buffer('04', 'hex'), 20)
+ vm.loadPrecompiled(identityAddr, identityCode);
+ var secretKey = '3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511'
+ var publicKey = '0406cc661590d48ee972944b35ad13ff03c7876eae3fd191e8a2f77311b0a3c6613407b5005e63d7d8d76b89d5f900cde691497688bb281e07a5052ff61edebdc0'
+ var address = ethUtil.pubToAddress(new Buffer(publicKey, 'hex'));
+ $('#txorigin').text('0x' + address.toString('hex'));
+ var account = new EthVm.Account();
+ account.balance = 'f00000000000000001';
+ var nonce = 0;
+ stateTrie.put(address, account.serialize());
+ var runTx = function(data, to, cb) {
+ var tx = new EthVm.Transaction({
+ nonce: new Buffer([nonce++]), //@todo count beyond 255
+ gasPrice: '01',
+ gasLimit: '3000000000', // plenty
+ to: to,
+ data: data
+ });
+ tx.sign(new Buffer(secretKey, 'hex'));
+ vm.runTx({tx: tx}, cb);
+ };
+
+ var getConstructorInterface = function(abi) {
+ var funABI = {'name':'','inputs':[],'type':'constructor','outputs':[]};
+ for (var i = 0; i < abi.length; i++)
+ if (abi[i].type == 'constructor') {
+ funABI.inputs = abi[i].inputs || [];
+ break;
+ }
+ return funABI;
+ };
+
+ var getCallButton = function(args) {
+ // args.abi, args.bytecode [constr only], args.address [fun only]
+ // args.appendFunctions [constr only]
+ var isConstructor = args.bytecode !== undefined;
+ var fun = new web3.eth.function(args.abi);
+ var inputs = '';
+ $.each(args.abi.inputs, function(i, inp) {
+ if (inputs != '') inputs += ', ';
+ inputs += inp.type + ' ' + inp.name;
+ });
+ var inputField = $('
').attr('placeholder', inputs);
+ var outputSpan = $('
');
+ var button = $('
')
+ .text(args.bytecode ? 'Create' : fun.displayName())
+ .click(function() {
+ var funArgs = $.parseJSON('[' + inputField.val() + ']');
+ var data = fun.toPayload(funArgs).data;
+ if (data.slice(0, 2) == '0x') data = data.slice(2);
+ if (isConstructor)
+ data = args.bytecode + data.slice(8);
+ outputSpan.text('...');
+ runTx(data, args.address, function(err, result) {
+ if (err)
+ outputSpan.text(err);
+ else if (isConstructor) {
+ outputSpan.text(' Creation used ' + result.vm.gasUsed.toString(10) + ' gas.');
+ args.appendFunctions(result.createdAddress);
+ } else {
+ var outputObj = fun.unpackOutput('0x' + result.vm.return.toString('hex'));
+ outputSpan.text(' Returned: ' + JSON.stringify(outputObj));
+ }
+ });
+ });
+ if (!isConstructor)
+ button.addClass('runButton');
+ var c = $('
')
+ .append(button);
+ if (args.abi.inputs.length > 0)
+ c.append(inputField);
+ return c.append(outputSpan);
+ };
+
+ var getExecuteInterface = function(contract, name) {
+ var abi = $.parseJSON(contract.interface);
+ var execInter = $('
');
+ var funABI = getConstructorInterface(abi);
+
+ var appendFunctions = function(address) {
+ var instance = $('
');
+ var title = $('
').text('Contract at ' + address.toString('hex') );
+ instance.append(title);
+ $.each(abi, function(i, funABI) {
+ if (funABI.type != 'function') return;
+ instance.append(getCallButton({
+ abi: funABI,
+ address: address
+ }));
+ });
+ execInter.append(instance);
+ title.click(function(ev){ $(this).parent().toggleClass('hide') });
+ };
+
+ if (contract.bytecode.length > 0)
+ execInter
+ .append(getCallButton({
+ abi: funABI,
+ bytecode: contract.bytecode,
+ appendFunctions: appendFunctions
+ }));
+ return execInter;
+ };
+
+