Commit 6b071bec authored by didi's avatar didi
Browse files

extended demo frontend: more metadata, details, adapted to Metamask and...

extended demo frontend: more metadata, details, adapted to Metamask and Rinkeby, added option for discrete transfer
parent 82c4c3c6
// for contract metadata (format as produced by truffle)
function initContract() {
const abi = contract.abi;
// get address - in case of multiple network entries, the last seems to be a safe bet
for(let networkId in contract.networks) {
console.log(contract.networks[networkId])
var address = contract.networks[networkId].address
}
var Web3 = require('web3');
if (typeof web3 !== 'undefined') {
// Mist / Metamask
console.log('web3 connects to provider')
web3 = new Web3(web3.currentProvider);
} else {
// standalone
//alert("This Dapp needs web3 injected (e.g. through the Metamask plugin.");
console.log('web3 connects to rpc')
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"))
// TODO: use the async getAccounts() instead
web3.eth.defaultAccount = web3.eth.accounts[0]
}
// contract is the contract template based on our abi
const Streem = web3.eth.contract(abi);
streem = Streem.at(address);
return new Promise( (resolve, reject) => {
const abi = contract.abi;
// get address - in case of multiple network entries, the last seems to be a safe bet
/*
for(let networkId in contract.networks) {
//console.log(contract.networks[networkId])
var address = contract.networks[networkId].address
}
*/
var address = '0xf73f6bd052061bb84913be57d5f7565b0aa38827'
console.log(`contract loaded at ${address} for defaultAccount ${web3.eth.defaultAccount}`)
var Web3 = require('web3');
web3.eth.getBlockNumber((err, ret) => {
console.log("Block: " + ret);
});
// callback on block advance
web3.eth.filter('latest').watch((err, hash) => {
console.log(`new block: ${hash}`)
if (typeof onNextBlock == 'function') {
onNextBlock(hash)
if (typeof web3 !== 'undefined') {
// Mist / Metamask
console.log('web3 connects to provider')
web3 = new Web3(web3.currentProvider);
} else {
// standalone
//alert("This Dapp needs web3 injected (e.g. through the Metamask plugin.");
console.log('web3 connects to rpc')
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"))
}
web3.eth.getAccounts((err, ret) => {
if (!err) web3.eth.defaultAccount = ret[0]
if (web3.eth.defaultAccount == undefined) {
alert("no Ethereum account found")
} else {
console.log(`defaultAccount: ${web3.eth.defaultAccount}`)
// contract is the contract template based on our abi
const Streem = web3.eth.contract(abi);
streem = Streem.at(address);
streem.totalSupply( (err, ret) => {
if(err || ret == 0) {
alert(`Cannot communicate with contract at given address ${address}`)
} else {
console.log(`contract loaded at ${address}`)
web3.version.getNetwork( (err, ret) => {
resolve({ contractaddr: address, networkid: ret})
})
}
})
}
})
// callback on block advance
web3.eth.filter('latest').watch((err, hash) => {
console.log(`new block: ${hash}`)
if (typeof onNextBlock == 'function') {
onNextBlock(hash)
}
})
// callback on pending transactions
/*
// the address filter seems not to work
web3.eth.filter({address: web3.eth.defaultAccount}).watch((err, ret) => {
console.log(`filtered event: ${JSON.stringify(ret)}`)
getReceipt(ret.transactionHash)
})
*/
})
}
function onOpenStreamButton() {
console.log("start stream clicked")
console.log("open stream clicked")
const rcv = document.getElementById('receiver').value
const speed = document.getElementById('speed').value
console.log(`starting stream to ${rcv} with speed ${speed}`)
streem.openStream(rcv, speed, {gas: 200000}) // TODO: gas is just a guess to make it working on testrpc
console.log(`opening stream to ${rcv} with speed ${speed}`)
streem.openStream(rcv, speed, {gas: 200000}, txHandler) // TODO: gas is just a guess to make it working on testrpc
}
// TODO: first test locally if it can be executed
function onCloseStreamButton() {
console.log("stop stream clicked")
streem.closeStream({gas: 200000}) // TODO: gas is just a guess to make it working on testrpc
console.log("close stream clicked")
streem.closeStream({gas: 200000}, txHandler) // TODO: gas is just a guess to make it working on testrpc
}
function onTransferButton() {
console.log("transfer clicked")
const rcv = document.getElementById('transfer-receiver').value
const amount = document.getElementById('transfer-amount').value
streem.transfer(rcv, amount, {gas: 200000}, txHandler) // TODO: gas is just a guess to make it working on testrpc
}
// TODO: how to detect failure of a transaction? See https://ethereum.stackexchange.com/questions/6007/how-can-the-transaction-status-from-a-thrown-error-be-detected-when-gas-can-be-e
function txHandler(err, txHash) {
if(err) {
console.log(`transfer failed: ${err}`)
alert(`transaction failed: ${err}`)
} else {
console.log(`new pending transaction: ${txHash}`)
window.pendingTx = txHash
document.getElementById('pendingtx').innerHTML = `<a href="${network.explorer}/tx/${txHash}" target="_blank">${txHash}</a>`
function incrementCounter(startTime) {
window.setTimeout(() => {
document.getElementById('pendingtxcounter').innerHTML = Math.floor(Date.now() / 1000) - startTime
if(window.pendingTx) {
incrementCounter(startTime)
} else {
document.getElementById('pendingtxcounter').innerHTML = ''
}
}, 1000)
}
incrementCounter(Math.floor(Date.now() / 1000))
}
}
function getReceipt(txHash) {
console.log(`getReceipt for tx ${txHash}`)
web3.eth.getTransactionReceipt(txHash, (err, ret) => {
if(err) {
console.log(`getTransactionReceipt failed: ${err}`)
} else {
console.log(`receipt: ${JSON.stringify(ret)}`)
}
})
}
// shows the balance of <address> by calling the function <updateUi> with the balance as parameter
......@@ -62,18 +130,65 @@ function showBalance(address, updateUi) {
streem.balanceOf(address, (err, ret) => {
window.balance = ret
const bal = web3.toDecimal(ret)
console.log(`balance is ${bal}`)
updateUi(bal)
})
}
// callback for new block event
function onNextBlock(hash) {
showBalance(web3.eth.defaultAccount, (bal) => { document.getElementById('mybalance-result').innerHTML = bal } )
function onNextBlock() {
web3.eth.getBlockNumber((err, ret) => {
document.getElementById('blocknr').innerHTML = ret
})
web3.eth.getBalance(web3.eth.defaultAccount, (err, ret) => {
if(! err) {
window.balance = ret
const balWei = web3.fromWei(web3.toDecimal(ret))
document.getElementById('etherbalance').innerHTML = balWei
} else {
alert("Something went wront: can't read Ether balance. Blockchain node connected?")
}
})
showBalance(web3.eth.defaultAccount, (bal) => {
document.getElementById('streembalance').innerHTML = bal
} )
const receiverAddr = document.getElementById('receiver').value
if(receiverAddr != "") {
showBalance(receiverAddr, (bal) => { document.getElementById('somebalance-result').innerHTML = bal } )
// showBalance(receiverAddr, (bal) => { document.getElementById('somebalance-result').innerHTML = bal } )
}
streem.dev_inStream((err, ret) => {
const sender = ret[0], speed = web3.toDecimal(ret[1]), age = web3.toDecimal(ret[2])
document.getElementById('in-sender').innerHTML = speed != 0 ? `<a href="${network.explorer}/token/${contractaddr}?a=${sender}" target="_blank">${sender}</a>` : '-'
document.getElementById('in-speed').innerHTML = speed != 0 ? speed: '-'
document.getElementById('in-age').innerHTML = speed != 0 ? age : '-'
if(speed != 0) {
streem.balanceOf(sender, (err, ret) => {
const bal = web3.toDecimal(ret)
const runway = Math.floor(bal / speed)
document.getElementById(`in-runway`).innerHTML = runway
})
} else {
document.getElementById(`in-runway`).innerHTML = '-'
}
})
streem.dev_outStream((err, ret) => {
const receiver = ret[0], speed = web3.toDecimal(ret[1]), age = web3.toDecimal(ret[2])
document.getElementById('out-receiver').innerHTML = speed != 0 ? `<a href="${network.explorer}/token/${contractaddr}?a=${receiver}" target="_blank">${receiver}</a>` : '-'
document.getElementById('out-speed').innerHTML = speed != 0 ? speed: '-'
document.getElementById('out-age').innerHTML = speed != 0 ? age : '-'
if(speed != 0) {
streem.balanceOf(web3.eth.defaultAccount, (err, ret) => {
const bal = web3.toDecimal(ret)
const runway = Math.floor(bal / speed)
document.getElementById(`out-runway`).innerHTML = runway
})
} else {
document.getElementById(`out-runway`).innerHTML = '-'
}
})
}
// trigger block creation every <blocktime> seconds by transferring 1 wei (useful for testrpc).
......@@ -88,29 +203,48 @@ function keepBlockchainAlive(blocktime) {
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM loaded')
document.querySelector('#start-stream').onclick = onOpenStreamButton
document.querySelector('#stop-stream').onclick = onCloseStreamButton
document.querySelector('#open-stream').onclick = onOpenStreamButton
document.querySelector('#close-stream').onclick = onCloseStreamButton
document.querySelector('#transfer').onclick = onTransferButton
})
window.addEventListener('load', () => {
console.log('window loaded')
initContract()
if(web3.eth.defaultAccount == undefined) {
alert("no Ethereum account found")
}
web3.eth.getBalance(web3.eth.defaultAccount, (err, ret) => {
if(! err) {
window.balance = ret
console.log(`Ether balance is ${web3.fromWei(web3.toDecimal(ret))}`)
} else {
alert("Something went wront: can't read Ether balance. Blockchain node connected?")
}
initContract().then( ret => {
this.contractaddr = ret.contractaddr
const networks = {1: 'mainnet', 3: 'ropsten', 4: {name: 'rinkeby', explorer: 'http://rinkeby.etherscan.io'}}
this.network = networks[ret.networkid] ? networks[ret.networkid] : {name: 'unknown', explorer: ''}
console.log(`network-id: ${ret.networkid}`)
document.getElementById('network').innerHTML = `id ${ret.networkid} <a href="${network.explorer}" target="_blank">${network.name}</a>`
document.getElementById('contractaddr').innerHTML = `<a href="${network.explorer}/token/${contractaddr}" target="_blank">${contractaddr}</a>`
document.getElementById('ownaddr').innerHTML = `<a href="${network.explorer}/token/${contractaddr}?a=${web3.eth.defaultAccount}" target="_blank">${web3.eth.defaultAccount}</a>`
web3.version.getNode( (err, ret) => {
if(ret.toLocaleLowerCase().indexOf("testrpc") != -1) {
console.log('starting timer for dummy transaction in order to keep the chain going...')
keepBlockchainAlive(5)
}
})
streem.Transfer().watch( (err, ret) => {
console.log(`Transfer event: ${JSON.stringify(ret)}`)
})
streem.allEvents().watch( (err, ret) => {
console.log(`Event: ${JSON.stringify(ret)}`)
// example value of ret:
// {"address":"0xf73f6bd052061bb84913be57d5f7565b0aa38827","blockNumber":693587,"transactionHash":"0xf97f95ae04c98fb1be277ba6888e6bf8b77d360b1196f774c6e91cc9f8bba115","transactionIndex":0,"blockHash":"0x570a4102cdd5a9f30efbac1a8ff00f6054d4af4f9b092c1dda8d705904d8d957","logIndex":0,"removed":false,"event":"StreamOpened","args":{"_from":"0x1e8a58eaab8c001022921d60010ddeb57f01b674","_to":"0x54717fdd2d61dda38f60cf822c225fe65fc18e64","_perSecond":"2"}}
if(ret.transactionHash == window.pendingTx) {
document.getElementById('pendingtx').innerHTML = ''
window.pendingTx = null
document.getElementById('executedtx').innerHTML = `<a href="${network.explorer}/tx/${ret.transactionHash}" target="_blank">${ret.transactionHash}</a>`
}
})
// trigger once manually in order to init some of the UI fields
onNextBlock()
})
if(web3.version.node.toLocaleLowerCase().indexOf("testrpc") != -1) {
keepBlockchainAlive(5)
}
})
\ No newline at end of file
......@@ -6,18 +6,45 @@
</head>
<body>
<h3>Streem PoC Demo</h3>
<p>Works only in an Ethereum aware Browser (e.g. Mist, Browser with Metamask plugin).<br>
<h2>Streem PoC Demo</h2>
<p>Works only in an Ethereum aware Browser (e.g. Mist, Chromium/Chrome with <a href="http://metamask.io" target="_blank">Metamask</a> plugin).<br>
The connected Blockchain needs to have the Streem contract deployed and the file js/streem_contract.js needs to have the correct contract ABI and address.<br></p>
<hr>
<h3>General</h3>
Contract address: <span id="contractaddr">-</span><br>
My address: <span id="ownaddr">-</span><br>
Network: <span id="network">-</span><br>
Blockchain height: <span id="blocknr">-</span><br>
<br>
My Ether balance: <span id="etherbalance">-</span><br>
My Streem balance: <span id="streembalance">-</span><br>
Last pending transaction: <span id="pendingtx"></span> - <span id="pendingtxcounter"></span><br>
Last executed transaction: <span id="executedtx">-</span><br>
<hr>
<h3>Stream states</h3>
<b>Incoming Stream</b><br>
sender: <span id="in-sender">-</span> | speed (tokens/sec): <span id="in-speed">-</span> | age (sec): <span id="in-age">-</span> | funds left for (sec): <span id="in-runway">-</span><br>
<br>
<b>Outgoing Stream</b><br>
receiver: <span id="out-receiver">-</span> | speed (tokens/sec): <span id="out-speed">-</span> | age (sec): <span id="out-age">-</span> | funds left for (sec): <span id="out-runway">-</span><br>
<br>
Note that <i>funds left for</i> is calculated based on the current balance, ignoring expected future funds from open incoming streams (since they are not assured).<br>
<hr>
<h3>Outgoing Stream control</h3>
There can be only one open stream at a time. This interface is totally dumb, it's up to you to do things in the right order.<br>
Note that after issuing a command, it can take some time (up to ~30 seconds) for it to take effect.<br>
<br>
Receiver: <input id="receiver" type="text"/><br>
Speed (units per second): <input id="speed" type="number"/><br>
<button id="start-stream">Start Stream</button><br>
<button id="stop-stream">Stop Stream</button><br>
Speed (tokens per second): <input id="speed" type="number"/><br>
<button id="open-stream">Open Stream</button><br>
<button id="close-stream">Close Stream</button><br>
<hr>
My balance: <span id="mybalance-result">-</span><br>
Receiver balance: <span id="somebalance-result">-</span><br>
<p>(Automatically updated with every new block)</p>
<h3>Discrete transfer</h3>
Here you can do a <i>normal</i> token transfer. This can be done in parallel to an open stream.<br>
<br>
Receiver: <input id="transfer-receiver" type="text"/><br>
Amount: <input id="transfer-amount" type="text"/><br>
<button id="transfer">Transfer</button><br>
<script type="text/javascript" src="lib/web3.js"></script>
<script type="text/javascript" src="js/streem_contract.js"></script>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment