Skip to content

Instantly share code, notes, and snippets.

@vkobel
Last active December 23, 2016 09:33
Show Gist options
  • Save vkobel/dd6b12cf9d4e82439227936090751ee2 to your computer and use it in GitHub Desktop.
Save vkobel/dd6b12cf9d4e82439227936090751ee2 to your computer and use it in GitHub Desktop.
Simple Ethereum contract to act as a shop
<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>
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