remix-project mirror
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
remix-project/index.html

650 lines
21 KiB

9 years ago
<html>
<head>
<meta charset="utf-8">
9 years ago
<!--
The MIT License (MIT)
Copyright (c) 2014, 2015, the individual contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
9 years ago
<meta http-equiv="X-UA-Compatible" content="chrome=1">
9 years ago
<title>Solidity realtime compiler and runtime</title>
9 years ago
<link rel="stylesheet" href="stylesheets/styles.css">
<link rel="stylesheet" href="stylesheets/pygment_trac.css">
<link rel="stylesheet" href="stylesheets/browser-solidity.css">
9 years ago
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<style type="text/css">
9 years ago
</style>
<script src="bin/list.js"></script>
<script src="libs/jquery-2.1.3.min.js"></script>
9 years ago
<script src="libs/ace.js"></script>
<script src="libs/mode-solidity.js"></script>
<script src="bin/soljson-latest.js"></script>
<script src="libs/ethereumjs-vm.js"></script>
<script src="libs/web3.min.js"></script>
<script src="ballot.sol.js"></script>
9 years ago
</head>
<body>
<div id="editor">
9 years ago
<div id="files">
<span class="newFile" title="New File">+</span>
</div>
<div id="input"></div>
</div>
<div id="righthand-panel">
<div id="dragbar"></div>
<div id="header">
<img id="solIcon" src="solidity.svg">
<h1>Solidity realtime<br/>compiler and runtime</h1>
<div class="info">
<p>Version: <span id="version">(loading)</span><br/>
Change to: <select id="versionSelector"></select><br/>
Execution environment does not connect to any node, everyhing is local and in memory only.<br/>
<code>tx.origin = <span id="txorigin"/></code></p>
</div>
<div id="optimizeBox">
<input id="editorWrap" type="checkbox"><label for="editorWrap">Text Wrap</label>
<input id="optimize" type="checkbox"><label for="optimize">Enable Optimization</label>
</div>
</div>
<div id="output"></div>
9 years ago
</div>
<script>
// ----------------- editor ----------------------
var SOL_CACHE_FILE = "Untitled"
9 years ago
var SOL_CACHE_FILES_KEY = "sol-cache-files";
var editor = ace.edit("input");
var session = editor.getSession();
var Range = ace.require('ace/range').Range;
var errMarkerId = null;
9 years ago
var solFiles = JSON.parse(window.localStorage.getItem(SOL_CACHE_FILES_KEY)) || [SOL_CACHE_FILE];
if (solFiles.length === 0) solFiles = [SOL_CACHE_FILE];
9 years ago
var solCache = window.localStorage.getItem(SOL_CACHE_FILE) || BALLOT_EXAMPLE;
window.localStorage.setItem(SOL_CACHE_FILE, solCache)
if (window.localStorage.getItem('sol-cache')) {
// Backwards-compatibility
var count = '';
while (window.localStorage.getItem('Untitled' + count))
count = (count - 0) + 1;
window.localStorage.setItem('Untitled' + count, window.localStorage.getItem('sol-cache'));
window.localStorage.removeItem('sol-cache');
solFiles.push('Untitled' + count);
}
9 years ago
window.localStorage.setItem(SOL_CACHE_FILES_KEY, JSON.stringify(solFiles));
9 years ago
editor.setValue(solCache, -1);
9 years ago
session.setMode("ace/mode/javascript");
9 years ago
session.setTabSize(4);
session.setUseSoftTabs(true);
9 years ago
// ----------------- file selector-------------
var count = 0;
var $filesEl = $('#files');
9 years ago
$filesEl.on('click','.newFile', function() {
count++;
9 years ago
var name = 'Unititled' + count;
solFiles.push(name);
SOL_CACHE_FILE = name;
9 years ago
window.localStorage.setItem(SOL_CACHE_FILES_KEY, JSON.stringify(solFiles));
window.localStorage.setItem(SOL_CACHE_FILE, '');
updateFiles();
9 years ago
})
9 years ago
$filesEl.on('click', '.file:not(.active)', showFileHandler);
9 years ago
9 years ago
$filesEl.on('click', '.file.active', function(ev) {
var $fileTabEl = $(this);
var originalName = $fileTabEl.find('.name').text();
ev.preventDefault();
9 years ago
if ($(this).find('input').length > 0) return false;
var $fileNameInputEl = $('<input value="'+originalName+'"/>');
9 years ago
$fileTabEl.html($fileNameInputEl);
$fileNameInputEl.focus();
$fileNameInputEl.select();
9 years ago
$fileNameInputEl.on('blur', handleRename);
$fileNameInputEl.keyup(handleRename);
9 years ago
function handleRename(ev) {
ev.preventDefault();
if (ev.which && ev.which !== 13) return false;
var newName = ev.target.value;
$fileNameInputEl.off('blur');
$fileNameInputEl.off('keyup');
9 years ago
if (newName !== originalName && confirm("Are you sure you want to rename: " + originalName + " to " + newName + '?')) {
9 years ago
solFiles.splice(solFiles.indexOf(originalName), 1, newName);
window.localStorage.setItem(newName, window.localStorage.getItem(originalName));
window.localStorage.setItem(SOL_CACHE_FILES_KEY, JSON.stringify(solFiles));
window.localStorage.removeItem(originalName);
SOL_CACHE_FILE = newName;
}
9 years ago
updateFiles();
return false;
9 years ago
}
return false;
})
9 years ago
$filesEl.on('click', '.file .remove', function(ev) {
ev.preventDefault();
9 years ago
var name = $(this).parent().find('.name').text();
9 years ago
if (confirm("Are you sure you want to remove: " + name + " from local storage?")) {
var index = solFiles.indexOf(name);
9 years ago
solFiles.splice(index, 1);
window.localStorage.setItem(SOL_CACHE_FILES_KEY, JSON.stringify(solFiles));
SOL_CACHE_FILE = solFiles[ Math.max(0, index - 1)];
window.localStorage.removeItem(name);
updateFiles();
}
return false;
});
9 years ago
9 years ago
function showFileHandler(ev) {
ev.preventDefault();
SOL_CACHE_FILE = $(this).find('.name').text();
9 years ago
(updateFiles();
return false;
9 years ago
}
function fileTabFromName(name) {
9 years ago
return $('#files .file').filter(function(){ return $(this).find('.name').text() == name; });
9 years ago
}
9 years ago
function updateFiles() {
$filesEl.find('.file').remove();
for (var f in solFiles) {
9 years ago
if (solFiles[f]) $filesEl.append(fileTabTemplate(solFiles[f]));
}
var active = fileTabFromName(SOL_CACHE_FILE);
9 years ago
active.addClass('active');
editor.setValue(window.localStorage.getItem(SOL_CACHE_FILE) || '', -1);
$('#input').toggle(typeof SOL_CACHE_FILE === 'string');
9 years ago
}
9 years ago
function fileTabTemplate(name) {
return $('<span class="file"><span class="name">'+name+'</span><span class="remove">x</span></span>');
9 years ago
}
9 years ago
SOL_CACHE_FILE = solFiles[0];
9 years ago
updateFiles();
// ----------------- version selector-------------
// var soljsonSources is provided by bin/list.js
$('option', '#versionSelector').remove();
$.each(soljsonSources, function(i, file) {
if (file) {
var version = file.replace(/soljson-(.*).js/, "$1");
$('#versionSelector').append(new Option(version, file));
}
});
$('#versionSelector').change(function() {
Module = null;
compileJSON = null;
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'bin/' + $('#versionSelector').val();
$('head').append(script);
onCompilerLoaded();
});
// ----------------- resizeable ui ---------------
var EDITOR_SIZE_CACHE_KEY = "editor-size-cache";
var dragging = false;
$('#dragbar').mousedown(function(e){
e.preventDefault();
dragging = true;
var main = $('#righthand-panel');
var ghostbar = $('<div id="ghostbar">', {
css: {
top: main.offset().top,
left: main.offset().left
}
}).prependTo('body');
$(document).mousemove(function(e){
9 years ago
ghostbar.css("left",e.pageX+2);
});
});
var $body = $('body');
function setEditorSize (delta) {
$('#righthand-panel').css("width", delta);
$('#editor').css("right", delta);
onResize();
}
9 years ago
$(document).mouseup(function(e){
if (dragging) {
var delta = $body.width() - e.pageX+2;
$('#ghostbar').remove();
$(document).unbind('mousemove');
dragging = false;
9 years ago
setEditorSize(delta)
window.localStorage.setItem(EDITOR_SIZE_CACHE_KEY, delta);
}
});
// set cached defaults
9 years ago
var cachedSize = window.localStorage.getItem(EDITOR_SIZE_CACHE_KEY);
if (cachedSize) setEditorSize(cachedSize);
// ----------------- editor resize ---------------
function onResize() {
editor.resize();
session.setUseWrapMode(document.querySelector('#editorWrap').checked);
if(session.getUseWrapMode()) {
var characterWidth = editor.renderer.characterWidth;
var contentWidth = editor.container.ownerDocument.getElementsByClassName("ace_scroller")[0].clientWidth;
if(contentWidth > 0) {
session.setWrapLimit(parseInt(contentWidth / characterWidth, 10));
}
}
}
window.onresize = onResize;
onResize();
9 years ago
document.querySelector('#editor').addEventListener('change', onResize);
// ----------------- compiler ----------------------
var compileJSON;
var previousInput = '';
var sourceAnnotations = [];
var compile = function() {
editor.getSession().clearAnnotations();
sourceAnnotations = [];
editor.getSession().removeMarker(errMarkerId);
$('#output').empty();
var input = editor.getValue();
9 years ago
window.localStorage.setItem(SOL_CACHE_FILE, input);
9 years ago
var inputIncludingImports = includeLocalImports(input);
var optimize = document.querySelector('#optimize').checked;
try {
var data = $.parseJSON(compileJSON(inputIncludingImports, optimize ? 1 : 0));
} catch (exception) {
renderError("Uncaught JavaScript Exception:\n" + exception);
return;
}
if (data['error'] !== undefined)
renderError(data['error']);
if (data['errors'] != undefined)
$.each(data['errors'], function(i, err) {
renderError(err);
});
else
renderContracts(data, input);
}
var compileTimeout = null;
var onChange = function() {
var input = editor.getValue();
if (input === "") {
9 years ago
window.localStorage.setItem(SOL_CACHE_FILE, '')
return;
}
if (input === previousInput)
return;
previousInput = input;
if (compileTimeout) window.clearTimeout(compileTimeout);
compileTimeout = window.setTimeout(compile, 300);
};
var onCompilerLoaded = function() {
compileJSON = Module.cwrap("compileJSON", "string", ["string", "number"]);
$('#version').text(Module.cwrap("version", "string", [])());
previousInput = '';
onChange();
};
9 years ago
function includeLocalImports(input) {
var importRegex = /import\s[\'\"]([^\'\"]+)[\'\"];/g
var imports = [];
var matches = [];
var match;
while ((match = importRegex.exec(input)) !== null) {
if (match[1] && solFiles.indexOf(match[1]) !== -1) {
9 years ago
imports.push(match[1])
matches.push(match[0])
}
}
for (var i in imports) {
9 years ago
imported = includeLocalImports(window.localStorage.getItem(imports[i]))
input = input.replace(matches[i], imported);
}
return input;
}
if (Module)
onCompilerLoaded();
editor.getSession().on('change', onChange);
document.querySelector('#optimize').addEventListener('change', compile);
// ----------------- compiler output renderer ----------------------
var detailsOpen = {};
var renderError = function(message) {
$('#output')
.append($('<pre class="error"></pre>').text(message));
var err = message.match(/^:([0-9]*):([0-9]*)/)
if (err && err.length) {
9 years ago
var errLine = parseInt(err[1], 10) - 1;
var errCol = err[2] ? parseInt(err[2], 10) : 0;
sourceAnnotations[sourceAnnotations.length] ={
row: errLine,
column: errCol,
text: message,
type: "error"
};
editor.getSession().setAnnotations(sourceAnnotations);
}
};
var gethDeploy = function(contractName, interface, bytecode){
var code = "";
var funABI = getConstructorInterface($.parseJSON(interface));
$.each(funABI.inputs, function(i, inp) {
9 years ago
code += "var " + inp.name + " = /* var of type " + inp.type + " here */ ;\n";
});
9 years ago
code += "\nvar " + contractName + "Contract = web3.eth.contract(" + interface.replace("\n","") + ");"
+"\nvar " + contractName + " = " + contractName + "Contract.new(";
$.each(funABI.inputs, function(i, inp) {
9 years ago
code += "\n " + inp.name + ",";
});
code += "\n {"+
"\n from: web3.eth.accounts[0], "+
"\n data: '"+bytecode+"', "+
"\n gas: 3000000"+
"\n }, function(e, contract){"+
"\n if (typeof contract.address != 'undefined') {"+
"\n console.log(e, contract);"+
"\n console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);" +
"\n }" +
"\n })";
return code;
};
var combined = function(contractName, interface, bytecode){
9 years ago
return JSON.stringify([{name: contractName, interface: interface, bytecode: bytecode}]);
};
var renderContracts = function(data, source) {
$('#output').empty();
for (var contractName in data.contracts) {
var contract = data.contracts[contractName];
var title = $('<h3 class="title"/>').text(contractName);
var contractOutput = $('<div class="contractOutput"/>')
.append(title);
var body = $('<div class="body" />')
9 years ago
contractOutput.append(body);
if (contract.bytecode.length > 0)
title.append($('<div class="size"/>').text((contract.bytecode.length / 2) + ' bytes'))
body.append(getExecuteInterface(contract, contractName))
.append(tableRow('Bytecode', contract.bytecode));
body.append(tableRow('Interface', contract['interface']))
.append(textRow('Web3 deploy', gethDeploy(contractName.toLowerCase(),contract['interface'],contract.bytecode), 'deploy'))
.append(textRow('uDApp', combined(contractName,contract['interface'],contract.bytecode), 'deploy'))
.append(getDetails(contract, source, contractName));
$('#output').append(contractOutput);
title.click(function(ev){ $(this).parent().toggleClass('hide') });
}
9 years ago
$('.col2 input,textarea').click(function() { this.select(); });
};
var tableRowItems = function(first, second, cls) {
return $('<div class="row"/>')
.addClass(cls)
.append($('<div class="col1">').append(first))
.append($('<div class="col2">').append(second));
};
var tableRow = function(description, data) {
return tableRowItems(
$('<span/>').text(description),
$('<input readonly="readonly"/>').val(data));
};
var textRow = function(description, data, cls) {
return tableRowItems(
$('<strong/>').text(description),
$('<textarea readonly="readonly" class="gethDeployText"/>').val(data),
cls);
};
var getDetails = function(contract, source, contractName) {
var button = $('<button>Details</button>');
var details = $('<div style="display: none;"/>')
.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($('<span class="col1">Functions</span>'));
details.append($('<pre/>').text(funHashes));
details.append($('<span class="col1">Gas Estimates</span>'));
details.append($('<pre/>').text(formatGasEstimates(contract.gasEstimates)));
if (contract.runtimeBytecode && contract.runtimeBytecode.length > 0)
details.append(tableRow('Runtime Bytecode', contract.runtimeBytecode));
if (contract.assembly !== null)
{
details.append($('<span class="col1">Assembly</span>'));
var assembly = $('<pre/>').text(formatAssemblyText(contract.assembly, '', source));
details.append(assembly);
}
button.click(function() { detailsOpen[contractName] = !detailsOpen[contractName]; details.toggle(); });
if (detailsOpen[contractName])
details.show();
return $('<div/>').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;
};
9 years ago
$('.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 = $('<input/>').attr('placeholder', inputs);
var outputSpan = $('<div class="output"/>');
var button = $('<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 = $('<div class="contractProperty"/>')
.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 = $('<div/>');
var funABI = getConstructorInterface(abi);
var appendFunctions = function(address) {
var instance = $('<div class="contractInstance"/>');
9 years ago
var title = $('<span class="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;
};
</script>
9 years ago
</body>
</html>