1+ pragma solidity ^ 0.4.24 ;
2+
3+ import "openzeppelin-solidity/contracts/math/SafeMath.sol " ;
4+ import "../math/KindMath.sol " ;
5+
6+ contract ERC1410Basic {
7+
8+ using SafeMath for uint256 ;
9+
10+ // Represents a fungible set of tokens.
11+ struct Partition {
12+ uint256 amount;
13+ bytes32 partition;
14+ }
15+
16+ uint256 _totalSupply;
17+
18+ // Mapping from investor to aggregated balance across all investor token sets
19+ mapping (address => uint256 ) balances;
20+
21+ // Mapping from investor to their partitions
22+ mapping (address => Partition[]) partitions;
23+
24+ // Mapping from (investor, partition) to index of corresponding partition in partitions
25+ // @dev Stored value is always greater by 1 to avoid the 0 value of every index
26+ mapping (address => mapping (bytes32 => uint256 )) partitionToIndex;
27+
28+ event TransferByPartition (
29+ bytes32 indexed _fromPartition ,
30+ address _operator ,
31+ address indexed _from ,
32+ address indexed _to ,
33+ uint256 _value ,
34+ bytes _data ,
35+ bytes _operatorData
36+ );
37+
38+ /**
39+ * @dev Total number of tokens in existence
40+ */
41+ function totalSupply () external view returns (uint256 ) {
42+ return _totalSupply;
43+ }
44+
45+ /// @notice Counts the sum of all partitions balances assigned to an owner
46+ /// @param _tokenHolder An address for whom to query the balance
47+ /// @return The number of tokens owned by `_tokenHolder`, possibly zero
48+ function balanceOf (address _tokenHolder ) external view returns (uint256 ) {
49+ return balances[_tokenHolder];
50+ }
51+
52+ /// @notice Counts the balance associated with a specific partition assigned to an tokenHolder
53+ /// @param _partition The partition for which to query the balance
54+ /// @param _tokenHolder An address for whom to query the balance
55+ /// @return The number of tokens owned by `_tokenHolder` with the metadata associated with `_partition`, possibly zero
56+ function balanceOfByPartition (bytes32 _partition , address _tokenHolder ) external view returns (uint256 ) {
57+ if (_validPartition (_partition, _tokenHolder))
58+ return partitions[_tokenHolder][partitionToIndex[_tokenHolder][_partition] - 1 ].amount;
59+ else
60+ return 0 ;
61+ }
62+
63+ /// @notice Use to get the list of partitions `_tokenHolder` is associated with
64+ /// @param _tokenHolder An address corresponds whom partition list is queried
65+ /// @return List of partitions
66+ function partitionsOf (address _tokenHolder ) external view returns (bytes32 []) {
67+ bytes32 [] memory partitionsList = new bytes32 [](partitions[_tokenHolder].length );
68+ for (uint256 i = 0 ; i < partitions[_tokenHolder].length ; i++ ) {
69+ partitionsList[i] = partitions[_tokenHolder][i].partition;
70+ }
71+ return partitionsList;
72+ }
73+
74+ /// @notice Transfers the ownership of tokens from a specified partition from one address to another address
75+ /// @param _partition The partition from which to transfer tokens
76+ /// @param _to The address to which to transfer tokens to
77+ /// @param _value The amount of tokens to transfer from `_partition`
78+ /// @param _data Additional data attached to the transfer of tokens
79+ /// @return The partition to which the transferred tokens were allocated for the _to address
80+ function transferByPartition (bytes32 _partition , address _to , uint256 _value , bytes _data ) external returns (bytes32 ) {
81+ // Add a function to verify the `_data` parameter
82+ // TODO: Need to create the bytes division of the `_partition` so it can be easily findout in which receiver's partition
83+ // token will transfered. For current implementation we are assuming that the receiver's partition will be same as sender's
84+ // as well as it also pass the `_validPartition()` check. In this particular case we are also assuming that reciever has the
85+ // some tokens of the same partition as well (To avoid the array index out of bound error).
86+ // Note- There is no operator used for the execution of this call so `_operator` value in
87+ // in event is address(0) same for the `_operatorData`
88+ _transferByPartition (msg .sender , _to, _value, _partition, _data, address (0 ), "" );
89+ }
90+
91+ /// @notice The standard provides an on-chain function to determine whether a transfer will succeed,
92+ /// and return details indicating the reason if the transfer is not valid.
93+ /// @param _from The address from whom the tokens get transferred.
94+ /// @param _to The address to which to transfer tokens to.
95+ /// @param _partition The partition from which to transfer tokens
96+ /// @param _value The amount of tokens to transfer from `_partition`
97+ /// @param _data Additional data attached to the transfer of tokens
98+ /// @return ESC (Ethereum Status Code) following the EIP-1066 standard
99+ /// @return Application specific reason codes with additional details
100+ /// @return The partition to which the transferred tokens were allocated for the _to address
101+ function canTransferByPartition (address _from , address _to , bytes32 _partition , uint256 _value , bytes _data ) external view returns (byte , bytes32 , bytes32 ) {
102+ // TODO: Applied the check over the `_data` parameter
103+ if (! _validPartition (_partition, _from))
104+ return (0x50 , "Partition not exists " , bytes32 ("" ));
105+ else if (partitions[_from][partitionToIndex[_from][_partition]].amount < _value)
106+ return (0x52 , "Insufficent balance " , bytes32 ("" ));
107+ else if (_to == address (0 ))
108+ return (0x57 , "Invalid receiver " , bytes32 ("" ));
109+ else if (! KindMath.checkSub (balances[_from], _value) || ! KindMath.checkAdd (balances[_to], _value))
110+ return (0x50 , "Overflow " , bytes32 ("" ));
111+
112+ // Call function to get the receiver's partition. For current implementation returning the same as sender's
113+ return (0x51 , "Success " , _partition);
114+ }
115+
116+ function _transferByPartition (address _from , address _to , uint256 _value , bytes32 _partition , bytes _data , address _operator , bytes _operatorData ) internal {
117+ require (_validPartition (_partition, _from), "Invalid partition " );
118+ require (partitions[_from][partitionToIndex[_from][_partition] - 1 ].amount >= _value, "Insufficient balance " );
119+ require (_to != address (0 ), "0x address not allowed " );
120+ uint256 _fromIndex = partitionToIndex[_from][_partition] - 1 ;
121+ uint256 _toIndex = partitionToIndex[_to][_partition] - 1 ;
122+
123+ // Changing the state values
124+ partitions[_from][_fromIndex].amount = partitions[_from][_fromIndex].amount.sub (_value);
125+ balances[_from] = balances[_from].sub (_value);
126+ partitions[_to][_toIndex].amount = partitions[_to][_toIndex].amount.add (_value);
127+ balances[_to] = balances[_to].add (_value);
128+ // Emit transfer event.
129+ emit TransferByPartition (_partition, _operator, _from, _to, _value, _data, _operatorData);
130+ }
131+
132+ function _validPartition (bytes32 _partition , address _holder ) internal view returns (bool ) {
133+ if (partitions[_holder].length < partitionToIndex[_holder][_partition] || partitionToIndex[_holder][_partition] == 0 )
134+ return false ;
135+ else
136+ return true ;
137+ }
138+
139+
140+
141+ }
0 commit comments