Skip to content

Instantly share code, notes, and snippets.

@computerphysicslab
Last active October 9, 2017 17:02
Show Gist options
  • Save computerphysicslab/2460d47bebf7c5d51e194f84e78934d5 to your computer and use it in GitHub Desktop.
Save computerphysicslab/2460d47bebf7c5d51e194f84e78934d5 to your computer and use it in GitHub Desktop.
// 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