Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save computerphysicslab/9ac2c55017186683a8266da1f3c4f08b to your computer and use it in GitHub Desktop.
Save computerphysicslab/9ac2c55017186683a8266da1f3c4f08b to your computer and use it in GitHub Desktop.
/*
Watafan asset Smart Contract v3.0 tiny and splitted, part 2
developed by:
MarketPay.io , 2017
https://marketpay.io/
http://lnked.in/blockchain
v1.0 https://gist.github.com/computerphysicslab/93405f03880b7eb422013cdbbc3d493f
+ ERC-20 tokens
+ Mobile interface
+ Issuable assets and tradable with tokens
v2.0 https://gist.github.com/computerphysicslab/f362383f9d3fed26becba48b934bbcfc
+ onlyOwner modifier
+ Haltable
+ safeMath
+ Added tokenExchangeRate
+ onlyIssuer modifier
+ asset state machine
v3.0 tiny https://gist.github.com/computerphysicslab/7438c8dfdba705faeb7c41af0ae036cc
+ Removes extra functionalities to make it deployable:
* Mortal
* Haltable
* Mobile interface
* Asset pointers
* timestamp
* onlyOwner, replaced by checking against hardcoded owner address
* safeMath
* allocated status for tokens
* onlyIssuers, replaced by checking against hardcoded issuer addresses
+ splitted token from asset functionality into two address linked contracts
*/
pragma solidity ^0.4.13;
contract TokenInterface {
// **** DATA
address owner;
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
uint256 public initialSupply;
uint256 public totalSupply;
string public name;
string public symbol;
uint8 public decimals;
string public standard;
// **** FUNCTIONS
function isOwner() returns (bool);
function balanceOf(address _owner) constant returns (uint256 balance);
function transfer(address _to, uint256 _amount) returns (bool success);
function transferFrom(address _from, address _to, uint256 _amount) returns (bool success);
function approve(address _spender, uint256 _amount) returns (bool success);
function allowance(address _owner, address _spender) constant returns (uint256 remaining);
function Token();
// **** EVENTS
event Error(string error);
}
contract Asset is TokenInterface {
// **** Link to token contract
address tokenContract;
// Contract constructor
function Asset(address _tokenContractAddress) {
tokenContract = _tokenContractAddress;
}
// **** DATA
/** Asset states
*
* - Released: Once issued the asset stays as released until sent for free to someone specified by issuer
* - ForSale: The asset belongs to a user and is open to be sold
* - Unfungible: The asset cannot be sold, remaining to the user it belongs to.
*/
enum assetStatus { Released, ForSale, Unfungible }
// https://ethereum.stackexchange.com/questions/1807/enums-in-solidity
struct asst {
uint256 assetId;
address assetOwner;
address issuer;
string content; // a JSON object containing the image data of the asset and its title
uint256 sellPrice; // in Watafan tokens, how many of them for this asset
assetStatus status; // behaviour (tradability) of the asset depends upon its status
}
mapping (uint256 => asst) assetsById;
uint256 lastAssetId; // Last assetId
// **** METHODS
// Queries the asset, knowing the id
function getAssetById(uint256 assetId) constant returns (uint256 _assetId, address _assetOwner, address _issuer, string _content, uint256 _sellPrice, uint256 _status) {
return (assetsById[assetId].assetId, assetsById[assetId].assetOwner, assetsById[assetId].issuer, assetsById[assetId].content, assetsById[assetId].sellPrice, uint256(assetsById[assetId].status));
}
// Seller sends an owned asset to a buyer, providing its allowance matches token price and transfer the tokens from buyer
function sendAssetTo(uint256 assetId, address assetBuyer) returns (bool) {
// assetId must not be zero
if (assetId == 0) {
Error('sendAssetTo: assetId must not be zero');
return false;
}
// Check whether the asset belongs to the seller
if (assetsById[assetId].assetOwner != msg.sender) {
Error('sendAssetTo: the asset does not belong to you, the seller');
return false;
}
if (assetsById[assetId].sellPrice > 0) { // for non-null token paid transactions
// Check whether there is balance enough from the buyer to get its tokens
if (balances[assetBuyer] < assetsById[assetId].sellPrice) {
Error('sendAssetTo: there is not enough balance from the buyer to get its tokens');
return false;
}
// Check whether there is allowance enough from the buyer to get its tokens
if (TokenInterface(tokenContract).allowance(assetBuyer, msg.sender) < assetsById[assetId].sellPrice) {
Error('sendAssetTo: there is not enough allowance from the buyer to get its tokens');
return false;
}
// Get the buyer tokens
if (!TokenInterface(tokenContract).transferFrom(assetBuyer, msg.sender, assetsById[assetId].sellPrice)) {
Error('sendAssetTo: transferFrom failed'); // This shouldn't happen ever, but just in case...
return false;
}
}
// Set the asset status to Unfungible
assetsById[assetId].status = assetStatus.Unfungible;
// Transfer the asset to the buyer
assetsById[assetId].assetOwner = assetBuyer;
// Event log
SendAssetTo(assetId, assetBuyer);
return true;
}
// Buyer gets an asset providing it is in ForSale status, and pays the corresponding tokens to the seller/owner. amount must match assetPrice to have a deal.
function buyAsset(uint256 assetId, uint256 amount) returns (bool) {
// assetId must not be zero
if (assetId == 0) {
Error('buyAsset: assetId must not be zero');
return false;
}
// Check whether the asset is in ForSale status
if (assetsById[assetId].status != assetStatus.ForSale) {
Error('buyAsset: the asset is not for sale');
return false;
}
// Check whether the asset price is the same as amount
if (assetsById[assetId].sellPrice != amount) {
Error('buyAsset: the asset price does not match the specified amount');
return false;
}
if (assetsById[assetId].sellPrice > 0) { // for non-null token paid transactions
// Check whether there is balance enough from the buyer to pay the asset
if (balances[msg.sender] < assetsById[assetId].sellPrice) {
Error('buyAsset: there is not enough token balance to buy this asset');
return false;
}
// Send the buyer's tokens to the seller
if (!TokenInterface(tokenContract).transfer(assetsById[assetId].assetOwner, assetsById[assetId].sellPrice)) {
Error('buyAsset: transfer failed'); // This shouldn't happen ever, but just in case...
return false;
}
}
// Set the asset status to Unfungible
assetsById[assetId].status = assetStatus.Unfungible;
// Transfer the asset to the buyer
assetsById[assetId].assetOwner = msg.sender;
// Event log
BuyAsset(assetId, amount);
return true;
}
// Asset issuers:
// 0x9b9731b07b9b5f982289bab87097eade6a4bc25d => metamask-ropsten or geth node-ropsten
// 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c => JS VM solidity-browser
function isIssuer() returns (bool) {
if (
msg.sender == 0x009b9731b07b9b5f982289bab87097eade6a4bc25d ||
msg.sender == 0x0014723a09acff6d2a60dcdf7aa4aff308fddc160c
) return true;
return false;
}
// To be called by issueAssetTo() and properly authorized issuers
function issueAsset(string content, uint256 sellPrice) internal returns (uint256 nextAssetId) {
// Check whether user is allowed to issue a new asset
if (!isIssuer()) {
Error('issueAsset: only issuers are allowed to issue new assets');
return 0;
}
// Find out next asset Id
nextAssetId = lastAssetId + 1;
assetsById[nextAssetId].assetId = nextAssetId;
assetsById[nextAssetId].assetOwner = msg.sender;
assetsById[nextAssetId].issuer = msg.sender;
assetsById[nextAssetId].content = content;
assetsById[nextAssetId].sellPrice = sellPrice;
assetsById[nextAssetId].status = assetStatus.Released;
// Update lastAssetId
lastAssetId++;
// Event log
IssueAsset(nextAssetId, msg.sender, sellPrice);
return nextAssetId;
}
// Issuer sends a new free asset to a given user as a gift
function issueAssetTo(string content, address to) returns (bool) {
uint256 assetId = issueAsset(content, 0); // 0 tokens, as a gift
if (assetId == 0) {
Error('issueAssetTo: asset has not been properly issued');
return (false);
}
// The brand new asset is inmediatly sent to the recipient
return(sendAssetTo(assetId, to));
}
// Seller can block tradability of its assets
function setAssetUnfungible(uint256 assetId) returns (bool) {
// assetId must not be zero
if (assetId == 0) {
Error('setAssetUnfungible: assetId must not be zero');
return false;
}
// Check whether the asset belongs to the caller
if (assetsById[assetId].assetOwner != msg.sender) {
Error('setAssetUnfungible: only owners of the asset are allowed to update its status');
return false;
}
assetsById[assetId].status = assetStatus.Unfungible;
// Event log
SetAssetUnfungible(assetId, msg.sender);
return true;
}
// Seller updates the price of its assets and its status to ForSale
function setAssetPrice(uint256 assetId, uint256 sellPrice) returns (bool) {
// assetId must not be zero
if (assetId == 0) {
Error('setAssetPrice: assetId must not be zero');
return false;
}
// Check whether the asset belongs to the caller
if (assetsById[assetId].assetOwner != msg.sender) {
Error('setAssetPrice: only owners of the asset are allowed to set its price and update its status');
return false;
}
assetsById[assetId].sellPrice = sellPrice;
assetsById[assetId].status = assetStatus.ForSale;
// Event log
SetAssetPrice(assetId, msg.sender, sellPrice);
return true;
}
// **** EVENTS
// Triggered when a seller sends its asset to a buyer and receives the corresponding tokens
event SendAssetTo(uint256 assetId, address assetBuyer);
// Triggered when a buyer sends its tokens to a seller and receives the specified asset
event BuyAsset(uint256 assetId, uint256 amount);
// Triggered when the admin issues a new asset
event IssueAsset(uint256 nextAssetId, address assetOwner, uint256 sellPrice);
// Triggered when the user updates its asset status to Unfungible
event SetAssetUnfungible(uint256 assetId, address assetOwner);
// Triggered when the user updates its asset price and status to ForSale
event SetAssetPrice(uint256 assetId, address assetOwner, uint256 sellPrice);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment