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

677 lines
22 KiB

9 years ago
<html>
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
<head>
<meta charset="utf-8">
<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">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<style type="text/css">
body {
padding: 0px;
font-size: 12px;
color: #111111;
}
#editor {
9 years ago
position: absolute;
top: 0;
left: 0px;
width: auto;
bottom: 0px;
right: 37em;
9 years ago
}
#input {
font-size: 15px;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
min-width: 20vw;
9 years ago
}
#righthand-panel {
9 years ago
position: absolute;
top: 0;
width: 37em;
max-width: 80vw;
right: 0;
9 years ago
bottom: 0px;
overflow: auto;
border-left: 1px dotted black;
box-sizing: border-box;
9 years ago
}
9 years ago
#output {
border-top: 1px dotted black;
display: block;
9 years ago
}
#header {
font-size: 14px;
padding: 1em;
font-size: 12px;
}
#header h1 {
margin-top: 0;
}
#header .info { clear: left; }
#header #solIcon {
float: left;
height: 5em;
}
.col1 {
width: 30%;
float: left;
}
.col2 {
width: 70%;
float: left;
}
.row {
overflow: auto;
}
9 years ago
.gethDeployText {
border-color: #bebebe;
height: 2.5em;
width: 100%;
display: block;
}
9 years ago
.contractInstance {
background-color: #ccc;
margin-bottom: 1em;
padding: 0.6em;
9 years ago
}
.contractInstance.hide > *:not(.title) {
display: none;
}
.contractInstance .contractProperty input,
.contractInstance .contractProperty button {
text-align: left;
padding: 0.3em;
width: 50%;
margin: 0;
}
.contractOutput .contractInstance .contractProperty {
margin-bottom: 0;
}
.contractOutput .contractInstance .contractProperty .output {
padding: 0.4em;
background-color: #333;
color: white;
margin-bottom: 1em;
display: block;
}
.contractInstance .contractProperty .output:empty { display: none; }
.contractOutput {
border-bottom: 1px dotted black;
padding: 0.6em;
box-sizing: border-box;
}
.contractOutput .contractProperty {
margin-bottom: 0.6em;
}
.contractOutput .contractProperty .output {
display: inline;
}
.contractOutput .body {
margin-top: 10px;
}
.contractOutput.hide {
background-color: #4C4C67;
color: white;
}
.contractOutput.hide > *:not(.title) {
display: none;
}
.title {
margin: 0;
cursor: pointer;
font-family: monospace;
font-weight: bold;
}
.title:before {
content: "\25BC";
opacity: 0.5;
margin-right: 0.4em;
font-size: 10px;
}
.hide > .title:before {
content: "\25B6";
}
.contractOutput > .title {
border-bottom: #4C4C67;
}
.title .size {
font-weight: normal;
float: right;
}
.solError {
position: absolute;
background-color: rgba(255, 0, 0, 0.2);
z-index:40;
}
.error {
background-color: rgba(255, 0, 0, 0.5);
border-radius: 0;
word-wrap: break-word;
border: 1px solid #D00909;
}
#ghostbar {
width: 1px;
background-color: red;
opacity: 0.5;
position: absolute;
cursor: col-resize;
z-index: 9999;
top: 0;
bottom: 0;
}
#dragbar{
background-color: transparent;
position: absolute;
width: 5px;
left: 0;
top: 0;
bottom: 0;
cursor: col-resize;
z-index: 999;
}
input[readonly] {
padding: .4em;
border: 1px solid #ccc;
box-sizing: border-box;
display: block;
width: 100%;
}
9 years ago
</style>
<script src="libs/jquery-2.1.3.min.js"></script>
9 years ago
<script src="libs/ace.js"></script>
<script src="mode-solidity.js"></script>
<script src="soljson.js"></script>
9 years ago
<script src="ethereumjs-vm.js"></script>
<script src="web3.min.js"></script>
<script src="ballot.sol.js"></script>
9 years ago
</head>
<body>
<div id="editor">
<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/>
Execution environment does not connect to any node, everyhing is in-memory only.<br/>
<strong>Note:</strong> If Chrome/Chromium reports &quot;Uncaught JavaScript Exception&quot;,
enable the debug console (Ctrl+Shift+i) and reload.</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>
</div>
<script>
// ----------------- editor ----------------------
var SOL_CACHE_KEY = "sol-cache";
var editor = ace.edit("input");
var session = editor.getSession();
var Range = ace.require('ace/range').Range;
var errMarkerId = null;
var solCache = window.localStorage.getItem( SOL_CACHE_KEY );
editor.setValue( solCache || BALLOT_EXAMPLE, 1 );
session.setMode("ace/mode/javascript");
session.setTabSize(4);
session.setUseSoftTabs(true);
// ----------------- 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){
ghostbar.css("left",e.pageX+2);
});
});
var $body = $('body');
function setEditorSize (delta) {
$('#righthand-panel').css("width", delta);
$('#editor').css("right", delta);
onResize();
}
$(document).mouseup(function(e){
if (dragging) {
var delta = $body.width() - e.pageX+2;
$('#ghostbar').remove();
$(document).unbind('mousemove');
dragging = false;
setEditorSize( delta )
window.localStorage.setItem( EDITOR_SIZE_CACHE_KEY, delta );
}
});
// set cached defaults
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();
document.querySelector('#editor').addEventListener('change', onResize );
// ----------------- compiler ----------------------
var compileJSON = Module.cwrap("compileJSON", "string", ["string", "number"]);
$('#version').text(Module.cwrap("version", "string", [])());
var previousInput = '';
var compile = function() {
editor.getSession().clearAnnotations();
editor.getSession().removeMarker(errMarkerId);
var input = editor.getValue();
var optimize = document.querySelector('#optimize').checked;
try {
var data = $.parseJSON(compileJSON(input, optimize ? 1 : 0));
} catch (exception) {
renderError("Uncaught JavaScript Exception:\n" + exception);
return;
}
if (data['error'] !== undefined)
renderError(data['error']);
else
renderContracts(data, input);
}
var compileTimeout = null;
var onChange = function() {
var input = editor.getValue();
if (input === "") {
window.localStorage.setItem( SOL_CACHE_KEY, '' )
return;
}
if (input === previousInput)
return;
previousInput = input;
if (compileTimeout) window.clearTimeout(compileTimeout);
compileTimeout = window.setTimeout(compile, 300);
};
onChange();
editor.getSession().on('change', onChange);
document.querySelector('#optimize').addEventListener('change', compile);
// ----------------- compiler output renderer ----------------------
var detailsOpen = {};
var renderError = function(message) {
var $output = $('#output').empty()
var err = message.match(/^:([0-9]*):([0-9]*)/)
if (err && err.length) {
var errLine = parseInt( err[1], 10 ) - 1;
var errCol = err[2] ? parseInt( err[2], 10 ) : 0;
$output.append($('<pre class="error"></pre>').text(message));
editor.getSession().setAnnotations([{
row: errLine,
column: errCol,
text: message,
type: "error"
}]);
}
};
var gethDeploy = function(contractName, interface, bytecode){
var code = "";
var funABI = getConstructorInterface($.parseJSON(interface));
$.each(funABI.inputs, function(i, inp) {
code += "var "+inp.name+" = /* var of type " + inp.type + " here */ ;\n";
});
code += "\nvar "+contractName+"Contract = web3.eth.contract("+interface.replace("\n","")+");"
+"\nvar "+contractName+" = "+contractName+"Contract.new(";
$.each(funABI.inputs, function(i, inp) {
code += "\n "+inp.name+",";
});
code += "\n {"+
"\n from: web3.eth.accounts[0], "+
"\n data: '"+bytecode+"', "+
"\n gas: 1000000"+
"\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){
return JSON.stringify( [{name: contractName, interface: interface, bytecode: bytecode}]);
};
var renderContracts = function(data, source) {
window.localStorage.setItem( SOL_CACHE_KEY, 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" />')
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') });
}
$('.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.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;
};
$('.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'));
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: '3000000',
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"/>');
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>