Created
June 9, 2017 14:29
-
-
Save computerphysicslab/93405f03880b7eb422013cdbbc3d493f to your computer and use it in GitHub Desktop.
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
/* | |
Watafan asset Smart Contract v1.0 | |
developed by: | |
MarketPay.io , 2017 | |
https://marketpay.io/ | |
http://lnked.in/blockchain | |
*/ | |
pragma solidity ^0.4.6; | |
contract Mortal { | |
address owner; | |
function Mortal() { owner = msg.sender; } | |
function kill() { if (msg.sender == owner) suicide(owner); } | |
// @notice For debugging purposes when using solidity online browser | |
function whoAmI() constant returns (address) { | |
return msg.sender; | |
} | |
// @notice Get the current timestamp from last mined block | |
function timestamp() constant returns (uint256) { | |
return block.timestamp; | |
} | |
// @A generic error log | |
event Error(string error); | |
} | |
// @title Standard Token Contract | |
// ERC20-compliant tokens => https://github.com/ethereum/EIPs/issues/20 | |
// A token is a fungible virtual good that can be traded. | |
// ERC-20 Tokens comply to the standard described in the Ethereum ERC-20 proposal. | |
// Basic, standardized Token contract. Defines the functions to check token balances | |
// send tokens, send tokens on behalf of a 3rd party and the corresponding approval process. | |
contract TokenInterface is Mortal { | |
// **** DATA | |
mapping (address => uint256) balances; | |
mapping (address => mapping (address => uint256)) allowed; | |
uint256 public initialSupply; // Initial and total token supply | |
uint256 public totalSupply; | |
bool allocated = false; // True after defining token parameters and initial mint | |
// Public variables of the token, all used for display | |
// HumanStandardToken is a specialisation of ERC20 defining these parameters | |
string public name; | |
string public symbol; | |
uint8 public decimals; | |
string public standard = 'H0.1'; | |
// **** METHODS | |
// Get total amount of tokens, totalSupply is a public var actually | |
// function totalSupply() constant returns (uint256 totalSupply) {} | |
// Get the account balance of another account with address _owner | |
function balanceOf(address _owner) constant returns (uint256 balance); | |
// Send _amount amount of tokens to address _to | |
function transfer(address _to, uint256 _amount) returns (bool success); | |
// Send _amount amount of tokens from address _from to address _to | |
// The transferFrom method is used for a withdraw workflow, allowing contracts to send | |
// tokens on your behalf, for example to "deposit" to a contract address and/or to charge | |
// fees in sub-currencies; the command should fail unless the _from account has | |
// deliberately authorized the sender of the message via some mechanism | |
function transferFrom(address _from, address _to, uint256 _amount) returns (bool success); | |
// Allow _spender to withdraw from your account, multiple times, up to the _amount amount. | |
// If this function is called again it overwrites the current allowance with _amount. | |
function approve(address _spender, uint256 _amount) returns (bool success); | |
// Returns the amount which _spender is still allowed to withdraw from _owner | |
function allowance(address _owner, address _spender) constant returns (uint256 remaining); | |
// **** EVENTS | |
// Triggered when tokens are transferred | |
event Transfer(address indexed _from, address indexed _to, uint256 _amount); | |
// Triggered whenever approve(address _spender, uint256 _amount) is called | |
event Approval(address indexed _owner, address indexed _spender, uint256 _amount); | |
} | |
contract Token is TokenInterface { | |
function balanceOf(address _owner) constant returns (uint256 balance) { | |
return balances[_owner]; | |
} | |
function transfer(address _to, uint256 _amount) returns (bool success) { | |
// Preventing balance integer overflow at (2^256 - 1) | |
if (balances[msg.sender] >= _amount && balances[_to] + _amount > balances[_to]) { | |
balances[msg.sender] -= _amount; | |
balances[_to] += _amount; | |
Transfer(msg.sender, _to, _amount); | |
return true; | |
} else { | |
return false; | |
} | |
} | |
function transferFrom(address _from, address _to, uint256 _amount) returns (bool success) { | |
// Preventing balance integer overflow at (2^256 - 1) | |
if (balances[_from] >= _amount && allowed[_from][msg.sender] >= _amount && balances[_to] + _amount > balances[_to]) { | |
balances[_to] += _amount; | |
balances[_from] -= _amount; | |
allowed[_from][msg.sender] -= _amount; | |
Transfer(_from, _to, _amount); | |
return true; | |
} else { | |
return false; | |
} | |
} | |
function approve(address _spender, uint256 _amount) returns (bool success) { | |
allowed[msg.sender][_spender] = _amount; | |
Approval(msg.sender, _spender, _amount); | |
return true; | |
} | |
function allowance(address _owner, address _spender) constant returns (uint256 remaining) { | |
return allowed[_owner][_spender]; | |
} | |
// Constructor: set up token properties and owner token balance | |
function Token() { | |
// This is the constructor, so owner should be equal to msg.sender, and this method should be called just once | |
// make sure owner address is configured | |
// if(owner == 0x0) throw; | |
// owner address can call this function | |
// if (msg.sender != owner ) throw; | |
// call this function just once | |
// if (allocated) throw; | |
initialSupply = 1000000000; | |
totalSupply = initialSupply; | |
name = "Watafan"; | |
symbol = "FAN"; | |
decimals = 0; | |
balances[owner] = totalSupply; | |
Transfer(this, owner, totalSupply); | |
allocated = true; | |
} | |
} | |
contract Mobile is Mortal { | |
// **** DATA | |
mapping (address => uint40) phoneNumbers; | |
mapping (uint40 => address) phoneNumbersInverse; | |
// **** METHODS | |
// User signs up, registering its mobile phone number | |
function signUp(uint40 phoneNumber) { | |
phoneNumbers[msg.sender] = phoneNumber; | |
phoneNumbersInverse[phoneNumber] = msg.sender; | |
SignUp(phoneNumber, msg.sender, timestamp()); // Event log | |
} | |
// Operator checks whether a given user has been registered | |
function hasSignedUp(uint40 phoneNumber) constant returns (bool signedUp) { | |
if (phoneNumbersInverse[phoneNumber] == address(0)) { // check if the address is not set | |
return (false); | |
} else { | |
return (true); | |
} | |
} | |
// **** EVENTS | |
// Triggered when a new user signs up from its DApp | |
event SignUp(uint40 phoneNumber, address who, uint256 timestamp); | |
} | |
contract MobileToken is Token, Mobile { | |
function mobileBalanceOf(uint40 phoneNumber) constant returns (uint256 balance) { | |
if (!hasSignedUp(phoneNumber)) { | |
return (0); | |
} | |
return balances[phoneNumbersInverse[phoneNumber]]; | |
} | |
function mobileTransfer(uint40 toPhoneNumber, uint256 amount) returns (bool success) { | |
if (!hasSignedUp(toPhoneNumber)) { | |
return (false); | |
} | |
return transfer(phoneNumbersInverse[toPhoneNumber], amount); | |
} | |
function mobileTransferFrom(uint40 fromPhoneNumber, uint40 toPhoneNumber, uint256 amount) returns (bool success) { | |
if (!hasSignedUp(fromPhoneNumber)) { | |
return (false); | |
} | |
if (!hasSignedUp(toPhoneNumber)) { | |
return (false); | |
} | |
return transferFrom(phoneNumbersInverse[fromPhoneNumber], phoneNumbersInverse[toPhoneNumber], amount); | |
} | |
function mobileApprove(uint40 spenderPhoneNumber, uint256 amount) returns (bool success) { | |
if (!hasSignedUp(spenderPhoneNumber)) { | |
return (false); | |
} | |
return approve(phoneNumbersInverse[spenderPhoneNumber], amount); | |
} | |
function mobileAllowance(uint40 ownerPhoneNumber, uint40 spenderPhoneNumber) constant returns (uint256 remaining) { | |
if (!hasSignedUp(ownerPhoneNumber)) { | |
return (0); | |
} | |
if (!hasSignedUp(spenderPhoneNumber)) { | |
return (0); | |
} | |
return allowance(phoneNumbersInverse[ownerPhoneNumber], phoneNumbersInverse[spenderPhoneNumber]); | |
} | |
} | |
contract Asset is Token { | |
// **** DATA | |
struct asst { | |
uint256 assetId; | |
uint256 next; // pointer to link assets by owner, 0 if ain't next | |
uint256 prev; // pointer to link assets by owner, 0 if ain't prev | |
uint256 timestampCreation; | |
address assetOwner; | |
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 | |
} | |
mapping (uint256 => asst) assetsById; | |
mapping (address => uint256) firstAssetId; // Roots for the linked assets, by address | |
uint256 lastAssetId; // Last assetId | |
// **** METHODS | |
// Queries the asset, knowing the id | |
function getAssetById(uint256 assetId) constant returns (uint256 _assetId, uint256 _next, uint256 _prev, uint256 _timestampCreation, address _assetOwner, string _content, uint256 _sellPrice) { | |
return (assetsById[assetId].assetId, assetsById[assetId].next, assetsById[assetId].prev, assetsById[assetId].timestampCreation, assetsById[assetId].assetOwner, assetsById[assetId].content, assetsById[assetId].sellPrice); | |
} | |
// Queries the first asset a user owns | |
function getFirstAssetIdOwnedBy(address assetOwner) constant returns (uint256 assetId) { | |
return firstAssetId[assetOwner]; | |
} | |
// 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 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 balance overflow | |
if (balances[msg.sender] + assetsById[assetId].sellPrice < balances[msg.sender]) { | |
Error('sendAssetTo: overflow on seller balance'); | |
return false; | |
} | |
// Check whether there is allowance enough from the buyer to get its tokens | |
if (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 (!transferFrom(assetBuyer, msg.sender, assetsById[assetId].sellPrice)) { | |
Error('sendAssetTo: transferFrom failed'); // This shouldn't happen ever, but just in case... | |
return false; | |
} | |
} | |
// Transfer the asset to the buyer | |
assetsById[assetId].assetOwner = msg.sender; | |
// Rebuild asset linkage for seller, msg.sender, removes the asset | |
if (firstAssetId[msg.sender] == assetId) { // seller root update | |
firstAssetId[msg.sender] = assetsById[assetId].next; | |
} else { | |
assetsById[assetsById[assetId].prev].next = assetsById[assetId].next; // update previous seller asset link | |
if (assetsById[assetId].next > 0) { | |
assetsById[assetsById[assetId].next].prev = assetsById[assetId].prev; // update next seller asset link | |
} | |
} | |
// Rebuild asset linkage for buyer, assetBuyer, adds the asset, and updates asset links and properties | |
if (firstAssetId[assetBuyer] > 0) { | |
assetsById[firstAssetId[assetBuyer]].prev = assetId; // update next buyer asset link | |
} | |
assetsById[assetId].next = firstAssetId[assetBuyer]; | |
firstAssetId[assetBuyer] = assetId; // bought asset becomes first in the buyer linkage | |
assetsById[assetId].prev = 0; | |
assetsById[assetId].assetOwner = assetBuyer; | |
// Event log | |
SendAssetTo(assetId, assetBuyer, timestamp()); | |
return true; | |
} | |
function issueAsset(string content, uint256 sellPrice) returns (bool) { | |
// Check whether user is allowed to issue a new asset | |
if (msg.sender != owner ) { | |
Error('issueAsset: only SC owner is allowed to issue new assets'); | |
return false; | |
} | |
// Find out next asset Id | |
uint256 nextAssetId = lastAssetId + 1; | |
assetsById[nextAssetId].assetId = nextAssetId; | |
assetsById[nextAssetId].next = firstAssetId[msg.sender]; | |
assetsById[nextAssetId].prev = 0; | |
assetsById[nextAssetId].timestampCreation = timestamp(); | |
assetsById[nextAssetId].assetOwner = msg.sender; | |
assetsById[nextAssetId].content = content; | |
assetsById[nextAssetId].sellPrice = sellPrice; | |
// Update linkage | |
firstAssetId[msg.sender] = nextAssetId; // new asset becomes first in the issuer linkage | |
if (assetsById[nextAssetId].next > 0) { | |
assetsById[assetsById[nextAssetId].next].prev = nextAssetId; | |
} | |
// Update lastAssetId | |
lastAssetId++; | |
// Event log | |
IssueAsset(nextAssetId, msg.sender, sellPrice, timestamp()); | |
return true; | |
} | |
// **** EVENTS | |
// Triggered when a seller sends its asset to a buyer and receives the corresponding tokens | |
event SendAssetTo(uint256 assetId, address assetBuyer, uint256 timestamp); | |
// Triggered when the admin issues a new asset | |
event IssueAsset(uint256 nextAssetId, address assetOwner, uint256 sellPrice, uint256 timestamp); | |
} | |
contract MobileAsset is Asset, MobileToken { | |
function mobileGetAssetById(uint256 assetId) constant returns (uint256 _assetId, uint256 _next, uint256 _prev, uint256 _timestampCreation, uint40 _assetOwnerPhoneNumber, string _content, uint256 _sellPrice) { | |
return (assetsById[assetId].assetId, assetsById[assetId].next, assetsById[assetId].prev, assetsById[assetId].timestampCreation, phoneNumbers[assetsById[assetId].assetOwner], assetsById[assetId].content, assetsById[assetId].sellPrice); | |
} | |
function mobileGetFirstAssetIdOwnedBy(uint40 assetOwnerPhoneNumber) constant returns (uint256 assetId) { | |
if (!hasSignedUp(assetOwnerPhoneNumber)) { | |
return (0); | |
} | |
return firstAssetId[phoneNumbersInverse[assetOwnerPhoneNumber]]; | |
} | |
function mobileSendAssetTo(uint256 assetId, uint40 assetBuyerPhoneNumber) returns (bool) { | |
if (!hasSignedUp(assetBuyerPhoneNumber)) { | |
return (false); | |
} | |
return (sendAssetTo(assetId, phoneNumbersInverse[assetBuyerPhoneNumber])); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment