Last active
October 9, 2017 17:02
-
-
Save computerphysicslab/2460d47bebf7c5d51e194f84e78934d5 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-Smart-Contracts-1.2.sol | |
/* | |
Watafan Smart Contracts v1.2 | |
developed by: | |
MarketPay.io , 2017 | |
https://marketpay.io/ | |
http://lnked.in/blockchain | |
v1.2 https://gist.github.com/computerphysicslab/2460d47bebf7c5d51e194f84e78934d5 | |
+ Assets: Added branding field | |
+ Using revert on every error | |
+ ICO: Control hardCap using weisHardCap | |
+ ICO: Control minInvestment using weisMinInvestment | |
+ ICO: Owner may update ether price | |
+ Added SafeMath on token operations | |
v1.1 https://gist.github.com/computerphysicslab/56e681633b7731c52829c9e87fca8da7 | |
+ Added transferOrigin using tx.origin | |
+ Mintable token | |
+ Every address and id on Events is now indexed | |
+ Added SafeMath | |
+ Added auto ICO funding, filling contract with tokens on constructor | |
+ Testing revert | |
+ Formula to get weis per token at ICO | |
+ idolName added to Issuers and Assets contracts | |
v1.0 https://gist.github.com/computerphysicslab/9673ba183d7d9674192c924545da7c77 | |
+ Four contracts together: tokens, ICO, issuers, assets | |
*/ | |
pragma solidity ^0.4.11; | |
/** | |
* @title SafeMath | |
* @dev Math operations with safety checks that throw on error | |
*/ | |
library SafeMath { | |
function mul(uint256 a, uint256 b) internal constant returns (uint256) { | |
uint256 c = a * b; | |
assert(a == 0 || c / a == b); | |
return c; | |
} | |
function div(uint256 a, uint256 b) internal constant returns (uint256) { | |
// assert(b > 0); // Solidity automatically throws when dividing by 0 | |
uint256 c = a / b; | |
// assert(a == b * c + a % b); // There is no case in which this doesn't hold | |
return c; | |
} | |
function sub(uint256 a, uint256 b) internal constant returns (uint256) { | |
assert(b <= a); | |
return a - b; | |
} | |
function add(uint256 a, uint256 b) internal constant returns (uint256) { | |
uint256 c = a + b; | |
assert(c >= a); | |
return c; | |
} | |
} | |
contract System { | |
using SafeMath for uint256; | |
address owner; | |
// @notice To limit functions usage to contract owner | |
modifier onlyOwner() { | |
if (msg.sender != owner) { | |
error('Mortal: onlyOwner function called by user that is not owner'); | |
} else { | |
_; | |
} | |
} | |
// @notice For debugging purposes when using solidity online browser | |
function whoAmI() public constant returns (address) { | |
return msg.sender; | |
} | |
// @notice Get the current timestamp from last mined block | |
function timestamp() public constant returns (uint256) { | |
return block.timestamp; | |
} | |
// @notice Calls whenever an error occurs, logs it and reverts transaction | |
function error(string _error) internal { | |
Error(_error); | |
revert; | |
} | |
// @notice System constructor, defines owner | |
function System() public { | |
// This is the constructor, so owner should be equal to msg.sender, and this method should be called just once | |
owner = msg.sender; | |
// make sure owner address is configured | |
if(owner == 0x0) error('System constructor: Owner address is 0x0'); // Never should happen, but just in case... | |
} | |
// **** EVENTS | |
// @notice A generic error log | |
event Error(string _error); | |
} | |
contract Tokens is System { | |
// **** 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() public constant returns (uint256 totalSupply) {} | |
// Get the account balance of another account with address _owner | |
function balanceOf(address _owner) public constant returns (uint256 balance) { | |
return balances[_owner]; | |
} | |
// Send _amount amount of tokens to address _to | |
function transfer(address _to, uint256 _amount) public returns (bool success) { | |
if (balances[msg.sender] < _amount) { | |
error('transfer: the amount to transfer is higher than your token balance'); | |
return false; | |
} | |
balances[msg.sender] = balances[msg.sender].sub(_amount); | |
balances[_to] = balances[_to].add(_amount); | |
Transfer(msg.sender, _to, _amount); | |
return true; | |
} | |
// 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) public returns (bool success) { | |
if (balances[_from] < _amount) { | |
error('transfer: the amount to transfer is higher than the token balance of the source'); | |
return false; | |
} | |
if (allowed[_from][msg.sender] < _amount) { | |
error('transfer: the amount to transfer is higher than the maximum token transfer allowed by the source'); | |
return false; | |
} | |
balances[_from] = balances[_from].sub(_amount); | |
balances[_to] = balances[_to].add(_amount); | |
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_amount); | |
Transfer(_from, _to, _amount); | |
return true; | |
} | |
// 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) public returns (bool success) { | |
allowed[msg.sender][_spender] = _amount; | |
Approval(msg.sender, _spender, _amount); | |
return true; | |
} | |
// Returns the amount which _spender is still allowed to withdraw from _owner | |
function allowance(address _owner, address _spender) public constant returns (uint256 remaining) { | |
return allowed[_owner][_spender]; | |
} | |
// Constructor: set up token properties and owner token balance | |
function Tokens() public { | |
// call this function just once | |
// if (allocated) throw; | |
initialSupply = 100000000000; // 100M tokens, 3 decimals | |
totalSupply = initialSupply; | |
name = "Watafan"; | |
symbol = "WFAN"; | |
decimals = 3; | |
balances[owner] = totalSupply; | |
Transfer(this, owner, totalSupply); | |
// allocated = true; | |
} | |
// This is out of ERC20 standard. Send _amount amount of tokens to from tx.origin to address _to | |
function transferOrigin(address _to, uint256 _amount) public returns (bool success) { | |
if (balances[tx.origin] < _amount) { | |
error('transferOrigin: the amount to transfer is higher than your token balance'); | |
return false; | |
} | |
balances[tx.origin] = balances[tx.origin].sub(_amount); | |
balances[_to] = balances[_to].add(_amount); | |
Transfer(tx.origin, _to, _amount); | |
return true; | |
} | |
/** | |
* @dev Function to mint tokens | |
* @param _to The address that will receive the minted tokens. | |
* @param _amount The amount of tokens to mint. | |
* @return A boolean that indicates if the operation was successful. | |
*/ | |
function mint(address _to, uint256 _amount) external onlyOwner returns (bool) { | |
totalSupply = totalSupply.add(_amount); | |
balances[_to] = balances[_to].add(_amount); | |
Mint(_to, _amount); | |
Transfer(0x0, _to, _amount); | |
return true; | |
} | |
// **** 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); | |
// Triggered when tokens are minted | |
event Mint(address indexed _to, uint256 _amount); | |
} | |
// Minimal interface of ERC20 token contract, just to cast the contract address and make it callable from the ICO and Assets contracts | |
contract IFerc20Tokens { | |
function balanceOf(address _owner) public constant returns (uint256 balance); | |
function transfer(address _to, uint256 _amount) public returns (bool success); | |
function transferFrom(address _from, address _to, uint256 _amount) public returns (bool success); | |
function allowance(address _owner, address _spender) public constant returns (uint256 remaining); | |
function transferOrigin(address _to, uint256 _amount) public returns (bool success); | |
} | |
contract ICO is System { | |
// The token being sold | |
IFerc20Tokens public SCTokens; | |
// start and end timestamps where investments are allowed (both inclusive) | |
uint256 public startTime; | |
uint256 public endTime; | |
// address where funds are collected | |
address public wallet; | |
uint256 public weisPerToken; // how many token units a buyer gets per wei | |
uint256 public weisPerEther; | |
uint256 public eurosPerEther; | |
uint256 public tokenPrice; // in thousands of Euro | |
uint256 public weisRaised; // amount of Weis raised | |
uint256 public tokensHardCap; // Max amount of Tokens for sale | |
uint256 public weisHardCap; // Max amount of Weis raised | |
uint256 public eurosMinInvestment; // Min amount of Euros to perform a token sale | |
uint256 public weisMinInvestment; // Min amount of Weis to perform a token sale | |
// constructor | |
function ICO(address _SCTokens) public { | |
startTime = timestamp(); | |
endTime = timestamp().add(10000); | |
// rate = 1; | |
// 1 ether = 1000000000000000000 weis | |
weisPerEther = 1000000000000000000; | |
// 1 ether = 1000 euros | |
eurosPerEther = 255; | |
// 1 euro = 1000000000000000 weis | |
// 1 token = 1 euro | |
tokenPrice = 550; // Pre-ICO | |
// 1 token = 1000000000000000 weis | |
// 1 token (3 decimals) = 1000000000000 weis | |
weisPerToken = weisPerEther.div(eurosPerEther).mul(tokenPrice).div(1000).div(1000); | |
// weisPerToken = 1000000000000; | |
// wallet = 0x0034bd0dcc942221d12e331dcc9d67ec869dd516e9; | |
wallet = 0x00a7e3c7c227c72a60e5a2f9912448fb1c21078769; // juan_nodo | |
SCTokens = IFerc20Tokens(_SCTokens); | |
tokensHardCap = 8000000000; // 8 million tokens, 3 decimals | |
weisHardCap = weisPerToken.mul(tokensHardCap); | |
eurosMinInvestment = 50000; | |
weisMinInvestment = eurosMinInvestment.mul(weisPerEther).div(eurosPerEther); | |
// Fill ICO contract with for-sale tokens | |
if (!SCTokens.transferOrigin(this, tokensHardCap)) { | |
error('ICO constructor: Token funding failed'); | |
} | |
} | |
function updateEtherPrice(uint256 _eurosPerEther) public onlyOwner returns (bool) { | |
eurosPerEther = _eurosPerEther; | |
return true; | |
} | |
// fallback function can be used to buy tokens | |
function () payable public { | |
buyTokens(msg.sender); | |
} | |
// low level token purchase function | |
function buyTokens(address beneficiary) public payable returns (bool) { | |
if (beneficiary == 0x0) { | |
error('buyTokens: beneficiary == 0x0'); | |
return false; | |
} | |
bool withinPeriod = timestamp() >= startTime && timestamp() <= endTime; | |
if (!withinPeriod) { | |
error('buyTokens: Not withinPeriod'); | |
return false; | |
} | |
bool nonZeroPurchase = msg.value != 0; | |
if (!nonZeroPurchase) { | |
error('buyTokens: ZeroPurchase'); | |
return false; | |
} | |
uint256 weisAmount = msg.value; | |
// Verify whether enough ether has been sent to buy the min amount of investment | |
if (weisAmount < weisMinInvestment) { | |
error('buyTokens: Minimal investment not reached. Not enough ethers to perform the minimal purchase.'); | |
return false; | |
} | |
// Verify whether there are enough tokens to sell | |
if (weisRaised.add(weisAmount) > weisHardCap) { | |
error('buyTokens: HardCap reached. Not enough tokens on ICO contract to perform this purchase.'); | |
return false; | |
} | |
// calculate token amount to be created | |
// uint256 tokens = weisAmount.mul(rate); | |
uint256 tokenAmount = weisAmount.div(weisPerToken); | |
// update state | |
weisRaised = weisRaised.add(weisAmount); | |
// Send the tokens to the investor | |
if (!SCTokens.transfer(beneficiary, tokenAmount)) { | |
error('buyTokens: unable to transfer tokens from ICO contract to beneficiary'); | |
return false; | |
} | |
TokenPurchase(msg.sender, beneficiary, weisAmount, tokenAmount); | |
// Send the investor's ethers to the wallet | |
forwardFunds(); | |
return true; | |
} | |
// send ether to the fund collection wallet | |
// override to create custom fund forwarding mechanisms | |
function forwardFunds() internal { | |
wallet.transfer(msg.value); | |
} | |
// @return true if crowdsale event has ended | |
function hasEnded() public constant returns (bool) { | |
return timestamp() > endTime; | |
} | |
event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount); | |
} | |
contract Issuers is System { | |
// **** DATA | |
struct issu { | |
uint256 issuerId; | |
bool issuerAuth; | |
address issuerAddress; | |
string issuerContent; // a JSON object containing data about the issuer | |
bytes32 idolName; | |
} | |
mapping (address => issu) issuerData; | |
mapping (uint256 => address) issuerAddressById; // indexed issuers so as to be full scannable | |
uint256 lastId; | |
// **** METHODS | |
// Checks whether a given user is an authorized issuer | |
function isIssuer(address _issuer) public constant returns (bool) { | |
return (issuerData[_issuer].issuerAuth); | |
} | |
function newIssuer(address _issuer, string _content, bytes32 _idolName) internal onlyOwner returns (uint256 id) { | |
// Update Index | |
id = lastId.add(1); | |
issuerData[_issuer].issuerId = id; | |
issuerData[_issuer].issuerAuth = false; | |
issuerData[_issuer].issuerAddress = _issuer; | |
issuerData[_issuer].issuerContent = _content; | |
issuerData[_issuer].idolName = _idolName; | |
issuerAddressById[id] = _issuer; | |
lastId = lastId.add(1); | |
NewIssuer(_issuer, id, _idolName, timestamp()); // Event log | |
} | |
function grantIssuer(address _issuer, string _content, bytes32 _idolName) external onlyOwner { | |
// Checks whether this user has been previously added as an issuer | |
uint256 id; | |
if (issuerData[_issuer].issuerId > 0) { | |
id = issuerData[_issuer].issuerId; | |
} else { | |
id = newIssuer(_issuer, _content, _idolName); | |
} | |
issuerData[_issuer].issuerAuth = true; | |
GrantIssuer(_issuer, id, _idolName, timestamp()); // Event log | |
} | |
function revokeIssuer(address _issuer) external onlyOwner { | |
issuerData[_issuer].issuerAuth = false; | |
RevokeIssuer(_issuer, timestamp()); // Event log | |
} | |
// Queries the issuer, knowing the address | |
function getIssuerByAddress(address _issuer) public constant returns (uint256 _issuerId, bool _issuerAuth, address _issuerAddress, string _issuerContent, bytes32 _idolName) { | |
return (issuerData[_issuer].issuerId, issuerData[_issuer].issuerAuth, issuerData[_issuer].issuerAddress, issuerData[_issuer].issuerContent, issuerData[_issuer].idolName); | |
} | |
// Queries the issuer/idol name, knowing the address | |
function getIdolNameByAddress(address _issuer) public constant returns (bytes32 _idolName) { | |
return (issuerData[_issuer].idolName); | |
} | |
// Queries the issuer, knowing the id | |
function getIssuerById(uint256 id) public constant returns (uint256 _issuerId, bool _issuerAuth, address _issuerAddress, string _issuerContent, bytes32 _idolName) { | |
address _issuer = issuerAddressById[id]; | |
return (getIssuerByAddress(_issuer)); | |
} | |
// **** EVENTS | |
// Triggered when a new issuer is created | |
event NewIssuer(address indexed who, uint256 indexed _id, bytes32 _idolName, uint256 _timestamp); | |
// Triggered when a user is granted to become an issuer | |
event GrantIssuer(address indexed who, uint256 indexed _id, bytes32 _idolName, uint256 _timestamp); | |
// Triggered when a user is revoked for being an issuer | |
event RevokeIssuer(address indexed who, uint256 _timestamp); | |
} | |
// Interface of issuer contract, just to cast the contract address and make it callable from the asset contract | |
contract IFIssuers { | |
function isIssuer(address _issuer) public constant returns (bool); | |
function getIdolNameByAddress(address _issuer) public constant returns (bytes32 _idolName); | |
} | |
contract Assets is System { | |
// **** 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 | |
bytes32 idolName; | |
string branding; // A string added to every asset when issued, and may be defined by contract owner | |
} | |
mapping (uint256 => asst) assetsById; | |
uint256 lastAssetId; // Last assetId | |
uint256 assetFeeIssuer; // Fee percentage for Issuer on every asset sale transaction | |
uint256 assetFeeWatafan; // Fee percentage for Watafan on every asset sale transaction | |
string brandingDefault; // Branding text by default | |
IFerc20Tokens public SCTokens; // The token used to pay for the assets | |
IFIssuers public SCIssuers; // Contract that defines who is an issuer and who is not | |
// **** METHODS | |
// Constructor | |
function Assets(address _SCTokens, address _SCIssuers) public { | |
SCTokens = IFerc20Tokens(_SCTokens); | |
SCIssuers = IFIssuers(_SCIssuers); | |
assetFeeIssuer = 25; // default fee | |
assetFeeWatafan = 25; // default fee | |
brandingDefault = "Download Watafan App and market your idol cards"; // default branding | |
} | |
// Queries the asset, knowing the id, part 1 | |
function getAssetById(uint256 assetId) public constant returns (uint256 _assetId, address _assetOwner, address _issuer, string _content, uint256 _sellPrice, uint256 _status, bytes32 _idolName) { | |
return (assetsById[assetId].assetId, assetsById[assetId].assetOwner, assetsById[assetId].issuer, assetsById[assetId].content, assetsById[assetId].sellPrice, uint256(assetsById[assetId].status), assetsById[assetId].idolName); | |
} | |
// Queries the asset, knowing the id, part 2 | |
function getAssetById2(uint256 assetId) public constant returns (string _branding) { | |
return (assetsById[assetId].branding); | |
} | |
// 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) public 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 (SCTokens.balanceOf(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 (SCTokens.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 (!SCTokens.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) public 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 (SCTokens.balanceOf(msg.sender) < assetsById[assetId].sellPrice) { | |
error('buyAsset: there is not enough token balance to buy this asset'); | |
return false; | |
} | |
// Calculate the seller income | |
uint256 aThousand = 1000; | |
uint256 sellerIncome = assetsById[assetId].sellPrice.mul((aThousand.sub(assetFeeIssuer).sub(assetFeeWatafan))).div(aThousand); | |
// Send the buyer's tokens to the seller | |
if (!SCTokens.transferOrigin(assetsById[assetId].assetOwner, sellerIncome)) { | |
error('buyAsset: seller token transfer failed'); // This shouldn't happen ever, but just in case... | |
return false; | |
} | |
// Send the issuer's fee | |
uint256 issuerIncome = assetsById[assetId].sellPrice.mul(assetFeeIssuer).div(aThousand); | |
if (!SCTokens.transferOrigin(assetsById[assetId].issuer, issuerIncome)) { | |
error('buyAsset: issuer token transfer failed'); // This shouldn't happen ever, but just in case... | |
return false; | |
} | |
// Send the Watafan's fee | |
uint256 watafanIncome = assetsById[assetId].sellPrice.mul(assetFeeWatafan).div(aThousand); | |
if (!SCTokens.transferOrigin(owner, watafanIncome)) { | |
error('buyAsset: watafan token 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; | |
} | |
// To limit issue functions just to authorized issuers | |
modifier onlyIssuer() { | |
if (!SCIssuers.isIssuer(msg.sender)) { | |
error('onlyIssuer function called by user that is not an authorized issuer'); | |
} else { | |
_; | |
} | |
} | |
// To be called by issueAssetTo() and properly authorized issuers | |
function issueAsset(string content, uint256 sellPrice) onlyIssuer internal returns (uint256 nextAssetId) { | |
// Find out next asset Id | |
nextAssetId = lastAssetId.add(1); | |
bytes32 idolName = SCIssuers.getIdolNameByAddress(msg.sender); | |
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; | |
assetsById[nextAssetId].idolName = idolName; | |
assetsById[nextAssetId].branding = brandingDefault; | |
// Update lastAssetId | |
lastAssetId++; | |
// Event log | |
IssueAsset(nextAssetId, idolName, msg.sender, sellPrice); | |
return nextAssetId; | |
} | |
// Issuer sends a new free asset to a given user as a gift | |
function issueAssetTo(string content, address to) public 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) public 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) public 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; | |
} | |
// Owner updates the fees for assets sale transactions | |
function setAssetSaleFees(uint256 feeIssuer, uint256 feeWatafan) public onlyOwner returns (bool) { | |
// Check new fees are consistent | |
if (feeIssuer.add(feeWatafan) > 1000) { | |
error('setAssetSaleFees: added fees exceed 100.0%. Not updated.'); | |
return false; | |
} | |
assetFeeIssuer = feeIssuer; | |
assetFeeWatafan = feeWatafan; | |
// Event log | |
SetAssetSaleFees(feeIssuer, feeWatafan); | |
return true; | |
} | |
// Owner updates the default branding string for assets issued from now on | |
function setAssetBranding(string _branding) public onlyOwner returns (bool) { | |
brandingDefault = _branding; | |
// Event log | |
SetAssetBranding(_branding); | |
return true; | |
} | |
// **** EVENTS | |
// Triggered when a seller sends its asset to a buyer and receives the corresponding tokens | |
event SendAssetTo(uint256 indexed assetId, address indexed assetBuyer); | |
// Triggered when a buyer sends its tokens to a seller and receives the specified asset | |
event BuyAsset(uint256 indexed assetId, uint256 amount); | |
// Triggered when the admin issues a new asset | |
event IssueAsset(uint256 indexed assetId, bytes32 idolName, address indexed assetOwner, uint256 sellPrice); | |
// Triggered when the user updates its asset status to Unfungible | |
event SetAssetUnfungible(uint256 indexed assetId, address assetOwner); | |
// Triggered when the user updates its asset price and status to ForSale | |
event SetAssetPrice(uint256 indexed assetId, address assetOwner, uint256 sellPrice); | |
// Triggered when the owner updates the asset sale fees | |
event SetAssetSaleFees(uint256 feeIssuer, uint256 feeWatafan); | |
// Triggered when the owner updates the asset default branding string | |
event SetAssetBranding(string _branding); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment