Merge pull request #38 from chriseth/libraries

Libraries
pull/1/head
chriseth 9 years ago
commit 083df317a0
  1. 32
      index.html
  2. 114
      libs/universal-dapp.js
  3. 12
      stylesheets/browser-solidity.css
  4. 1
      stylesheets/universal-dapp.css

@ -500,34 +500,44 @@ THE SOFTWARE.
};
var renderContracts = function(data, source) {
var udappContracts = [];
for (var contractName in data.contracts) {
var contract = data.contracts[contractName];
var dapp = new UniversalDApp([{
udappContracts.push({
name: contractName,
interface: contract['interface'],
bytecode: contract.bytecode
}], {
});
}
var dapp = new UniversalDApp(udappContracts, {
vm: executionContext === 'vm',
removable: false,
removable_instances: true
});
var $contractOutput = dapp.render();
$contractOutput
removable_instances: true,
renderOutputModifier: function(contractName, $contractOutput) {
var contract = data.contracts[contractName];
return $contractOutput
.append(textRow('Bytecode', contract.bytecode))
.append(textRow('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));
}});
var $contractOutput = dapp.render();
if (executionContext === 'vm') $('#txorigin').text('0x' + dapp.address.toString('hex'));
else web3.eth.getAccounts( function(err,accounts) {
if (err) renderError(err.message);
if (executionContext === 'vm')
$('#txorigin').text('0x' + dapp.address.toString('hex'));
else
web3.eth.getAccounts(function(err, accounts) {
if (err)
renderError(err.message);
if (accounts && accounts[0])
$('#txorigin').text(accounts[0]);
else
$('#txorigin').text('unknown');
});
$contractOutput.find('.title').click(function(ev){ $(this).closest('.udapp').toggleClass('hide') });
$contractOutput.find('.title').click(function(ev){ $(this).closest('.contract').toggleClass('hide'); });
$('#output').append( $contractOutput );
}
$('.col2 input,textarea').click(function() { this.select(); });
};
var tableRowItems = function(first, second, cls) {

@ -2,6 +2,7 @@ function UniversalDApp (contracts, options) {
this.options = options || {};
this.$el = $('<div class="udapp" />');
this.contracts = contracts;
this.renderOutputModifier = options.renderOutputModifier || function(name, content) { return content; };
if (!options.vm && web3.currentProvider) {
@ -44,7 +45,7 @@ UniversalDApp.prototype.render = function () {
}
$contractEl.append( $title ).append( this.getCreateInterface( $contractEl, this.contracts[c]) );
}
this.$el.append( $contractEl );
this.$el.append(this.renderOutputModifier(this.contracts[c].name, $contractEl));
}
}
$legend = $('<div class="legend" />')
@ -59,10 +60,17 @@ UniversalDApp.prototype.render = function () {
return this.$el;
}
UniversalDApp.prototype.getContractByName = function(contractName) {
for (var c in this.contracts)
if (this.contracts[c].name == contractName)
return this.contracts[c];
return null;
};
UniversalDApp.prototype.getABIInputForm = function (cb){
var self = this;
var $el = $('<div class="udapp-setup" />');
var $jsonInput = $('<textarea class="json" placeholder=\'[ { "name": name, "bytecode": bytyecode, "interface": abi }, { ... } ]\'/>')
var $jsonInput = $('<textarea class="json" placeholder=\'[ { "name": name, "bytecode": bytecode, "interface": abi }, { ... } ]\'/>')
var $createButton = $('<button class="udapp-create"/>').text('Create a Universal ÐApp')
$createButton.click(function(ev){
var contracts = $.parseJSON( $jsonInput.val() );
@ -157,6 +165,7 @@ UniversalDApp.prototype.getInstanceInterface = function (contract, address, $tar
if (!address || !$target) {
$createInterface.append( this.getCallButton({
abi: funABI,
contractName: contract.name,
bytecode: contract.bytecode,
appendFunctions: appendFunctions
}));
@ -179,7 +188,7 @@ UniversalDApp.prototype.getConstructorInterface = function(abi) {
UniversalDApp.prototype.getCallButton = function(args) {
var self = this;
// args.abi, args.bytecode [constr only], args.address [fun only]
// args.abi, args.contractName [constr only], args.bytecode, args.address [fun only]
// args.appendFunctions [constr only]
var isConstructor = args.bytecode !== undefined;
var lookupOnly = ( args.abi.constant && !isConstructor );
@ -212,40 +221,65 @@ UniversalDApp.prototype.getCallButton = function(args) {
}
var getOutput = function() {
var values = Array.prototype.slice.call(arguments);
var $result = $('<div class="result" />');
var $close = $('<div class="udapp-close" />');
$close.click( function(){ $result.remove(); } );
$result.append( $close );
for( var v in values ) { $result.append( values[v] ); }
return $result;
}
};
var clearOutput = function($result) {
$(':not(.udapp-close)', $result).remove();
};
var replaceOutput = function($result, message) {
clearOutput($result);
$result.append(message);
};
var handleCallButtonClick = function( ev ) {
var handleCallButtonClick = function(ev, $result) {
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);
var $result = getOutput( $('<a class="waiting" href="#" title="Waiting for transaction to be mined.">Polling for tx receipt...</a>') );
if (lookupOnly && !inputs.length) {
if (!$result) {
$result = getOutput();
if (lookupOnly && !inputs.length)
$outputOverride.empty().append( $result );
} else {
else
outputSpan.append( $result );
}
replaceOutput($result, $('<span>Waiting for transaction to be mined...</span>'));
if (isConstructor) {
if (args.bytecode.indexOf('_') >= 0) {
replaceOutput($result, $('<span>Deploying and linking required libraries...</span>'));
if (self.options.vm)
self.linkBytecode(args.contractName, function(err, bytecode) {
if (err)
replaceOutput($result, $('<span/>').text('Error deploying required libraries: ' + err));
else {
args.bytecode = bytecode;
handleCallButtonClick(ev, $result);
}
});
else
replaceOutput($result, $('<span>Contract needs to be linked to a library, this is only supported in the JavaScript VM for now.</span>'));
return;
} else
data = args.bytecode + data.slice(8);
}
self.runTx(data, args, function(err, result) {
if (err) {
$result.replaceWith( getOutput( $('<span/>').text(err).addClass('error') ) );
replaceOutput($result, $('<span/>').text(err).addClass('error'));
} else if (self.options.vm && isConstructor) {
$result.replaceWith( getOutput( getGasUsedOutput( result ) ) );
replaceOutput($result, getGasUsedOutput(result));
args.appendFunctions(result.createdAddress);
} else if (self.options.vm){
var outputObj = fun.unpackOutput('0x' + result.vm.return.toString('hex'));
$result.replaceWith( getOutput( getReturnOutput( outputObj ), getGasUsedOutput( result.vm ) ) );
clearOutput($result);
$result.append(getReturnOutput(outputObj)).append(getGasUsedOutput(result.vm));
} else if (args.abi.constant && !isConstructor) {
$result.replaceWith( getOutput( getReturnOutput( result ) ) );
replaceOutput($result, getReturnOutput(result));
} else {
function tryTillResponse (txhash, done) {
@ -262,7 +296,10 @@ UniversalDApp.prototype.getCallButton = function(args) {
if (isConstructor) {
$result.html('');
args.appendFunctions(result.contractAddress);
} else $result.replaceWith( getOutput( getReturnOutput( result ), getGasUsedOutput( result ) ) );
} else {
clearOutput($result);
$result.append(getReturnOutput(result)).append(getGasUsedOutput(result));
}
})
}
@ -289,6 +326,49 @@ UniversalDApp.prototype.getCallButton = function(args) {
return $contractProperty.append(outputSpan);
}
UniversalDApp.prototype.linkBytecode = function(contractName, cb) {
var bytecode = this.getContractByName(contractName).bytecode;
if (bytecode.indexOf('_') < 0)
return cb(null, bytecode);
var m = bytecode.match(/__([^_]{1,36})__/);
if (!m)
return cb("Invalid bytecode format.");
var libraryName = m[1];
if (!this.getContractByName(contractName))
return cb("Library " + libraryName + " not found.");
var self = this;
this.deployLibrary(libraryName, function(err, address) {
if (err) return cb(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);
self.getContractByName(contractName).bytecode = bytecode;
self.linkBytecode(contractName, cb);
});
};
UniversalDApp.prototype.deployLibrary = function(contractName, cb) {
if (this.getContractByName(contractName).address)
return cb(null, this.getContractByName(contractName).address);
var self = this;
var bytecode = this.getContractByName(contractName).bytecode;
if (bytecode.indexOf('_') >= 0)
this.linkBytecode(contractName, function(err, bytecode) {
if (err) cb(err);
else self.deployLibrary(contractName, cb);
});
else {
this.runTx(bytecode, {abi: {constant: false}, bytecode: bytecode}, function(err, result) {
if (err) return cb(err);
self.getContractByName(contractName).address = result.createdAddress;
cb(err, result.createdAddress);
});
}
};
UniversalDApp.prototype.clickNewContract = function ( self, $contract, contract ) {
$contract.append( self.getInstanceInterface(contract) );
}

@ -126,19 +126,15 @@ body {
float: left;
}
.udapp.hide {
.contract.hide {
padding-bottom: 0;
}
.udapp.hide > *:not(.contract) {
display: none;
}
.udapp.hide .contract {
.contract.hide {
margin: 0;
}
.udapp.hide .contract > *:not(.title) {
.contract.hide > *:not(.title) {
display: none;
}
@ -153,7 +149,7 @@ body {
font-size: 10px;
}
.udapp.hide > .contract > .title:before {
.contract.hide > .title:before {
content: "\25B6";
}

@ -43,6 +43,7 @@
.udapp .contract {
margin-bottom: 1em;
clear: both;
}
.udapp .create {

Loading…
Cancel
Save