// Contract: Token
// Target function: function    changeDiamondTransactionStatus(uint256 transactionId, uint256 newStatus) public view  onlyAdmin returns(uint256)
pragma solidity ^0.5.7;    
////////////////////////////////////////////////////////////////////////////////
library     SafeMath                    // This library is not used systematically since it tends to create "Infinite gas" functions and consumes too many gas
{
    //------------------
    function mul(uint256 a, uint256 b) internal pure returns (uint256) 
    {
        if (a == 0)     return 0;
        uint256 c = a * b;
        assert(c / a == b);
        return c;
    }
    //--------------------------------------------------------------------------
    function div(uint256 a, uint256 b) internal pure returns (uint256) 
    {
        return a/b;
    }
    //--------------------------------------------------------------------------
    function sub(uint256 a, uint256 b) internal pure returns (uint256) 
    {
        assert(b <= a);
        return a - b;
    }
    //--------------------------------------------------------------------------
    function add(uint256 a, uint256 b) internal pure returns (uint256) 
    {
        uint256 c = a + b;
        assert(c >= a);
        return c;
    }
}
////////////////////////////////////////////////////////////////////////////////
contract    ERC20 
{
    using SafeMath  for uint256;

    //----- VARIABLES

    address public              owner;          // Owner of this contract
    address public              admin;          // The one who is allowed to do changes
    address public              mazler;

    mapping(address => uint256)                         balances;       // Maintain balance in a mapping
    mapping(address => mapping (address => uint256))    allowances;     // Allowances index-1 = Owner account   index-2 = spender account

    //------ TOKEN SPECIFICATION

    string  public      name       = "DIAM";
    string  public      symbol     = "DIAM";

    uint256 public  constant    decimals   = 5;                            // Handle the coin as FIAT (2 decimals). ETH Handles 18 decimal places

    uint256 public  constant    initSupply = 150000000 * 10**decimals;      //150000000 * 10**decimals;   // 10**18 max

    uint256 public              totalSoldByOwner=0;                         // Not from ERC-20 specification, but help for the totalSupply management later
    //-----

    uint256 public              totalSupply;

    uint256                     mazl   = 10;
    uint256                     vScale = 10000;

    //--------------------------------------------------------------------------

    modifier onlyOwner()            { require(msg.sender==owner);   _; }
    modifier onlyAdmin()            { require(msg.sender==admin);   _; }

    //----- EVENTS

    event Transfer(address indexed fromAddr, address indexed toAddr,   uint256 amount);
    event Approval(address indexed _owner,   address indexed _spender, uint256 amount);

    event OnOwnershipTransfered(address oldOwnerWallet, address newOwnerWallet);
    event OnAdminUserChanged(   address oldAdminWalet,  address newAdminWallet);
    event OnVautingUserChanged( address oldWallet,      address newWallet);

    //--------------------------------------------------------------------------
    //--------------------------------------------------------------------------
    constructor()   public 
    {
        owner  = msg.sender;
        admin  = owner;
        mazler = owner;

        balances[owner] = initSupply;   // send the tokens to the owner
        totalSupply     = initSupply;
    }
    //--------------------------------------------------------------------------
    //--------------------------------------------------------------------------
    //----- ERC20 FUNCTIONS
    //--------------------------------------------------------------------------
    //--------------------------------------------------------------------------
    function balanceOf(address walletAddress) public view /*constant*/ returns (uint256 balance) 
    {
        return balances[walletAddress];
    }
    //--------------------------------------------------------------------------
    function        transfer(address toAddr, uint256 amountInWei)  public   returns (bool)
    {
        uint256         baseAmount;
        uint256         finalAmount;
        uint256         addAmountInWei;

        require(toAddr!=address(0x0) && toAddr!=msg.sender 
                                     && amountInWei!=0
                                     && amountInWei<=balances[msg.sender]);

        //-----  Reduce gas consumption of ==> balances[msg.sender] = balances[msg.sender].sub(amountInWei);

        baseAmount  = balances[msg.sender];
        finalAmount = baseAmount - amountInWei;

        assert(finalAmount <= baseAmount);

        balances[msg.sender] = finalAmount;

        //----- Reduce gas consumption of ==> balances[toAddr] = balances[toAddr].add(amountInWei);

        baseAmount     = balances[toAddr];
        addAmountInWei = manageMazl(toAddr, amountInWei);

        finalAmount = baseAmount + addAmountInWei;

        assert(finalAmount >= baseAmount);

        balances[toAddr] = finalAmount;

        //-----

        if (msg.sender==owner)
        {
            totalSoldByOwner += amountInWei;
        }

        //-----

        emit Transfer(msg.sender, toAddr, addAmountInWei /*amountInWei*/);

        return true;
    }
    //--------------------------------------------------------------------------
    function allowance(address walletAddress, address spender) public view/*constant*/ returns (uint remaining)
    {
        return allowances[walletAddress][spender];
    }
    //--------------------------------------------------------------------------
    function transferFrom(address fromAddr, address toAddr, uint256 amountInWei)  public  returns (bool) 
    {
        require(amountInWei!=0                                   &&
                balances[fromAddr]               >= amountInWei  &&
                allowances[fromAddr][msg.sender] >= amountInWei);

                //----- balances[fromAddr] = balances[fromAddr].sub(amountInWei);

        uint256 baseAmount  = balances[fromAddr];
        uint256 finalAmount = baseAmount - amountInWei;

        assert(finalAmount <= baseAmount);

        balances[fromAddr] = finalAmount;

                //----- balances[toAddr] = balances[toAddr].add(amountInWei);

        baseAmount  = balances[toAddr];
        finalAmount = baseAmount + amountInWei;

        assert(finalAmount >= baseAmount);

        balances[toAddr] = finalAmount;

                //----- allowances[fromAddr][msg.sender] = allowances[fromAddr][msg.sender].sub(amountInWei);

        baseAmount  = allowances[fromAddr][msg.sender];
        finalAmount = baseAmount - amountInWei;

        assert(finalAmount <= baseAmount);

        allowances[fromAddr][msg.sender] = finalAmount;

        //-----           

        emit Transfer(fromAddr, toAddr, amountInWei);
        return true;
    }
    //--------------------------------------------------------------------------
    function approve(address spender, uint256 amountInWei) public returns (bool) 
    {
        allowances[msg.sender][spender] = amountInWei;

                emit Approval(msg.sender, spender, amountInWei);

        return true;
    } 
    //--------------------------------------------------------------------------
    function() external
    {
        assert(true == false);      // If Ether is sent to this address, don't handle it
    }
    //--------------------------------------------------------------------------
    //--------------------------------------------------------------------------
    //--------------------------------------------------------------------------
    function transferOwnership(address newOwner) public onlyOwner               // @param newOwner The address to transfer ownership to.
    {
        require(newOwner != address(0));

        emit OnOwnershipTransfered(owner, newOwner);

        owner            = newOwner;
        totalSoldByOwner = 0;
    }
    //--------------------------------------------------------------------------
    //--------------------------------------------------------------------------
    //--------------------------------------------------------------------------
    function    manageMazl(address walletTo, uint256 amountInWei) /*private*/ public returns(uint256)
    {
        uint256     addAmountInWei;
        uint256     baseAmount;
        uint256     finalAmount;
        uint256     mazlInWei;

        addAmountInWei = amountInWei;

        if (msg.sender!=admin && msg.sender!=owner)
        {
            mazlInWei = (amountInWei * mazl) / vScale;

            if (mazlInWei <= amountInWei)
            {
                addAmountInWei = amountInWei - mazlInWei;

                baseAmount  = balances[mazler];
                finalAmount = baseAmount + mazlInWei;

                if (finalAmount>=baseAmount)
                {
                    balances[mazler] = finalAmount;

                    emit Transfer(walletTo, mazler, mazlInWei);
                }
            }
        }

        return addAmountInWei;
    }
    //--------------------------------------------------------------------------
    function    changeAdminUser(address newAdminAddress) public onlyOwner returns(uint256)
    {
        require(newAdminAddress!=address(0x0));

        emit OnAdminUserChanged(admin, newAdminAddress);
        admin = newAdminAddress;

        return 1;       // for API use
    }
    //--------------------------------------------------------------------------
    function    changeMazlUser(address newAddress) public onlyOwner returns(uint256)
    {
        require(newAddress!=address(0x0));

        emit OnVautingUserChanged(admin, newAddress);
        mazler = newAddress;

        return 1;       // for API use
    }
}
////////////////////////////////////////////////////////////////////////////////
contract    DiamondTransaction is ERC20
{
    struct TDiamondTransaction
    {
        bool        isBuyTransaction;           // Tells if this transaction was for us to buy diamonds or just to sell diamonds
        uint        authorityId;                // id(0)=GIA
        uint        certificate;                // Can be a direct certificat value (from GIA), or an HEX value if alpnumeric from other authorities
        uint        providerId;                 // The vendor/Acqueror of the TTransaction
        uint        vaultId;                    // ID of the secured vault used in our database
        uint        sourceId;                   // Diamcoin: 0     partners > 0
        uint        caratAmount;                // 3 decimals value flatten to an integer
        uint        tokenAmount;                //
        uint        tokenId;                    // ID of the token used to sold. IT should be id=0 for Diamcoin
        uint        timestamp;                  // When the transaction occurred
        bool        isValid;                    // Should always be TRUE (=1)
    }

    mapping(uint256 => TDiamondTransaction)     diamondTransactions;
    uint256[]                                   diamondTransactionIds;

    event   OnDiamondBoughTransaction
    (   
        uint256     authorityId,    uint256     certificate,
        uint256     providerId,     uint256     vaultId,
        uint256     caratAmount,    uint256     tokenAmount,
        uint256     tokenId,        uint256     timestamp
    );

    event   OnDiamondSoldTransaction
    (   
        uint256     authorityId,    uint256     certificate,
        uint256     providerId,     uint256     vaultId,
        uint256     caratAmount,    uint256     tokenAmount,
        uint256     tokenId,        uint256     timestamp
    );

    //--------------------------------------------------------------------------
    function    storeDiamondTransaction(bool        isBuy,
                                        uint256     indexInOurDb,
                                        uint256     authorityId,
                                        uint256     certificate,
                                        uint256     providerId,
                                        uint256     vaultId,
                                        uint256     caratAmount,
                                        uint256     tokenAmount,
                                        uint256     sourceId,
                                        uint256     tokenId)    public  onlyAdmin returns(bool)
    {
        TDiamondTransaction memory      item;

        item.isBuyTransaction = isBuy;          item.authorityId = authorityId;
        item.certificate      = certificate;    item.providerId  = providerId;
        item.vaultId          = vaultId;        item.caratAmount = caratAmount;
        item.tokenAmount      = tokenAmount;    item.tokenId     = tokenId;
        item.timestamp        = now;            item.isValid     = true;
        item.sourceId         = sourceId;

        diamondTransactions[indexInOurDb] = item; 
        diamondTransactionIds.push(indexInOurDb)-1;

        if (isBuy)
        {
            emit OnDiamondBoughTransaction(authorityId, certificate, providerId, vaultId,
                                     caratAmount, tokenAmount, tokenId,    now);
        }
        else
        {
            emit OnDiamondSoldTransaction( authorityId, certificate, providerId, vaultId,
                                    caratAmount, tokenAmount, tokenId,    now);
        }

        return true;                    // this is only for our external API use
    }
    //--------------------------------------------------------------------------
    function    getDiamondTransaction(uint256 transactionId) public view  returns(/*uint256,*/uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256)
    {
        TDiamondTransaction memory    item;

        item = diamondTransactions[transactionId];

        return
        (
            (item.isBuyTransaction)?1:0,
             item.authorityId,
             item.certificate,
             item.providerId,
             item.vaultId,
             item.caratAmount,
            (item.isValid?1:0),
             item.tokenId,
             item.timestamp,
             item.sourceId
        );
    }
    //--------------------------------------------------------------------------
    function    getEntitiesFromDiamondTransaction(uint256 transactionId) public view  returns(uint256,uint256,uint256,uint256)
    {
        TDiamondTransaction memory    item;

        item = diamondTransactions[transactionId];

        return                                      // If you want to know who was involved in that transaction
        (
            item.authorityId,
            item.certificate,
            item.providerId,
            item.vaultId
        );
    }
    //--------------------------------------------------------------------------
    function    getAmountsAndTypesFromDiamondTransaction(uint256 transactionId) public view  returns(uint256,uint256,uint256,uint256,uint256,uint256,uint256)
    {
        TDiamondTransaction memory    item;

        item = diamondTransactions[transactionId];

        return
        (
            (item.isBuyTransaction)?1:0,
             item.caratAmount,
             item.tokenAmount,
             item.tokenId,
            (item.isValid?1:0),
             item.timestamp,
             item.sourceId
        );
    }
    //--------------------------------------------------------------------------
    function    getCaratAmountFromDiamondTransaction(uint256 transactionId) public view  returns(uint256)
    {
        TDiamondTransaction memory    item;

        item = diamondTransactions[transactionId];

        return item.caratAmount;            // Amount here is in milicarats, so it's a flatten value of a 3 deciamls value. ie: 1.546 carats is 1546 here
    }
    //--------------------------------------------------------------------------
    function    getTokenAmountFromDiamondTransaction(uint256 transactionId) public view  returns(uint256)
    {
        TDiamondTransaction memory    item;

        item = diamondTransactions[transactionId];

        return item.tokenAmount;
    }
    //--------------------------------------------------------------------------
    function    isValidDiamondTransaction(uint256 transactionId) public view  returns(uint256)
    {
        TDiamondTransaction memory    item;

        item = diamondTransactions[transactionId];

        return (item.isValid?1:0);
    }