📜 ⬆️ ⬇️

Ethereum smart contracts: we structure tokens as stocks

Currently, there is a real wave of cryptocurrency hyip and a succession of successful ICOs of a wide variety of projects, including those that are highly dubious or have nothing to do with decentralization and other basic blockchain principles. During the ICO, some virtual entities, tokens, are put up for sale to the public. Filling these same tokens with any real “value” is usually unique for each project. In this article, I want to consider structuring a token as a “stock”, when the holder of these tokens claims to receive dividends from the project, in proportion to the percentage of tokens in its total issue. This creates a number of legal conflicts and uncertainties, so today there is not a single large project built on this logical and understandable for investors model, but we will put the legal aspects out of the brackets and focus only on the technical implementation.

To talk generally about any structuring of a token, first of all you need to have at least some basic implementation of the token. The listing of the average contract token without frills in the Solidity language is given below:

pragma solidity ^0.4.0; contract Token { string public standard = 'Token 0.1'; string public name; //!< name for display purporses string public symbol; //!< symbol for display purporses uint8 public decimals; //!< amount of decimals for display purporses mapping (address => uint256) public balanceOf; //!< array of all balances mapping (address => mapping (address => uint256)) public allowed; uint256 totalTokens; function Token( uint256 initialSupply, string tokenName, uint8 decimalUnits, string tokenSymbol) { totalTokens = initialSupply; balanceOf[msg.sender] = initialSupply; name = tokenName; symbol = tokenSymbol; decimals = decimalUnits; } // @brief Send coins // @param _to recipient of coins // @param _value amount of coins for send function transfer(address _to, uint256 _value) { if (balanceOf[msg.sender] < _value || _value <= 0) throw; balanceOf[msg.sender] -= _value; balanceOf[_to] += _value; } // @brief Send coins // @param _from source of coins // @param _to recipient of coins // @param _value amount of coins for send function transferFrom(address _from, address _to, uint256 _value) { if (balanceOf[_from] < _value || _value <= 0) throw; if (allowed[_from][msg.sender] < _value) throw; balanceOf[_from] -= _value; balanceOf[_to] += _value; allowed[_from][msg.sender] -= _value; } // @brief Allow another contract to spend some tokens in your behalf // @param _spender another contract address // @param _value amount of approved tokens function approve(address _spender, uint256 _value) { allowed[msg.sender][_spender] = _value; } // @brief Get allowed amount of tokens // @param _owner owner of allowance // @param _spender spender contract // @return the rest of allowed tokens function allowance(address _owner, address _spender) constant returns (uint256 remaining) { return allowed[_owner][_spender]; } } 

As can be seen from the contract code, all tokens are emitted at a time when the contract is loaded into the blockchain, and all issued tokens are recorded on the balance of the address that performed the download. Next, the standard functions of moving tokens between holders, transfer and transferFrom, are implemented, and the current balance is stored in the balanceOf card.

We will not think how money (ether, ether) gets into the account of this contract, it does not matter whether they are transferred directly to the address of the contract or get there through some functions that can be additionally implemented to give the token application specificity and functionality. It is important that there is a certain non-zero balance of this.balance contract, which we want to distribute completely between tokens holders, in proportion to each percentage of tokens from total emissions.
')
From the point of view of classical algorithmic programming, the task may seem elementary and looks like this in the form of pseudocode:

 function divideUpReward() { for (holder in balanceOf) { uint256 reward = this.balance * holder.value / totalTokens; holder.key.send(reward); } } 

Unfortunately, this pseudocode cannot be implemented in the Solidity language, since The mapping data structure is not iterable and there is no way to go through all its elements. On the Ethereum forums, this task has been repeatedly discussed, and the main argument why it’s done this way is that it is trivially expensive. Now is the time to remember that the smart contract is running on an EVM distributed virtual machine, i.e. runs on every full node, and since we spend other people's computing resources, we have to pay for it, and the more operations we do, the more commission that the one who will call these operations will have to pay. In our case, the pay will be the one who will call divideUpReward ().

If you continue to persist and try to implement this pseudocode for Solidity, then you can invent your own “bicycle” and make an iterative analog mapping. An example of such an implementation is available in the public domain ( here ), but it does not solve the problem of the high cost of executing the divideUpReward () function. Moreover, we also take care of all the costs of payment of transactions of sending ethers holder.se. (send) to all holders of tokens.

There is a logical desire to transfer all the commissions to receive their own dividends directly to the holder of the tokens - in the end, this is his reward, even if he himself pays the commission for him, and our expenses, as the holder of a smart contract, should be minimized by some simple procedure.

Why not do it like this?

 mapping (address => uint256) balanceOfOld; uint totalReward; uint lastDivideRewardTime; function divideUpReward() public { // prevent call if less than 30 days passed from previous one if (lastDivideRewardTime + 30 days > now) throw; // make copy of mapping balanceOfOld = balanceOf; // reward is contract's balance totalReward = this.balance; lastDivideRewardTime = now; } // this can be called by token's holders function withdrawReward() public returns(uint) { uint256 tokens = balanceOfOld[msg.sender]; if (tokens > 0) { uint amount = totalReward * tokens / totalTokens; if (!msg.sender.send(amount)) return 0; balanceOfOld[msg.sender] = 0; return amount; } return 0; } 

This code looks much better already. does not contain cycles! We, as the contract owner, or any other Ethereum user, call the public function divideUpReward (), which captures dividends at the time of the call, creating a copy of the container with the current distribution of tokens and remembering the current balance of the contract, giving it all to the holders of the tokens. At the same time, we remember the time of the last call of divideUpReward () and prevent the repeated call within 30 days, thus allowing the holders of tokens to withdraw their dividends through the public function reward (). If some holder did not withdraw his dividends within the designated time, they will be returned to the common basket and within the next period will be available for distribution among all holders - as they say, who did not have time, he was late.

The function to withdrawReward () is called directly by the holders of tokens, and therefore they pay all the fees associated with sending funds to their addresses. One could be happy with the solution found if it did not contain one construction that is unacceptable from the point of view of the Solidity language, namely “balanceOfOld = balanceOf” - creating a copy of the mapping is not provided for in Solidity. But even if we assume that it would be, it is logical to expect that the cost of such a copy would be extremely expensive in the limit, since I would still assume the presence of a hidden, but looped, all elements of the map.

Let's try to get rid of the operation of explicit copying, by the introduction of an additional container, which will be dynamically filled depending on the actions of a particular holder of tokens, and, therefore, be done at his expense.

  uint totalReward; uint lastDivideRewardTime; uint restReward; struct TokenHolder { uint256 balance; uint balanceUpdateTime; uint rewardWithdrawTime; } mapping(address => TokenHolder) holders; function reward() constant public returns(uint) { if (holders[msg.sender].rewardWithdrawTime >= lastDivideRewardTime) { return 0; } uint256 balance; if (holders[msg.sender].balanceUpdateTime <= lastDivideRewardTime) { balance = balanceOf[msg.sender]; } else { balance = holders[msg.sender].balance; } return totalReward * balance / totalTokens; } function withdrawReward() public returns(uint) { uint value = reward(); if (value == 0) { return 0; } if (!msg.sender.send(value)) { return 0; } if (balanceOf[msg.sender] == 0) { // garbage collector delete holders[msg.sender]; } else { holders[msg.sender].rewardWithdrawTime = now; } return value; } // Divide up reward and make it accessible for withdraw function divideUpReward() public { if (lastDivideRewardTime + 30 days > now) throw; lastDivideRewardTime = now; totalReward = this.balance; restReward = this.balance; } function beforeBalanceChanges(address _who) public { if (holders[_who].balanceUpdateTime <= lastDivideRewardTime) { holders[_who].balanceUpdateTime = now; holders[_who].balance = balanceOf[_who]; } } 

Attention should be paid to the beforeBalanceChanged (address _who) function, which replaces the copying of the mapping card with us. The call of this function should be added to the original transfer and transferFrom functions of our contract right before modifying the balance for a specific address. The function will check that the tokens are moving after fixing the dividend withdrawal period and will maintain the balance of the specific token holder for the remuneration distribution period, i.e. we make a copy of balanceOf element by element only if the balance of a particular holder changes.

If you combine all of the above together, you get the following text of the smart contract, which issues tokens, their structuring as shares with the subsequent accrual of dividends:

 pragma solidity ^0.4.0; contract Token { string public standard = 'Token 0.1'; string public name; //!< name for display purporses string public symbol; //!< symbol for display purporses uint8 public decimals; //!< amount of decimals for display purporses mapping (address => uint256) public balanceOf; //!< array of all balances mapping (address => mapping (address => uint256)) public allowed; uint256 totalTokens; uint totalReward; uint lastDivideRewardTime; uint restReward; struct TokenHolder { uint256 balance; uint balanceUpdateTime; uint rewardWithdrawTime; } mapping(address => TokenHolder) holders; function Token( uint256 initialSupply, string tokenName, uint8 decimalUnits, string tokenSymbol) { totalTokens = initialSupply; balanceOf[msg.sender] = initialSupply; name = tokenName; symbol = tokenSymbol; decimals = decimalUnits; } // @brief Send coins // @param _to recipient of coins // @param _value amount of coins for send function transfer(address _to, uint256 _value) { if (balanceOf[msg.sender] < _value || _value <= 0) throw; beforeBalanceChanges(msg.sender); beforeBalanceChanges(_to); balanceOf[msg.sender] -= _value; balanceOf[_to] += _value; } // @brief Send coins // @param _from source of coins // @param _to recipient of coins // @param _value amount of coins for send function transferFrom(address _from, address _to, uint256 _value) { if (balanceOf[_from] < _value || _value <= 0) throw; if (allowed[_from][msg.sender] < _value) throw; beforeBalanceChanges(_from); beforeBalanceChanges(_to); balanceOf[_from] -= _value; balanceOf[_to] += _value; allowed[_from][msg.sender] -= _value; } // @brief Allow another contract to spend some tokens in your behalf // @param _spender another contract address // @param _value amount of approved tokens function approve(address _spender, uint256 _value) { allowed[msg.sender][_spender] = _value; } // @brief Get allowed amount of tokens // @param _owner owner of allowance // @param _spender spender contract // @return the rest of allowed tokens function allowance(address _owner, address _spender) constant returns (uint256 remaining) { return allowed[_owner][_spender]; } function reward() constant public returns(uint) { if (holders[msg.sender].rewardWithdrawTime >= lastDivideRewardTime) { return 0; } uint256 balance; if (holders[msg.sender].balanceUpdateTime <= lastDivideRewardTime) { balance = balanceOf[msg.sender]; } else { balance = holders[msg.sender].balance; } return totalReward * balance / totalTokens; } function withdrawReward() public returns(uint) { uint value = reward(); if (value == 0) { return 0; } if (!msg.sender.send(value)) { return 0; } if (balanceOf[msg.sender] == 0) { // garbage collector delete holders[msg.sender]; } else { holders[msg.sender].rewardWithdrawTime = now; } return value; } // Divide up reward and make it accesible for withdraw function divideUpReward() public { if (lastDivideRewardTime + 30 days > now) throw; lastDivideRewardTime = now; totalReward = this.balance; restReward = this.balance; } function beforeBalanceChanges(address _who) public { if (holders[_who].balanceUpdateTime <= lastDivideRewardTime) { holders[_who].balanceUpdateTime = now; holders[_who].balance = balanceOf[_who]; } } } 

It should be remembered that the legal aspects of the emission of tokens structured as shares are taken out of the discussion, therefore the application of these technical solutions remains entirely and completely the responsibility of who will issue such a contract for ICO.

Useful links on the development of smart contracts for Ethereum:

Source: https://habr.com/ru/post/328246/


All Articles