Last active
December 23, 2016 09:33
-
-
Save vkobel/dd6b12cf9d4e82439227936090751ee2 to your computer and use it in GitHub Desktop.
Simple Ethereum contract to act as a shop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> | |
<script src="./web3.min.js"></script> | |
<title>Web3 test</title> | |
<meta charset='utf-8'> | |
</head> | |
<body> | |
<div id="main"> | |
<div id="coinbase-val"></div> | |
<div id="balance">Balance: <span id="balance-val"></span></div> | |
<ul id="items"></ul> | |
</div> | |
<script> | |
window.addEventListener('load', function() { | |
// Checking if Web3 has been injected by the browser (Mist/MetaMask) | |
if (typeof web3 !== 'undefined') { | |
// Use Mist/MetaMask's provider | |
web3 = new Web3(web3.currentProvider); | |
} else { | |
console.log('No web3? You should consider trying MetaMask!') | |
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail) | |
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); | |
} | |
var kobyshop = web3.eth.contract([{"constant":false,"inputs":[],"name":"withdraw","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_itemId","type":"bytes32"}],"name":"details","outputs":[{"name":"","type":"string"},{"name":"","type":"uint256"},{"name":"","type":"address"},{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_itemId","type":"bytes32"}],"name":"buy","outputs":[],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_price","type":"uint256"}],"name":"sell","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"itemId","type":"bytes32"},{"indexed":false,"name":"seller","type":"address"}],"name":"NewSale","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"itemId","type":"bytes32"},{"indexed":false,"name":"buyer","type":"address"}],"name":"NewPurchase","type":"event"}]).at("0x1d7C28F5075c102daE1588fBFFbb303B04852b49"); | |
startApp(kobyshop); | |
$("#items").on("click", "li", function(){ | |
var itmId = $(this).data("item-id"); | |
var price = $(this).data("price"); | |
kobyshop.buy(itmId, {value: price, from: web3.eth.accounts[0]}, function(rep){ | |
console.log(rep) | |
}); | |
}); | |
}); | |
function startApp(ks){ | |
watchBalance(); | |
listItems(ks); | |
} | |
function listItems(ks){ | |
var ul = document.getElementById("items"); | |
ks.NewSale({}, {fromBlock:0, toBlock:'latest'}, function(err, log){ | |
ks.details(log.args.itemId, function(err, itm){ | |
if(itm[3] === true){ | |
ul.innerHTML += "<li data-price='"+ itm[1] +"' data-item-id='"+ log.args.itemId +"'>"+ itm[0] +" - <b>"+ web3.fromWei(itm[1], "ether") +" ETH</b><br><i>owner: "+ itm[2] +"</i></li>"; | |
} | |
}) | |
}); | |
} | |
function watchBalance(){ | |
var cb = web3.eth.coinbase; | |
var cb_div = document.getElementById("coinbase-val"); | |
var bal_div = document.getElementById("balance-val"); | |
cb_div.innerHTML = cb; | |
web3.eth.getBalance(cb, function(e, bal){ | |
bal_div.innerHTML = web3.fromWei(bal, "ether").toNumber(); | |
}); | |
web3.eth.filter('latest').watch(function() { | |
web3.eth.getBalance(cb, function(e, bal){ | |
bal_div.innerText = web3.fromWei(bal, "ether").toNumber(); | |
}); | |
}); | |
} | |
</script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.4.6; | |
contract KobyShop { | |
struct Item { | |
string name; | |
uint price; | |
bool forSale; | |
address owner; | |
bool initialized; | |
} | |
mapping(bytes32 => Item) private items; | |
mapping(address => uint) private pendingWithdrawals; | |
event NewSale(bytes32 itemId, address seller); | |
event NewPurchase(bytes32 itemId, address buyer); | |
function sell(string _name, uint _price){ | |
var itemId = keccak256(_name); | |
// if item already exists we just update it if same owner | |
if(items[itemId].initialized){ | |
if(items[itemId].owner != msg.sender) throw; | |
items[itemId].price = _price; | |
items[itemId].forSale = true; | |
}else{ | |
items[itemId] = Item({ | |
name: _name, | |
price: _price, | |
forSale: true, | |
owner: msg.sender, | |
initialized: true | |
}); | |
} | |
NewSale(itemId, msg.sender); | |
} | |
function buy(bytes32 _itemId) payable { | |
var itm = items[_itemId]; | |
if(!itm.initialized) throw; | |
if(!itm.forSale) throw; | |
if(msg.value < itm.price) throw; | |
pendingWithdrawals[itm.owner] += itm.price; | |
itm.owner = msg.sender; | |
itm.forSale = false; | |
// Send any excess back | |
if (msg.value > itm.price){ | |
if(!msg.sender.send(msg.value - itm.price)){ | |
throw; | |
} | |
} | |
NewPurchase(_itemId, msg.sender); | |
} | |
function details(bytes32 _itemId) constant returns (string, uint, address, bool){ | |
return ( | |
items[_itemId].name, | |
items[_itemId].price, | |
items[_itemId].owner, | |
items[_itemId].forSale | |
); | |
} | |
function withdraw() returns (bool) { | |
uint amount = pendingWithdrawals[msg.sender]; | |
// Remember to zero the pending refund before | |
// sending to prevent re-entrancy attacks | |
pendingWithdrawals[msg.sender] = 0; | |
if (msg.sender.send(amount)) { | |
return true; | |
} else { | |
pendingWithdrawals[msg.sender] = amount; | |
return false; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment