项目作者: godappslab

项目描述 :
Implementation of Internal Distribution Token
高级语言: Solidity
项目地址: git://github.com/godappslab/internal-distribution-token.git
创建时间: 2019-01-31T09:46:47Z
项目社区:https://github.com/godappslab/internal-distribution-token

开源协议:MIT License

下载


Implementation Example of “Internal Distribution Token”

Read this in other languages: English, 日本語.

It is under development.

Overview

As an example of an internal distribution token whose distribution and value are managed, it is developed assuming an implementation of a token that can be used as a point. The points here mean things like the points given when shopping.

This token is realized using Cryptocurrency’s Smart Contract and related technology mechanisms.

Main point

This token assumes the point used by shopping etc., and implements the basic function to realize in Smart Contract. It works as Dapps on Ethereum and was developed in Solidity language.

In addition, with regard to the fee (GAS) that is incurred when sending a token on Ethereum, we have devised so that users who have been granted points do not need to have an ETH.

What can be achieved by this token

Merits of realization

  • Unavailable on Cryptocurrency Exchange (cannot be remit between users)
  • Smart Contract technology can be used to distribute or exchange tokens
  • Because it is recorded in Ethereum’s Public Chain, fraud prevention and transparency are secured
  • Can fix the value (price) of the token
  • There is no fee for users, so there is no need to have Ether, and you may want to spread the use of smart contracts.

specification

I want to make it impossible for the user to freely distribute coins, though it is easy to use like the ERC 20 token.

Ordinary tokens require the following restrictions, such as shopping points, because users can send tokens to each other freely.

Divide the token holder into the following three roles

  • Owner (owner of this smart contract)
  • Distributor (person who actually distributes the token assigned by the owner to the user)
  • User (shopping, collecting tokens)

The rough flow of tokens looks like this:

Token Flow

  • The distributor is determined by the owner
  • Distributors can be assigned tokens from the owner
  • Distributor distributes arbitrary tokens to users
  • The user query token held balanceOf() and the recording of the movement of the token Transfer() can be confirmed in the same mechanism as ERC20 token
  • The user can apply for token exchange (consumption of points) to the owner … (A)
  • The owner can receive a token of the quantity requested by the user (B)

Although information transfer from (A) to (B) is performed off-chain, an implementation example will be described later.

Token interface

  1. pragma solidity ^0.5.0;
  2. interface InternalDistributionTokenInterface {
  3. // Required methods
  4. // @title Is the ETH address of the argument the distributor of the token?
  5. // @param _account
  6. // @return bool (true:owner false:not owner)
  7. function isDistributor(address _account) external view returns (bool);
  8. // @title A function that adds the ETH address of the argument to the distributor list of the token
  9. // @param _account ETH address you want to add
  10. // @return bool
  11. function addToDistributor(address _account) external returns (bool success);
  12. // @title A function that excludes the ETH address of the argument from the distributor list of the token
  13. // @param _account ETH address you want to delete
  14. // @return bool
  15. function deleteFromDistributor(address _account) external returns (bool success);
  16. // @title A function that accepts a user's transfer request (executed by the contract owner)
  17. // @param bytes memory _signature
  18. // @param address _requested_user
  19. // @param uint256 _value
  20. // @param string _nonce
  21. // @return bool
  22. function acceptTokenTransfer(bytes calldata _signature, address _requested_user, uint256 _value, string calldata _nonce)
  23. external
  24. returns (bool success);
  25. // @title A function that generates a hash value of a request to which a user sends a token (executed by the user of the token)
  26. // @params _requested_user ETH address that requested token transfer
  27. // @params _value Number of tokens
  28. // @params _nonce One-time string
  29. // @return bytes32 Hash value
  30. // @dev The user signs the hash value obtained from this function and hands it over to the owner outside the system
  31. function requestTokenTransfer(address _requested_user, uint256 _value, string calldata _nonce) external view returns (bytes32);
  32. // @title Returns whether it is a used signature
  33. // @params _signature Signature string
  34. // @return bool Used or not
  35. function isUsedSignature(bytes calldata _signature) external view returns (bool);
  36. // Events
  37. // token assignment from owner to distributor
  38. event Allocate(address indexed from, address indexed to, uint256 value);
  39. // tokens from distributor to users
  40. event Distribute(address indexed from, address indexed to, uint256 value);
  41. // tokens from distributor to owner
  42. event BackTo(address indexed from, address indexed to, uint256 value);
  43. // owner accepted the token from the user
  44. event Exchange(address indexed from, address indexed to, uint256 value, bytes signature, string nonce);
  45. event AddedToDistributor(address indexed account);
  46. event DeletedFromDistributor(address indexed account);
  47. }

The specifications that can not be defined in the interface are described below

Implement addition and deletion of distributors so that only the owner can execute. Implement the cancellation of the distributor so that it can be executed if the distributor does not have a token.

  1. // @title A function that adds the ETH address of the argument to the distributor list of the token
  2. // @param _account ETH address you want to add
  3. // @return bool
  4. function addToDistributor(address _account) external onlyOwner returns (bool success) {
  5. // `_account` is a correct address
  6. require(_account != address(0), "Correct EOA address is required");
  7. // `_account` is necessary to have no token
  8. require(_balances[_account] == 0, "This EOA address has a token");
  9. // `_account` is not an owner or a distributor
  10. require(this.isOwner(_account) == false && this.isDistributor(_account) == false, "This EOA address can not be a distributor");
  11. // `_account` is not a contract address
  12. require(_account.isContract() == false, "Contract address can not be specified");
  13. // Add to distributor
  14. Distributors.add(_account);
  15. emit AddedToDistributor(_account);
  16. return true;
  17. }
  1. // @title A function that excludes the ETH address of the argument from the distributor list of the token
  2. // @param _account ETH address you want to delete
  3. // @return bool
  4. function deleteFromDistributor(address _account) external onlyOwner returns (bool success) {
  5. // `_account` is a correct address
  6. require(_account != address(0), "Correct EOA address is required");
  7. // `_account` is necessary to have no token
  8. require(_balances[_account] == 0, "This EOA address has a token");
  9. // Delete from distributor
  10. Distributors.remove(_account);
  11. emit DeletedFromDistributor(_account);
  12. return true;
  13. }

The user specifies the quantity of tokens he wants to transfer to the owner.

requestTokenTransfer() following information is hashed by executing requestTokenTransfer() .

  • EOA address of the user requesting the remittance
  • Quantity to send money
  • A string _nonce identifying the remittance request

The number of tokens you want to exchange obtained by following implementation, but at that time _nonce , it is necessary to give those that do not overlap.

requestTokenTransfer() no transaction occurs at the time of execution of requestTokenTransfer() , it is not possible to count sequential numbers etc. inside the token.

Therefore, it is necessary to give and raise as an argument when executing a function, but it seems to be better to use a string that is difficult to guess like Nano ID as an example.

Also, the obtained signature obtained is handed over to the owner by a method other than the block chain.

  1. // @title A function that generates a hash value of a request to which a user sends a token (executed by the user of the token)
  2. // @params _requested_user ETH address that requested token transfer
  3. // @params _value Number of tokens
  4. // @params _nonce One-time string
  5. // @return bytes32 Hash value
  6. // @dev The user signs the hash value obtained from this function and hands it over to the owner outside the system
  7. function requestTokenTransfer(address _requested_user, uint256 _value, string calldata _nonce) external view returns (bytes32) {
  8. return keccak256(abi.encodePacked(address(this), bytes4(0x8210d627), _requested_user, _value, _nonce));
  9. }

The owner receives the following value from the user and executes the acceptTokenTransfer() function.

  • Signature string
  • EOA address of the user who wants to send money
  • Number of tokens to send
  • Value of _nonce at the time of signing
  1. // @title A function that accepts a user's transfer request (executed by the contract owner)
  2. // @param bytes _signature
  3. // @param address _requested_user
  4. // @param uint256 _value
  5. // @param string _nonce
  6. // @return bool
  7. function acceptTokenTransfer(bytes calldata _signature, address _requested_user, uint256 _value, string calldata _nonce)
  8. external
  9. onlyOwner
  10. returns (bool success)
  11. {
  12. // argument `_signature` is not yet used
  13. require(usedSignatures[_signature] == false);
  14. // Recalculate hash value
  15. bytes32 hashedTx = this.requestTokenTransfer(_requested_user, _value, _nonce);
  16. // Identify the requester's ETH Address
  17. address _user = hashedTx.recover(_signature);
  18. require(_user != address(0), "Unable to get EOA address from signature");
  19. // the argument `_requested_user` and
  20. // the value obtained by calculation from the signature are the same ETH address
  21. //
  22. // If they are different, it is judged that the user's request has not been transmitted correctly
  23. require(_user == _requested_user, "EOA address mismatch");
  24. // user has the amount of that token
  25. require(this.balanceOf(_user) >= _value, "Insufficient funds");
  26. _balances[_user] = _balances[_user].sub(_value);
  27. _balances[msg.sender] = _balances[msg.sender].add(_value);
  28. // Record as used signature
  29. usedSignatures[_signature] = true;
  30. // Execute events
  31. emit Transfer(_user, msg.sender, _value);
  32. emit Exchange(_user, msg.sender, _value, _signature, _nonce);
  33. return true;
  34. }

Owner assigns token to distributor

配布者がユーザーにトークン配布

Distributor distributes tokens to users

配布者がオーナーにトークンを返却

Distributor returns token to owner

ユーザーがトークンのトークン交換の申請を行う

User applies for token exchange of tokens

オーナーが配布者を追加登録

The owner additionally registers the distributor

オーナーが配布者を抹消

The owner cancels the distributor

オーナーが配布者を抹消

Notes on implementation

About the signature method currently implemented as an application for token exchange

web3.eth.sign() is scheduled to be discontinued, we plan to use EIP-712 signature verification, but we do not include it in the main unit, and we are experimenting with implementation of ÐApps in another repository.

While web3.eth.sign() will work for a while, we plan to switch to EIP712 signature.

署名処理の関連図

Test Cases

Check the operation with a test script using Truffle Suite .

However, since the processing of the signature does not function properly, the browser is used to test that part.

Implementation

Implementation of the token will be released on GitHub.

https://github.com/godappslab/internal-distribution-token

You can also manipulate this token from the website. (Currently available only for Ropsten Test Network)

https://lab.godapps.io/points/

References

Standards

  1. ERC-20 Token Standard. Https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md

Issues

  1. ERC 865: Pay transfers in tokens instead of gas, in one transaction # 865 https://github.com/ethereum/EIPs/issues/865