// File: BlobDefinition.sol



pragma solidity 0.5.10;

import './LibAddress.sol';
import './LibInteger.sol';

/**
 * @title BlobDefinition 
 * @dev HBD token contract adhering to ERC721 standard
 */
contract BlobDefinition
{
    using LibAddress for address;
    using LibInteger for uint;

    event Transfer(address indexed from, address indexed to, uint indexed id);
    event Approval(address indexed owner, address indexed approved, uint indexed id);
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev The admin of the contract
     */
    address payable private _admin;

    /**
     * @dev The total minted tokens
     */
    uint private _supply;

    /**
     * @dev The base url of token info page
     */
    string private _base_uri;

    /**
     * @dev Permitted addresses to carry out special functions
     */
    mapping (address => bool) private _permissions;

    /**
     * @dev Number of tokens held by an address
     */
    mapping (address => uint) private _token_balances;

    /**
     * @dev Owners of each token
     */
    mapping (uint => address) private _token_owners;

    /**
     * @dev Approved third party addresses for each token
     */
    mapping (uint => address) private _token_approvals;

    /**
     * @dev Approved third party addresses to manage all tokens belonging to some address
     */
    mapping (address => mapping (address => bool)) private _token_operators;

    /**
     * @dev The name of token
     */
    string private constant _name = "Hash Blob Definition";

    /**
     * @dev The symbol of token
     */
    string private constant _symbol = "HBD";

    /**
     * Maximum number of tokens to be minted
     */
    uint private constant _max_supply = 16384;

    /**
     * @dev Initialise the contract
     */
    constructor () public
    {
        //The contract creator becomes the admin
        _admin = msg.sender;
    }

    /**
     * @dev Allow access only for the admin of contract
     */
    modifier onlyAdmin()
    {
        require(msg.sender == _admin);
        _;
    }

    /**
     * @dev Allow access only for the permitted addresses
     */
    modifier onlyPermitted()
    {
        require(_permissions[msg.sender]);
        _;
    }

    /**
     * @dev Give or revoke permission of accounts
     * @param account The address to change permission
     * @param permission True if the permission should be granted, false if it should be revoked
     */
    function permit(address account, bool permission) public onlyAdmin
    {
        _permissions[account] = permission;
    }

    /**
     * @dev Withdraw from the balance of this contract
     * @param amount The amount to be withdrawn, if zero is provided the whole balance will be withdrawn
     */
    function clean(uint amount) public onlyAdmin
    {
        if (amount == 0){
            _admin.transfer(address(this).balance);
        } else {
            _admin.transfer(amount);
        }
    }

    /**
     * @dev Set the base uri
     * @param uri The base uri
     */
    function base(string memory uri) public onlyAdmin
    {
        _base_uri = uri;
    }

    /**
     * @dev Move token from one account to another
     * @param from The token sender address
     * @param to The recipient address
     * @param id The token id to transfer
     */
    function transferFrom(address from, address to, uint id) public
    {
        //The token must exist
        require(_isExist(id));

        //Caller must be approved or the owner of token
        require(_isApprovedOrOwner(msg.sender, id));

        //Do the transfer
        _send(from, to, id, "");
    }

    /**
     * @dev Safely move token from one account to another
     * @param from The token sender address
     * @param to The recipient address
     * @param id The token id to transfer
     */
    function safeTransferFrom(address from, address to, uint id) public
    {
        //The token must exist
        require(_isExist(id));

        //Caller must be approved or the owner of token
        require(_isApprovedOrOwner(msg.sender, id));

        //Do the transfer
        _send(from, to, id, "");
    }

    /**
     * @dev Safely move token from one account to another
     * @param from The token sender address
     * @param to The recipient address
     * @param id The token id to transfer
     * @param data Additional extra data
     */
    function safeTransferFrom(address from, address to, uint id, bytes memory data) public
    {
        //The token must exist
        require(_isExist(id));

        //Caller must be approved or the owner of token
        require(_isApprovedOrOwner(msg.sender, id));

        //Do the transfer
        _send(from, to, id, data);
    }

    /**
     * @dev Allow a third party to transfer caller's token
     * @param to The address to allow
     * @param id The token id to allow transfer
     */
    function approve(address to, uint id) public
    {
        //The token must exist
        require(_isExist(id));

        //No need to approve the owner
        address owner = ownerOf(id);
        require(to != owner);

        //Caller must be the owner of token or an approved operator
        require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
        
        //Grant approval
        _token_approvals[id] = to;

        //Emit events
        emit Approval(owner, to, id);
    }

    /**
     * @dev Grant or revoke approval for a third party to transfer all of caller's tokens
     * @param to The address to grant or revoke
     * @param approved Grant or revoke approval
     */
    function setApprovalForAll(address to, bool approved) public
    {
        //Cannot set own settings
        require(to != msg.sender);

        //Grant or revoke approval
        _token_operators[msg.sender][to] = approved;

        //Emit events
        emit ApprovalForAll(msg.sender, to, approved);
    }

    /**
     * @dev Mint tokens
     * @param to The owner of token
     */
    function mint(address to) public onlyPermitted returns (uint)
    {
        //No point of minting to zero address
        require(!to.isOriginAddress());

        //Must not exceed the maximum available tokens to be minted
        require(_supply < _max_supply);

        //Mint the token
        uint id = _supply.add(1);
        _token_owners[id] = to;
        _token_balances[to] = _token_balances[to].add(1);

        //Increment supply
        _supply = id;

        //Emit events
        emit Transfer(address(0), to, id);

        return id;
    }

    /**
     * @dev Enable tokens
     * @param to The owner of token
     * @param id The token id to enable
     */
    function enable(address to, uint id) public onlyPermitted
    {
        //Must be a valid id
        require(id > 0);

        //No point of minting to zero address
        require(!to.isOriginAddress());

        //Token does not already exists
        require(!_isExist(id));

        //Enable the token
        _token_owners[id] = to;
        _token_balances[to] = _token_balances[to].add(1);

        //Emit events
        emit Transfer(address(0), to, id);
    }

    /**
     * @dev Burn tokens
     * @param owner The owner of token
     * @param id The token id to burn
     */
    function disable(address owner, uint id) public onlyPermitted
    {
        //Token must exist
        require(_isExist(id));

        //Token must be owned by sent in address
        require(ownerOf(id) == owner);

        //Disable the token
        _token_approvals[id] = address(0);
        _token_balances[owner] = _token_balances[owner].sub(1);
        _token_owners[id] = address(0);

        //Emit events
        emit Transfer(owner, address(0), id);
    }

    /**
     * @dev Safely move token from one account to another
     * @param from The token sender address
     * @param to The recipient address
     * @param id The token id to transfer
     */
    function move(address from, address to, uint id) public onlyPermitted
    {
        //The token must exist
        require(_isExist(id));

        //Do the transfer
        _send(from, to, id, "");
    }

    /**
     * @dev Get the approved address for a token
     * @param id The token id
     * @return address The approved address
     */
    function getApproved(uint id) public view returns (address)
    {
        return _token_approvals[id];
    }

    /**
     * @dev Check whether the provided operator is approved to manage owner's tokens
     * @param owner The token owner address
     * @param operator The operator address to check against
     * @return bool True if the operator is approved, otherwise false
     */
    function isApprovedForAll(address owner, address operator) public view returns (bool)
    {
        return _token_operators[owner][operator];
    }

    /**
     * @dev Get number of tokens belonging to an account
     * @param account The address of account to check
     * @return uint The tokens balance
     */
    function balanceOf(address account) public view returns (uint)
    {
        return _token_balances[account];
    }

    /**
     * @dev Get the owner of a token
     * @param id The id of token
     * @return address The owner of token
     */
    function ownerOf(uint id) public view returns (address)
    {
        return _token_owners[id];
    }

    /**
     * @dev Get the url of token info page
     * @param id The id of token
     * @return string The url of token info page
     */
    function tokenURI(uint id) public view returns (string memory)
    {
        if(_isExist(id)) {
            return string(abi.encodePacked(_base_uri, id.toString()));
        } else {
            return "";
        }
    }

    /**
     * @dev Get the total number of tokens in existance
     * @return uint Number of tokens
     */
    function totalSupply() public view returns (uint)
    {
        return _supply;
    }

    /**
     * @dev Get the maximum number of tokens minted
     * @return uint Maximum number of tokens
     */
    function maxSupply() public pure returns (uint)
    {
        return _max_supply;
    }

    /**
     * @dev Get name of token
     * @return string The name
     */
    function name() public pure returns (string memory)
    {
        return _name;
    }

    /**
     * @dev Get symbol of token
     * @return string The symbol
     */
    function symbol() public pure returns (string memory)
    {
        return _symbol;
    }

    /**
     * @dev Check whether the provided address is permitted
     * @param account The address to check
     * @return bool True if the address is permitted, otherwise false
     */
    function isPermitted(address account) public view returns (bool)
    {
        return _permissions[account];
    }

    /**
     * @dev Check whether the token exists
     * @param id The id of token
     * @return bool True if the token exists, otherwise false
     */
    function _isExist(uint id) private view returns (bool)
    {
        return ownerOf(id) != address(0);
    }

    /**
     * @dev Check whether the provided address is the owner of token or a approved address
     * @param spender The address to check
     * @param id The id of token
     * @return bool True if the provided address is the owner of token or a approved address, otherwise false
     */
    function _isApprovedOrOwner(address spender, uint id) private view returns (bool)
    {
        address owner = ownerOf(id);
        return (spender == owner || getApproved(id) == spender || isApprovedForAll(owner, spender));
    }

    /**
     * @dev Transfer tokens from one account to another
     * @param from The token owner
     * @param to The token receiver
     * @param id The token id to transfer
     * @param data Additional data to add to the transaction
     */
    function _send(address from, address to, uint id, bytes memory data) private
    {
        //The token must be owned by the provided address
        require(ownerOf(id) == from);

        //No point of transferring to zero address
        require(!to.isOriginAddress());

        //Do nothing with the data
        delete data;

        //Clear approvals
        _token_approvals[id] = address(0);

        //Reduce the balance from owner
        _token_balances[from] = _token_balances[from].sub(1);

        //Increase the balance of receiver
        _token_balances[to] = _token_balances[to].add(1);

        //Set the new owner
        _token_owners[id] = to;

        //Emit events
        emit Transfer(from, to, id);
    }
}
// File: BlobFormation.sol



pragma solidity 0.5.10;

import './LibInteger.sol';

/**
 * @title BlobFormation 
 * @dev HBF token contract adhering to ERC20 standard
 */
contract BlobFormation
{
    using LibInteger for uint;

    event Transfer(address indexed from, address indexed to, uint value);
    event Approval(address indexed owner, address indexed spender, uint value);

    /**
     * @dev The admin of the contract
     */
    address payable private _admin;

    /**
     * @dev The current supply of token
     */
    uint private _supply;

    /**
     * @dev Permitted addresses to carry out special functions
     */
    mapping (address => bool) private _permissions;

    /**
     * @dev Number of tokens held by an address
     */
    mapping (address => uint) private _token_balances;

    /**
     * @dev Approved allowances for third party addresses
     */
    mapping (address => mapping(address => uint)) private _token_allowances;

    /**
     * Number of decimals in base currency
     */
    uint private constant _decimals = 18;

    /**
     * @dev The Maximum supply of token
     */
    uint private constant _max_supply = 400000 * 10**_decimals;

    /**
     * @dev The name of token
     */
    string private constant _name = "Hash Blob Formation";

    /**
     * @dev The symbol of token
     */
    string private constant _symbol = "HBF";

    /**
     * @dev Initialise the contract
     */
    constructor() public
    {
        //The contract creator becomes the admin
        _admin = msg.sender;

        //Mint max supply and send it to the admin
        _supply = _max_supply;
        _token_balances[_admin] = _supply;
        emit Transfer(address(0), _admin, _supply);
    }

    /**
     * @dev Allow access only for the admin of contract
     */
    modifier onlyAdmin()
    {
        require(msg.sender == _admin);
        _;
    }

    /**
     * @dev Allow access only for the permitted addresses
     */
    modifier onlyPermitted()
    {
        require(_permissions[msg.sender]);
        _;
    }

    /**
     * @dev Give or revoke permission of accounts
     * @param account The address to change permission
     * @param permission True if the permission should be granted, false if it should be revoked
     */
    function permit(address account, bool permission) public onlyAdmin
    {
        _permissions[account] = permission;
    }

    /**
     * @dev Withdraw from the balance of this contract
     * @param amount The amount to be withdrawn, if zero is provided the whole balance will be withdrawn
     */
    function clean(uint amount) public onlyAdmin
    {
        if (amount == 0){
            _admin.transfer(address(this).balance);
        } else {
            _admin.transfer(amount);
        }
    }

    /**
     * @dev Moves tokens from the caller's account to someone else
     * @param to The recipient address
     * @param value The number of tokens to send
     */
    function transfer(address to, uint value) public
    {
        _send(msg.sender, to, value);
    }

    /**
     * @dev Sets amount of tokens spender is allowed to transfer from caller's tokens
     * @param spender The spender to allow
     * @param value The number of tokens to allow
     */
    function approve(address spender, uint value) public
    {
        _token_allowances[msg.sender][spender] = value;
        emit Approval(msg.sender, spender, value);
    }

    /**
     * @dev Moves tokens using the allowance mechanism
     * @param from The owner of tokens
     * @param to The recipient address
     * @param value The number of tokens to send
     */
    function transferFrom(address from, address to, uint value) public
    {
        _token_allowances[from][msg.sender] = _token_allowances[from][msg.sender].sub(value);
        _send(from, to, value);
    }

    /**
     * @dev Burn tokens forever
     * @param from The owner of tokens
     * @param value The number of tokens to be burned
     */
    function burn(address from, uint value) public onlyPermitted
    {
        //Must set number of tokens that needs to be burned
        require(value > 0);

        //The total supply must be greater than the tokens that needs to be burned
        require(_supply >= value);

        //The owner must have enough tokens available
        require(_token_balances[from] >= value);

        //Reduce supply
        _supply = _supply.sub(value);

        //Reduce owner's balance
        _token_balances[from] = _token_balances[from].sub(value);

        //Emit events and return
        emit Transfer(from, address(0), value);
    }

    /**
     * @dev Get the total number of tokens in existance
     * @return uint Number of tokens
     */
    function totalSupply() public view returns (uint)
    {
        return _supply;
    }

    /**
     * @dev Get the maximum number of tokens minted
     * @return uint Maximum number of tokens
     */
    function maxSupply() public pure returns (uint)
    {
        return _max_supply;
    }

    /**
     * @dev Get the total number of tokens spender is allowed to spend out of owner's tokens
     * @param owner The tokens owner
     * @param spender The allowed spender
     * @return uint Number of tokens allowed to spend
     */
    function allowance(address owner, address spender) public view returns (uint)
    {
        return _token_allowances[owner][spender];
    }

    /**
     * @dev Get number of tokens belonging to an account
     * @param account The address of account to check
     * @return uint The tokens balance
     */
    function balanceOf(address account) public view returns (uint)
    {
        return _token_balances[account];
    }

    /**
     * @dev Get name of token
     * @return string The name
     */
    function name() public pure returns (string memory)
    {
        return _name;
    }

    /**
     * @dev Get symbol of token
     * @return string The symbol
     */
    function symbol() public pure returns (string memory)
    {
        return _symbol;
    }

    /**
     * @dev Get number of decimals of token
     * @return uint The decimals count
     */
    function decimals() public pure returns (uint)
    {
        return _decimals;
    }

    /**
     * @dev Check whether the provided address is permitted
     * @param account The address to check
     * @return bool True if the address is permitted, otherwise false
     */
    function isPermitted(address account) public view returns (bool)
    {
        return _permissions[account];
    }

    /**
     * @dev Transfer tokens from one account to another
     * @param from The token owner
     * @param to The token receiver
     * @param value The number of tokens to transfer
     */
    function _send(address from, address to, uint value) private
    {
        //Reduce the balance from owner
        _token_balances[from] = _token_balances[from].sub(value);

        //Increase the balance of receiver
        _token_balances[to] = _token_balances[to].add(value);

        //Emit events
        emit Transfer(from, to, value);
    }
}
// File: BlobManager.sol



pragma solidity 0.5.10;

import './LibInteger.sol';
import './LibBlob.sol';
import './InterfaceProduce.sol';
import './BlobDefinition.sol';
import './BlobFormation.sol';
import './BlobStorage.sol';

/**
 * @title BlobManager 
 * @dev Manage the core functionalities of blobs
 */
contract BlobManager
{
    using LibInteger for uint;

    BlobDefinition private _definition;
    BlobFormation private _formation;
    BlobStorage private _storage;
    InterfaceProduce private _producer;

    event Log(address indexed account, uint indexed blob, string action, uint data1, uint data2);

    /**
     * @dev The admin of the contract
     */
    address payable private _admin;

    /**
     * @dev The address of the definition token contract
     */
    address private _definition_contract;
    
    /**
     * @dev The address of the formation token contract
     */
    address private _formation_contract;

    /**
     * @dev The address of the blob storage contract
     */
    address private _storage_contract;

    /**
     * @dev The address of the blob producer contract
     */
    address private _producer_contract;

    /**
     * Number of tokens per minting segment
     */
    uint private constant _tokens_per_segment = 2048;

    /**
     * Starting price of minting
     */
    uint private constant _minting_starting_price = 10**17;

    /**
     * Minting price increment per segment
     */
    uint private constant _minting_price_increment = 10**17;

    /**
     * Formation grant multiplier when minting
     */
    uint private constant _minting_grant_multiplier = 10**18;

    /**
     * The formation tokens charged for merging, splitting and renaming
     */
    uint private constant _transformation_token_fee = 10**18;

    /**
     * The minimum transformation fee charged if payed by native currency
     */
    uint private constant _transformation_native_fee_min = 40 * 10**15;

    /**
     * The transformation fee increment per segment
     */
    uint private constant _transformation_native_fee_increment = 10**15;

    /**
     * The transformation fee increment gap
     */
    uint private constant _transformation_native_fee_gap = 2500 * 10**18;

    /**
     * The original minter selling fee percentage
     */
    uint private constant _minter_selling_fee_percentage = 2;

    /**
     * @dev Initialise the contract
     */
    constructor() public
    {
        //The contract creator becomes the admin
        _admin = msg.sender;
    }

    /**
     * @dev Allow access only for the admin of contract
     */
    modifier onlyAdmin()
    {
        require(msg.sender == _admin);
        _;
    }

    /**
     * @dev Withdraw from the balance of this contract
     * @param amount The amount to be withdrawn, if zero is provided the whole balance will be withdrawn
     */
    function clean(uint amount) public onlyAdmin
    {
        if (amount == 0){
            _admin.transfer(address(this).balance);
        } else {
            _admin.transfer(amount);
        }
    }

    /**
     * @dev Withdraw formation tokens of this contract
     * @param amount The amount to be withdrawn, if zero is provided the whole formation token balance will be withdrawn
     */
    function empty(uint amount) public onlyAdmin
    {
        if (amount == 0){
            _formation.transfer(_admin, _formation.balanceOf(address(this)));
        } else {
            _formation.transfer(_admin, amount);
        }
    }

    /**
     * @dev Update definition contract reference
     * @param account The address of contract
     */
    function setDefinition(address account) public onlyAdmin
    {
        _definition_contract = account;
        _definition = BlobDefinition(account);
    }

    /**
     * @dev Update formation contract reference
     * @param account The address of contract
     */
    function setFormation(address account) public onlyAdmin
    {
        _formation_contract = account;
        _formation = BlobFormation(account);
    }

    /**
     * @dev Update storage contract reference
     * @param account The address of contract
     */
    function setStorage(address account) public onlyAdmin
    {
        _storage_contract = account;
        _storage = BlobStorage(account);
    }

    /**
     * @dev Update producer contract reference
     * @param account The address of contract
     */
    function setProducer(address account) public onlyAdmin
    {
        _producer_contract = account;
        _producer = InterfaceProduce(account);
    }

    /**
     * @dev Mint new blobs
     */
    function mint() public payable
    {        
        //Must pay for minting
        require(msg.value > 0);

        //Get currently minted tokens
        uint minted = _definition.totalSupply();

        //Must pay the price
        require(_calcSegmentPrice(minted) == msg.value);

        //Grant formation tokens
        _formation.transfer(msg.sender, _calcSegmentGrant(minted));

        //Mint the token
        uint id = _definition.mint(msg.sender);

        //Save metadata
        _storage.incrementMetadata(id, _producer.init(id));

        //Save original minter
        _storage.setMinter(id, msg.sender);

        //Transfer fee
        _admin.transfer(msg.value.mul(90).div(100));

        //Emit events
        emit Log(msg.sender, id, "mint", msg.value, 0);
    }

    /**
     * @dev Merge two blobs
     * @param father The father blob id
     * @param mother The mother blob id
     */
    function merge(uint father, uint mother, uint[] memory params) public payable
    {
        //The parents must be owned by the sender
        require(msg.sender == _definition.ownerOf(father));
        require(msg.sender == _definition.ownerOf(mother));

        //The parents must not be the same
        require(father != mother);

        //Blobs must not be currently listed
        require(_storage.getListing(father) == 0);
        require(_storage.getListing(mother) == 0);

        //Parameters should be valid
        require(params.length == 6);
        for (uint i = 0; i < 6; i++) {
            require(params[i] == father || params[i] == mother);
        }

        //Read metadata of parents
        LibBlob.Metadata memory father_metadata = LibBlob.uintToMetadata(_storage.getLatestMetadata(father));
        LibBlob.Metadata memory mother_metadata = LibBlob.uintToMetadata(_storage.getLatestMetadata(mother));

        //Both father and mother should be in same level
        require(father_metadata.level == mother_metadata.level);

        //Cannot merge further after reaching level six
        require(father_metadata.level < 6 && mother_metadata.level < 6);

        //Setup partners
        father_metadata.partner = mother;
        mother_metadata.partner = father;

        //Merging increases the level of blob
        father_metadata.level = father_metadata.level.add(1);
        mother_metadata.level = mother_metadata.level.add(1);

        //Setting up father parameters
        father_metadata.param1 = (params[0] == father) ? father_metadata.param1 : mother_metadata.param1;
        father_metadata.param2 = (params[1] == father) ? father_metadata.param2 : mother_metadata.param2;
        father_metadata.param3 = (params[2] == father) ? father_metadata.param3 : mother_metadata.param3;
        father_metadata.param4 = (params[3] == father) ? father_metadata.param4 : mother_metadata.param4;
        father_metadata.param5 = (params[4] == father) ? father_metadata.param5 : mother_metadata.param5;
        father_metadata.param6 = (params[5] == father) ? father_metadata.param6 : mother_metadata.param6;

        //Setting up mother parameters
        mother_metadata.param1 = (params[0] == father) ? father_metadata.param1 : mother_metadata.param1;
        mother_metadata.param2 = (params[1] == father) ? father_metadata.param2 : mother_metadata.param2;
        mother_metadata.param3 = (params[2] == father) ? father_metadata.param3 : mother_metadata.param3;
        mother_metadata.param4 = (params[3] == father) ? father_metadata.param4 : mother_metadata.param4;
        mother_metadata.param5 = (params[4] == father) ? father_metadata.param5 : mother_metadata.param5;
        mother_metadata.param6 = (params[5] == father) ? father_metadata.param6 : mother_metadata.param6;

        //Charge for the action
        if (msg.value > 0) {
            require(msg.value == _calcTransformationNativeFee());
        } else {
            _formation.burn(msg.sender, _transformation_token_fee);
        }

        //Update metadata
        _storage.incrementMetadata(father, LibBlob.metadataToUint(father_metadata));
        _storage.incrementMetadata(mother, LibBlob.metadataToUint(mother_metadata));

        //Only father survives after merging
        _definition.disable(msg.sender, mother);
    
        //Emit events
        emit Log(msg.sender, father, "merge", msg.value, mother);
    }

    /**
     * @dev Split a blob back to its parents
     * @param id The id of blob
     */
    function split(uint id) public payable
    {
        //Blob must be owned by the sender
        require(msg.sender == _definition.ownerOf(id));

        //Blob must not be currently listed
        require(_storage.getListing(id) == 0);

        //Read metadata
        LibBlob.Metadata memory metadata = LibBlob.uintToMetadata(_storage.getLatestMetadata(id));

        //Cannot split further after reaching level one
        require(metadata.level > 1);

        //Charge for the action
        if (msg.value > 0) {
            require(msg.value == _calcTransformationNativeFee());
        } else {
            _formation.burn(msg.sender, _transformation_token_fee);
        }

        //Summon the partner
        _definition.enable(msg.sender, metadata.partner);

        //Restore metadata
        _storage.decrementMetadata(id);
        _storage.decrementMetadata(metadata.partner);
            
        //Emit events
        emit Log(msg.sender, id, "split", msg.value, metadata.partner);
    }

    /**
     * @dev Rename a blob
     * @param id The id of blob
     * @param chars The name to be set
     */
    function rename(uint id, uint[] memory chars) public payable
    {
        //Characters should be valid
        require(chars.length == 8);
        for (uint i = 0; i < 8; i++) {
            require(chars[i] >= 0 && chars[i] <= 62);
        }

        //Building name
        LibBlob.Name memory name_params;
        name_params.char1 = chars[0];
        name_params.char2 = chars[1];
        name_params.char3 = chars[2];
        name_params.char4 = chars[3];
        name_params.char5 = chars[4];
        name_params.char6 = chars[5];
        name_params.char7 = chars[6];
        name_params.char8 = chars[7];
        uint name = LibBlob.nameToUint(name_params);

        //Get current name
        uint current = _storage.getName(id);

        //The new name must be different from current name
        require(name != current);

        //The new name must be unique
        require(!_storage.isReserved(name));

        //The token must be owned by the sender
        require(msg.sender == _definition.ownerOf(id));

        //Blob must not be currently listed
        require(_storage.getListing(id) == 0);

        //Charge for the action
        if (msg.value > 0) {
            require(msg.value == _calcTransformationNativeFee());
        } else {
            _formation.burn(msg.sender, _transformation_token_fee);
        }
        
        //Release the previously reserved name
        if (current > 0) {
            _storage.setReservation(current, false);
        }

        //Set new name
        _storage.setReservation(name, true);
        _storage.setName(id, name);

        //Emit events
        emit Log(msg.sender, id, "rename", msg.value, name);
    }

    /**
     * @dev List blob for selling
     * @param id The id of blob
     * @param price The selling price
     */
    function list(uint id, uint price) public
    {
        //The price needs to be set
        require(price > 0);

        //Blob must be owned by the sender
        require(msg.sender == _definition.ownerOf(id));

        //Set selling price
        _storage.setListing(id, price); 

        //Emit events
        emit Log(msg.sender, id, "list", price, 0);
    }

    /**
     * @dev Withdraw blob from selling
     * @param id The id of blob
     */
    function withdraw(uint id) public
    {
        //Blob must be owned by the sender
        require(msg.sender == _definition.ownerOf(id));

        //Blob must be currently listed
        require(_storage.getListing(id) > 0);

        //Reset selling price
        _storage.setListing(id, 0); 

        //Emit events
        emit Log(msg.sender, id, "withdraw", 0, 0);           
    }

    /**
     * @dev Buy a listed blob
     * @param id The id of blob
     */
    function buy(uint id) public payable
    {
        uint price = _storage.getListing(id);
        uint minter_share = price.mul(_minter_selling_fee_percentage).div(100);
        uint seller_share = price.sub(minter_share);
        address payable seller = address(uint160(_definition.ownerOf(id)));

        //Blob must not be owned by the sender
        require(msg.sender != seller);

        //Blob must be currently listed
        require(price > 0);

        //Must send the buying amount
        require(msg.value == price);

        //Settle payments
        seller.transfer(seller_share);
        _storage.getMinter(id).transfer(minter_share);

        //Reset selling price
        _storage.setListing(id, 0); 

        //Transfer ownership
        _definition.move(seller, msg.sender, id);

        //Emit events
        emit Log(msg.sender, id, "buy", price, 0);           
    }

    /**
     * @dev Get common details of the system
     * @return The details
     */
    function getSystemDetails() public view returns (uint[8] memory)
    {
        uint minted = _definition.totalSupply();

        return [
            _calcSegmentId(minted),
            _calcSegmentPrice(minted),
            _calcSegmentGrant(minted),
            _calcTransformationNativeFee(),
            _formation.maxSupply(),
            _formation.totalSupply(),
            _definition.maxSupply(),
            _definition.totalSupply()
        ];
    }

    /**
     * @dev Get account related details of the system
     * @param account The account to get details about
     * @return The details
     */
    function getAccountDetails(address account) public view returns (uint[2] memory)
    {
        return [
            _definition.balanceOf(account),
            _formation.balanceOf(account)
        ];
    }

    /**
     * @dev Get blob details
     * @return The details
     */
    function getBlobDetails(uint id) public view returns (uint[] memory, uint[] memory, uint[] memory, uint, address, address)
    {
        return (
            getBlobLatestMetadataDetails(id),
            getBlobPreviousMetadataDetails(id),
            getBlobNameDetails(id),
            _storage.getListing(id),
            _storage.getMinter(id),
            _definition.ownerOf(id)
        );
    }

    /**
     * @dev Get blob latest metadata details
     * @return The details
     */
    function getBlobLatestMetadataDetails(uint id) public view returns (uint[] memory)
    {
        LibBlob.Metadata memory latest_metadata = LibBlob.uintToMetadata(_storage.getLatestMetadata(id));
        uint[] memory latest_metadata_params = new uint[](8);
        latest_metadata_params[0] = latest_metadata.partner;
        latest_metadata_params[1] = latest_metadata.level;
        latest_metadata_params[2] = latest_metadata.param1;
        latest_metadata_params[3] = latest_metadata.param2;
        latest_metadata_params[4] = latest_metadata.param3;
        latest_metadata_params[5] = latest_metadata.param4;
        latest_metadata_params[6] = latest_metadata.param5;
        latest_metadata_params[7] = latest_metadata.param6;

        return latest_metadata_params;
    }

    /**
     * @dev Get blob previous metadata details
     * @return The details
     */
    function getBlobPreviousMetadataDetails(uint id) public view returns (uint[] memory)
    {
        LibBlob.Metadata memory previous_metadata = LibBlob.uintToMetadata(_storage.getPreviousMetadata(id));
        uint[] memory previous_metadata_params = new uint[](8);
        previous_metadata_params[0] = previous_metadata.partner;
        previous_metadata_params[1] = previous_metadata.level;
        previous_metadata_params[2] = previous_metadata.param1;
        previous_metadata_params[3] = previous_metadata.param2;
        previous_metadata_params[4] = previous_metadata.param3;
        previous_metadata_params[5] = previous_metadata.param4;
        previous_metadata_params[6] = previous_metadata.param5;
        previous_metadata_params[7] = previous_metadata.param6;

        return previous_metadata_params;
    }

    /**
     * @dev Get blob name details
     * @return The details
     */
    function getBlobNameDetails(uint id) public view returns (uint[] memory)
    {
        LibBlob.Name memory name = LibBlob.uintToName(_storage.getName(id));
        uint[] memory name_params = new uint[](8);
        name_params[0] = name.char1;
        name_params[1] = name.char2;
        name_params[2] = name.char3;
        name_params[3] = name.char4;
        name_params[4] = name.char5;
        name_params[5] = name.char6;
        name_params[6] = name.char7;
        name_params[7] = name.char8;

        return name_params;
    }

    /**
     * @dev Calculates the current mining segment id
     * @param minted The number of tokens minted so far
     * @return uint The current mining segment id
     */
    function _calcSegmentId(uint minted) private pure returns (uint)
    {
        return minted.div(_tokens_per_segment);
    }

    /**
     * @dev Calculates the current mining segment price
     * @param minted The number of tokens minted so far
     * @return uint The current mining segment price
     */
    function _calcSegmentPrice(uint minted) private pure returns (uint)
    {
        return _minting_starting_price.add(_calcSegmentId(minted).mul(_minting_price_increment));
    }

    /**
     * @dev Calculates the current mining segment formation tokens grant
     * @param minted The number of tokens minted so far
     * @return uint The current mining segment formation tokens grant
     */
    function _calcSegmentGrant(uint minted) private pure returns (uint)
    {
        uint multiplier = _calcSegmentId(minted).add(2);
        return _minting_grant_multiplier.mul(multiplier.mul(multiplier.add(1))).div(2);
    }

    /**
     * @dev Calculates the native fee that should be payed for transformation
     * @return uint The current transformation native fee
     */
    function _calcTransformationNativeFee() private view returns (uint)
    {
        uint burnt_supply = _formation.maxSupply().sub(_formation.totalSupply());
        uint transformation_segment = burnt_supply.div(_transformation_native_fee_gap);

        return _transformation_native_fee_min.add(transformation_segment.mul(_transformation_native_fee_increment));
    }
}
// File: BlobStorage.sol



pragma solidity 0.5.10;

import './LibInteger.sol';
import './LibBlob.sol';

/**
 * @title BlobStorage 
 * @dev Store core details about the blobs permanently
 */
contract BlobStorage
{
    using LibInteger for uint;

    /**
     * @dev The admin of the contract
     */
    address payable private _admin;

    /**
     * @dev Permitted addresses to carry out storage functions
     */
    mapping (address => bool) private _permissions;

    /**
     * @dev Names of tokens
     */
    mapping (uint => uint) private _names;

    /**
     * @dev Listing prices of tokens
     */
    mapping (uint => uint) private _listings;

    /**
     * @dev Original minters of tokens
     */
    mapping (uint => address payable) private _minters;

    /**
     * @dev Names currently reserved
     */
    mapping (uint => bool) private _reservations;

    /**
     * @dev The metadata of blobs
     */
    mapping (uint => uint[]) private _metadata;

    /**
     * @dev Initialise the contract
     */
    constructor() public
    {
        //The contract creator becomes the admin
        _admin = msg.sender;
    }

    /**
     * @dev Allow access only for the admin of contract
     */
    modifier onlyAdmin()
    {
        require(msg.sender == _admin);
        _;
    }

    /**
     * @dev Allow access only for the permitted addresses
     */
    modifier onlyPermitted()
    {
        require(_permissions[msg.sender]);
        _;
    }

    /**
     * @dev Give or revoke permission of accounts
     * @param account The address to change permission
     * @param permission True if the permission should be granted, false if it should be revoked
     */
    function permit(address account, bool permission) public onlyAdmin
    {
        _permissions[account] = permission;
    }

    /**
     * @dev Withdraw from the balance of this contract
     * @param amount The amount to be withdrawn, if zero is provided the whole balance will be withdrawn
     */
    function clean(uint amount) public onlyAdmin
    {
        if (amount == 0){
            _admin.transfer(address(this).balance);
        } else {
            _admin.transfer(amount);
        }
    }

    /**
     * @dev Set the name of token
     * @param id The id of token
     * @param value The value to be set
     */
    function setName(uint id, uint value) public onlyPermitted
    {
        _names[id] = value;
    }

    /**
     * @dev Set the listing price of token
     * @param id The id of token
     * @param value The value to be set
     */
    function setListing(uint id, uint value) public onlyPermitted
    {
        _listings[id] = value;
    }

    /**
     * @dev Set the original minter of token
     * @param id The id of token
     * @param value The value to be set
     */
    function setMinter(uint id, address payable value) public onlyPermitted
    {
        _minters[id] = value;
    }

    /**
     * @dev Set whether the name is reserved
     * @param name The name
     * @param value True if the name is reserved, otherwise false
     */
    function setReservation(uint name, bool value) public onlyPermitted
    {
        _reservations[name] = value;
    }

    /**
     * @dev Add a new version of metadata to the token
     * @param id The token id
     * @param value The value to be set
     */
    function incrementMetadata(uint id, uint value) public onlyPermitted
    {
        _metadata[id].push(value);
    }

    /**
     * @dev Remove the latest version of metadata from token
     * @param id The token id
     */
    function decrementMetadata(uint id) public onlyPermitted
    {
        _metadata[id].length = _metadata[id].length.sub(1);
    }

    /**
     * @dev Get name of token
     * @param id The id of token
     * @return string The name
     */
    function getName(uint id) public view returns (uint)
    {
        return _names[id];
    }

    /**
     * @dev Get listing price of token
     * @param id The id of token
     * @return uint The listing price
     */
    function getListing(uint id) public view returns (uint)
    {
        return _listings[id];
    }

    /**
     * @dev Get original minter of token
     * @param id The id of token
     * @return uint The original minter
     */
    function getMinter(uint id) public view returns (address payable)
    {
        return _minters[id];
    }

    /**
     * @dev Check whether the provided name is reserved
     * @param name The name to check
     * @return bool True if the name is reserved, otherwise false
     */
    function isReserved(uint name) public view returns (bool)
    {
        return _reservations[name];
    }

    /**
     * @dev Check whether the provided address is permitted
     * @param account The address to check
     * @return bool True if the address is permitted, otherwise false
     */
    function isPermitted(address account) public view returns (bool)
    {
        return _permissions[account];
    }

    /**
     * @dev Get latest version of metadata of token
     * @param id The id of token
     * @return uint The metadata value
     */
    function getLatestMetadata(uint id) public view returns (uint)
    {
        if (_metadata[id].length > 0) {
            return _metadata[id][_metadata[id].length.sub(1)];
        } else {
            return 0;
        }
    }

    /**
     * @dev Get previous version of metadata of token
     * @param id The id of token
     * @return uint The metadata value
     */
    function getPreviousMetadata(uint id) public view returns (uint)
    {
        if (_metadata[id].length > 1) {
            return _metadata[id][_metadata[id].length.sub(2)];
        } else {
            return 0;
        }
    }
}
// File: InterfaceProduce.sol



pragma solidity 0.5.10;

/**
 * @title InterfaceProduce 
 * @dev Blob producer interface
 */
interface InterfaceProduce
{
    /**
     * @dev Initialise metadata
     * @param id The blob id
     * @return uint The generated metadata
     */
    function init(uint id) external view returns (uint);
}

// File: LibAddress.sol



pragma solidity 0.5.10;

/**
 * @title LibAddress 
 * @dev Address related utility functions
 */
library LibAddress
{
    /**
     * @dev Check whether the given address is zero address
     * @param account The address to check against
     * @return bool True if the given address is zero address
     */
    function isOriginAddress(address account) internal pure returns (bool)
    {
        return (account == address(0));
    }
}

// File: LibBlob.sol



pragma solidity 0.5.10;

/**
 * @title LibBlob
 * @dev Blob related utility functions
 */
library LibBlob
{
    struct Metadata
    {
        uint partner;
        uint level;
        uint param1;
        uint param2;
        uint param3;
        uint param4;
        uint param5;
        uint param6;
    }

    struct Name
    {
        uint char1;
        uint char2;
        uint char3;
        uint char4;
        uint char5;
        uint char6;
        uint char7;
        uint char8;
    }

    /**
     * @dev Convert metadata to a single integer
     * @param metadata The metadata to be converted
     * @return uint The integer representing the metadata
     */
    function metadataToUint(Metadata memory metadata) internal pure returns (uint)
    {
        uint params = uint(metadata.partner);
        params |= metadata.level<<32;
        params |= metadata.param1<<64;
        params |= metadata.param2<<96;
        params |= metadata.param3<<128;
        params |= metadata.param4<<160;
        params |= metadata.param5<<192;
        params |= metadata.param6<<224;

        return params;
    }

    /**
     * @dev Convert given integer to a metadata object
     * @param params The integer to be converted
     * @return Metadata The metadata represented by the integer
     */
    function uintToMetadata(uint params) internal pure returns (Metadata memory)
    {
        Metadata memory metadata;

        metadata.partner = uint(uint32(params));
        metadata.level = uint(uint32(params>>32));
        metadata.param1 = uint(uint32(params>>64));
        metadata.param2 = uint(uint32(params>>96));
        metadata.param3 = uint(uint32(params>>128));
        metadata.param4 = uint(uint32(params>>160));
        metadata.param5 = uint(uint32(params>>192));
        metadata.param6 = uint(uint32(params>>224));

        return metadata;
    }

    /**
     * @dev Convert name to a single integer
     * @param name The name to be converted
     * @return uint The integer representing the name
     */
    function nameToUint(Name memory name) internal pure returns (uint)
    {
        uint params = uint(name.char1);
        params |= name.char2<<32;
        params |= name.char3<<64;
        params |= name.char4<<96;
        params |= name.char5<<128;
        params |= name.char6<<160;
        params |= name.char7<<192;
        params |= name.char8<<224;

        return params;
    }

    /**
     * @dev Convert given integer to a name object
     * @param params The integer to be converted
     * @return Name The name represented by the integer
     */
    function uintToName(uint params) internal pure returns (Name memory)
    {
        Name memory name;

        name.char1 = uint(uint32(params));
        name.char2 = uint(uint32(params>>32));
        name.char3 = uint(uint32(params>>64));
        name.char4 = uint(uint32(params>>96));
        name.char5 = uint(uint32(params>>128));
        name.char6 = uint(uint32(params>>160));
        name.char7 = uint(uint32(params>>192));
        name.char8 = uint(uint32(params>>224));

        return name;
    }
}

// File: LibInteger.sol



pragma solidity 0.5.10;

/**
 * @title LibInteger 
 * @dev Integer related utility functions
 */
library LibInteger
{    
    /**
     * @dev Safely multiply, revert on overflow
     * @param a The first number
     * @param b The second number
     * @return uint The answer
    */
    function mul(uint a, uint b) internal pure returns (uint)
    {
        if (a == 0) {
            return 0;
        }

        uint c = a * b;
        require(c / a == b);

        return c;
    }

    /**
     * @dev Safely divide, revert if divisor is zero
     * @param a The first number
     * @param b The second number
     * @return uint The answer
    */
    function div(uint a, uint b) internal pure returns (uint)
    {
        require(b > 0, "");
        uint c = a / b;

        return c;
    }

    /**
     * @dev Safely substract, revert if answer is negative
     * @param a The first number
     * @param b The second number
     * @return uint The answer
    */
    function sub(uint a, uint b) internal pure returns (uint)
    {
        require(b <= a, "");
        uint c = a - b;

        return c;
    }

    /**
     * @dev Safely add, revert if overflow
     * @param a The first number
     * @param b The second number
     * @return uint The answer
    */
    function add(uint a, uint b) internal pure returns (uint)
    {
        uint c = a + b;
        require(c >= a, "");

        return c;
    }

    /**
     * @dev Convert number to string
     * @param value The number to convert
     * @return string The string representation
    */
    function toString(uint value) internal pure returns (string memory)
    {
        if (value == 0) {
            return "0";
        }

        uint temp = value;
        uint digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }

        bytes memory buffer = new bytes(digits);
        uint index = digits - 1;
        
        temp = value;
        while (temp != 0) {
            buffer[index--] = byte(uint8(48 + temp % 10));
            temp /= 10;
        }
        
        return string(buffer);
    }
}