├── .gitattributes ├── .gitignore ├── README.md ├── contracts ├── Migrations.sol ├── poc │ ├── Poc.sol │ └── simple_contract.sol ├── sale │ ├── CrowdSale.sol │ ├── FCTVTransferBulk.sol │ ├── PreSaleI.sol │ ├── TokenLock.sol │ ├── TokenLockDistribute.sol │ └── TransferBulk.sol ├── token │ ├── FCTV.sol │ ├── TestToken.sol │ └── [deprecated]FCT.sol └── utils │ ├── MultiOwnable.sol │ └── TokenSwap.sol ├── exec ├── DeployBulkTransferFCTV.js ├── DeployFCTV.js ├── DeploySimpleContract.js ├── DeploySwapContract.js ├── DeployTestSwap.js ├── DeployTestToken.js ├── DistributeFCTV.js └── SignMessage.js ├── migrations ├── 1_initial_migration.js ├── 2_deploy_contracts.js └── 3_deploy_bulk.js ├── package.json ├── test ├── FCT.js ├── FCTV.js ├── Poc.js ├── PreSaleI.js ├── TokenLock.js └── test-util.js └── truffle.example.js /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | bin 3 | node_modules 4 | proj.sublime-* 5 | package-lock.json 6 | config.json 7 | truffle.js 8 | *.swp 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FirmaChain Contract 2 | 3 | This is contract of FirmaChain. 4 | 5 | using zeppelin-solidity, truffle 6 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | constructor() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/poc/Poc.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "zeppelin-solidity/contracts/math/SafeMath.sol"; 4 | 5 | contract Poc { 6 | using SafeMath for uint256; 7 | mapping (uint256 => bool) public hasChild; 8 | mapping (uint256 => uint256) public parentContract; 9 | mapping (uint256 => address[]) public parties; 10 | mapping (uint256 => uint256[]) public hashes; 11 | mapping (uint256 => string[]) public extraData; 12 | mapping (uint256 => uint256) public signProgress; 13 | mapping (uint256 => uint256) public expireBlockNumbers; 14 | mapping (uint256 => bool) public approvals; 15 | 16 | constructor() public { 17 | } 18 | 19 | function getContractHistory(uint256 _contractId) public view returns (uint256[]) { 20 | uint256 _nowContractId = _contractId; 21 | uint256 _historySize = 1; 22 | while(parentContract[_nowContractId] != 0) { 23 | _historySize = _historySize.add(1); 24 | _nowContractId = parentContract[_nowContractId]; 25 | } 26 | uint256[] memory history = new uint256[](_historySize); 27 | _nowContractId = _contractId; 28 | for (uint256 i = 0; i < _historySize; i++) { 29 | history[_historySize - i - 1] = _nowContractId; 30 | _nowContractId = parentContract[_nowContractId]; 31 | } 32 | return history; 33 | } 34 | 35 | function getRootContract(uint256 _contractId) public view returns (uint256) { 36 | uint256[] memory history = getContractHistory(_contractId); 37 | return history[0]; 38 | } 39 | 40 | function isExpiredContract(uint256 _contractId) public view returns (bool) { 41 | return block.number > expireBlockNumbers[_contractId]; 42 | } 43 | 44 | function getParties(uint256 _contractId) public view returns (address[]) { 45 | uint256 _rootContractId = getRootContract(_contractId); 46 | return parties[_rootContractId]; 47 | } 48 | 49 | function getHashes(uint256 _contractId) public view returns (uint256[]) { 50 | return hashes[_contractId]; 51 | } 52 | 53 | // getStatus 54 | // 2 : Closed (which has child contract) 55 | // 1 : Approved 56 | // 0 : Wait to sign 57 | // -1 : Expired or nullified 58 | function getStatus(uint256 _contractId) public view returns (int) { 59 | if (hasChild[_contractId]) { 60 | return 2; 61 | } else if (approvals[_contractId]) { // approvals[_contractId] 62 | return 1; 63 | } else if (isExpiredContract(_contractId)) { // isExpiredContract(_contractId) && !approvals[_contractId] 64 | return -1; 65 | } else { // !isExpiredContract(_contractId) && !approvals[_contractId] 66 | return 0; 67 | } 68 | } 69 | 70 | function newContract(uint256 _contractId, address[] _parties, uint256 _lifetime) public { 71 | require(_lifetime > 0); 72 | require(_parties.length > 1 && _parties.length < 1000); 73 | require(_checkDistinctParties(_parties)); 74 | if (getStatus(_contractId) == -1) { 75 | bool overwrited = expireBlockNumbers[_contractId] != 0; 76 | hasChild[_contractId] = false; // NOTE: actually meaningless code; it is always false if reaching here 77 | parentContract[_contractId] = 0; 78 | parties[_contractId] = _parties; 79 | hashes[_contractId] = new uint256[](_parties.length); 80 | extraData[_contractId] = new string[](_parties.length); 81 | signProgress[_contractId] = 0; 82 | expireBlockNumbers[_contractId] = block.number.add(_lifetime); 83 | emit NewContract(_contractId, msg.sender, _parties, _lifetime, overwrited); 84 | } else { 85 | require(false); 86 | } 87 | } 88 | event NewContract(uint256 indexed _contractId, address indexed _creator, address[] _parties, uint256 _lifetime, bool _overwrite); 89 | 90 | function newSubContract(uint256 _contractId, uint256 _parentContractId, uint256 _lifetime) public { 91 | require(_lifetime > 0); 92 | if (getStatus(_contractId) == -1) { 93 | bool exist; 94 | uint256 index; 95 | (exist, index) = _getIndexOfParty(_parentContractId, msg.sender); 96 | require(exist); 97 | bool overwrited = expireBlockNumbers[_contractId] != 0; 98 | hasChild[_parentContractId] = true; 99 | parentContract[_contractId] = _parentContractId; 100 | address[] memory rootParties = getParties(_parentContractId); 101 | //parties[_contractId] = _parties; 102 | hashes[_contractId] = new uint256[](rootParties.length); 103 | extraData[_contractId] = new string[](rootParties.length); 104 | signProgress[_contractId] = 0; 105 | expireBlockNumbers[_contractId] = block.number.add(_lifetime); 106 | emit NewSubContract(_contractId, msg.sender, _parentContractId, _lifetime, overwrited); 107 | } else { 108 | require(false); 109 | } 110 | } 111 | event NewSubContract(uint256 indexed _contractId, address indexed _creator, uint256 indexed _parentContractId, uint256 _lifetime, bool _overwrite); 112 | 113 | function signContract(uint256 _contractId, uint256 _signedHash, string _extraData) public { 114 | address[] memory rootParties = getParties(_contractId); 115 | require(getStatus(_contractId) == 0); 116 | require(signProgress[_contractId] < rootParties.length); 117 | bool exist; 118 | uint256 index; 119 | (exist, index) = _getIndexOfParty(_contractId, msg.sender); 120 | require(exist); 121 | require(hashes[_contractId][index] == 0); 122 | hashes[_contractId][index] = _signedHash; 123 | extraData[_contractId][index] = _extraData; 124 | signProgress[_contractId] = signProgress[_contractId].add(1); 125 | emit SignContract(_contractId, msg.sender, _signedHash); 126 | if (signProgress[_contractId] == rootParties.length) { 127 | uint256 commonHash = hashes[_contractId][0]; 128 | bool approval = true; 129 | for(uint256 i = 1; i < hashes[_contractId].length; i++) { 130 | if (commonHash != hashes[_contractId][i]) { 131 | approval = false; 132 | expireBlockNumbers[_contractId] = 1; 133 | emit NullifyContract(_contractId, rootParties[i]); 134 | break; 135 | } 136 | } 137 | approvals[_contractId] = approval; 138 | if (approval) { 139 | emit ApproveContract(_contractId); 140 | } 141 | } 142 | } 143 | event SignContract(uint256 indexed _contractId, address indexed _signer, uint256 _signedHash); 144 | event NullifyContract(uint256 indexed _contractId, address _defector); 145 | event ApproveContract(uint256 indexed _contractId); 146 | 147 | function _getIndexOfParty(uint256 _contractId, address _addr) private view returns (bool, uint256) { 148 | address[] memory rootParties = getParties(_contractId); 149 | for(uint256 i = 0; i < rootParties.length; i++) { 150 | if (rootParties[i] == _addr) { 151 | return (true, i); 152 | } 153 | } 154 | return (false, 0); 155 | } 156 | 157 | function _checkDistinctParties(address[] _parties) private pure returns (bool) { 158 | for (uint256 i = 0; i < _parties.length; i++) { 159 | for (uint256 j = i + 1; j < _parties.length; j++) { 160 | if (_parties[i] == _parties[j]) { 161 | return false; 162 | } 163 | } 164 | } 165 | return true; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /contracts/poc/simple_contract.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | contract simple_contract { 4 | mapping (bytes32 => uint8[]) public vMap; 5 | mapping (bytes32 => bytes32[]) public rMap; 6 | mapping (bytes32 => bytes32[]) public sMap; 7 | 8 | constructor() public { 9 | } 10 | 11 | /* 12 | ecrecover : check signer 13 | */ 14 | function addSign(bytes32 hash, uint8[] vArray, bytes32[] rArray, bytes32[] sArray, address[] addrArray) public { 15 | require(vMap[hash].length == 0 && rMap[hash].length == 0 && sMap[hash].length == 0, "no map"); 16 | require(vArray.length == rArray.length && rArray.length == sArray.length, "invalid map"); 17 | require(addrArray.length > 0 && addrArray.length < 1000, "invalid address array length"); 18 | require(_checkDistinctParties(addrArray), "invalid address array"); 19 | vMap[hash] = new uint8[](vArray.length); 20 | rMap[hash] = new bytes32[](vArray.length); 21 | sMap[hash] = new bytes32[](vArray.length); 22 | for (uint256 i = 0; i < vArray.length; i++) { 23 | vMap[hash][i] = vArray[i]; 24 | rMap[hash][i] = rArray[i]; 25 | sMap[hash][i] = sArray[i]; 26 | 27 | bytes memory prefix = "\x19Ethereum Signed Message:\n32"; 28 | bytes32 prefixedHash = keccak256(abi.encodePacked(prefix, hash)); 29 | require(ecrecover(prefixedHash, vArray[i], rArray[i], sArray[i]) == addrArray[i], "ecrecover fail"); 30 | } 31 | } 32 | 33 | function getAddresses(bytes32 hash) public view returns (address[]) { 34 | uint8[] storage vArray = vMap[hash]; 35 | bytes32[] storage rArray = rMap[hash]; 36 | bytes32[] storage sArray = sMap[hash]; 37 | address[] memory addrArray = new address[](vArray.length); 38 | require(vArray.length > 0 && rArray.length > 0 && sArray.length > 0); 39 | for (uint256 i = 0; i < vArray.length; i++) { 40 | bytes memory prefix = "\x19Ethereum Signed Message:\n32"; 41 | bytes32 prefixedHash = keccak256(abi.encodePacked(prefix, hash)); 42 | addrArray[i] = ecrecover(prefixedHash, vArray[i], rArray[i], sArray[i]); 43 | } 44 | return addrArray; 45 | } 46 | 47 | function getEcrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public pure returns (address) { 48 | return ecrecover(hash, v, r, s); 49 | } 50 | 51 | function _checkDistinctParties(address[] _parties) private pure returns (bool) { 52 | for (uint256 i = 0; i < _parties.length; i++) { 53 | for (uint256 j = i + 1; j < _parties.length; j++) { 54 | if (_parties[i] == _parties[j]) { 55 | return false; 56 | } 57 | } 58 | } 59 | return true; 60 | } 61 | } -------------------------------------------------------------------------------- /contracts/sale/CrowdSale.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | -------------------------------------------------------------------------------- /contracts/sale/FCTVTransferBulk.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "zeppelin-solidity/contracts/token/ERC20/ERC20.sol"; 4 | import "zeppelin-solidity/contracts/ownership/Ownable.sol"; 5 | 6 | contract FCTVTransferBulk is Ownable { 7 | ERC20 public token; 8 | 9 | constructor (address _token) public { 10 | token = ERC20(_token); 11 | } 12 | 13 | function transferBulks(address[] _addrs, uint256[] _amounts) public onlyOwner { 14 | require(_addrs.length == _amounts.length); 15 | for (uint256 i = 0; i < _addrs.length; i++) { 16 | token.transfer(_addrs[i], _amounts[i]); 17 | } 18 | emit TransferBulkEvent(_addrs, _amounts); 19 | } 20 | 21 | event TransferBulkEvent(address[] _addrs, uint256[] _amounts); 22 | } 23 | -------------------------------------------------------------------------------- /contracts/sale/PreSaleI.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "zeppelin-solidity/contracts/math/SafeMath.sol"; 4 | import "zeppelin-solidity/contracts/ownership/Whitelist.sol"; 5 | import "zeppelin-solidity/contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract PreSaleI is Whitelist { 8 | using SafeMath for uint256; 9 | // rate is the amount of token to the ether. Bonus rate is included in exchange rate. 10 | uint256 public exchangeRate; 11 | 12 | // in ETH(wei), not token 13 | uint256 public minValue; 14 | uint256 public maxTotal; 15 | uint256 public maxPerAddress; 16 | 17 | uint256 public startTimestamp; 18 | uint256 public endTimestamp; 19 | bool public enabled; 20 | 21 | address public wallet; 22 | ERC20 public token; 23 | 24 | // in ETH(wei), not token 25 | uint256 public accumulatedAmount = 0; 26 | uint256 public accumulatedAmountExternal = 0; 27 | mapping (address => uint256) public buyAmounts; 28 | 29 | address[] public addresses; 30 | 31 | constructor(ERC20 _token, address _wallet, uint256 _exchangeRate, uint256 _minValue, uint256 _maxTotal, uint256 _maxPerAddress, uint256 _startTimestamp, uint256 _endTimestamp) public { 32 | require(_token != address(0)); 33 | require(_wallet != address(0)); 34 | token = _token; 35 | wallet = _wallet; 36 | exchangeRate = _exchangeRate; 37 | minValue = _minValue; 38 | maxTotal = _maxTotal; 39 | maxPerAddress = _maxPerAddress; 40 | startTimestamp = _startTimestamp; 41 | endTimestamp = _endTimestamp; 42 | enabled = false; 43 | } 44 | 45 | function toggleEnabled() public onlyOwner { 46 | enabled = !enabled; 47 | emit ToggleEnabled(enabled); 48 | } 49 | event ToggleEnabled(bool _enabled); 50 | 51 | function updateExternalAmount(uint256 _amount) public onlyOwner { 52 | accumulatedAmountExternal = _amount; 53 | emit UpdateTotalAmount(accumulatedAmount.add(accumulatedAmountExternal)); 54 | } 55 | event UpdateTotalAmount(uint256 _totalAmount); 56 | 57 | function () external payable { 58 | if (msg.sender != wallet) { 59 | buyTokens(); 60 | } 61 | } 62 | 63 | function buyTokens() public payable onlyWhitelisted { 64 | //require(msg.sender != address(0)); 65 | require(enabled); 66 | require(block.timestamp >= startTimestamp && block.timestamp <= endTimestamp); 67 | require(msg.value >= minValue); 68 | require(buyAmounts[msg.sender] < maxPerAddress); 69 | require(accumulatedAmount.add(accumulatedAmountExternal) < maxTotal); 70 | 71 | uint256 buyAmount; 72 | uint256 refundAmount; 73 | (buyAmount, refundAmount) = _calculateAmounts(msg.sender, msg.value); 74 | 75 | if (buyAmounts[msg.sender] == 0) { 76 | addresses.push(msg.sender); 77 | } 78 | 79 | accumulatedAmount = accumulatedAmount.add(buyAmount); 80 | buyAmounts[msg.sender] = buyAmounts[msg.sender].add(buyAmount); 81 | msg.sender.transfer(refundAmount); 82 | emit BuyTokens(msg.sender, buyAmount, refundAmount, buyAmount.mul(exchangeRate)); 83 | } 84 | event BuyTokens(address indexed _addr, uint256 _buyAmount, uint256 _refundAmount, uint256 _tokenAmount); 85 | 86 | function deliver(address _addr) public onlyOwner { 87 | require(_isEndCollect()); 88 | uint256 amount = buyAmounts[_addr]; 89 | require(amount > 0); 90 | uint256 tokenAmount = amount.mul(exchangeRate); 91 | buyAmounts[_addr] = 0; 92 | token.transfer(_addr, tokenAmount); 93 | emit Deliver(_addr, tokenAmount); 94 | } 95 | event Deliver(address indexed _addr, uint256 _tokenAmount); 96 | 97 | function refund(address _addr) public onlyOwner { 98 | require(_isEndCollect()); 99 | uint256 amount = buyAmounts[_addr]; 100 | require(amount > 0); 101 | buyAmounts[_addr] = 0; 102 | _addr.transfer(amount); 103 | accumulatedAmount = accumulatedAmount.sub(amount); 104 | emit Refund(_addr, amount); 105 | } 106 | event Refund(address indexed _addr, uint256 _buyAmount); 107 | 108 | function withdrawEth() public onlyOwner { 109 | wallet.transfer(address(this).balance); 110 | emit WithdrawEth(wallet, address(this).balance); 111 | } 112 | event WithdrawEth(address indexed _addr, uint256 _etherAmount); 113 | 114 | function terminate() public onlyOwner { 115 | require(getNotDelivered() == address(0)); 116 | token.transfer(wallet, token.balanceOf(address(this))); 117 | wallet.transfer(address(this).balance); 118 | emit Terminate(wallet, token.balanceOf(address(this)), address(this).balance); 119 | } 120 | event Terminate(address indexed _addr, uint256 _tokenAmount, uint256 _etherAmount); 121 | 122 | function getNotDelivered() public view returns (address) { 123 | for(uint256 i = 0; i < addresses.length; i++) { 124 | if (buyAmounts[addresses[i]] != 0) { 125 | return addresses[i]; 126 | } 127 | } 128 | return address(0); 129 | } 130 | 131 | function _calculateAmounts(address _buyAddress, uint256 _buyAmount) private view returns (uint256, uint256) { 132 | uint256 buyLimit1 = maxTotal.sub(accumulatedAmount.add(accumulatedAmountExternal)); 133 | uint256 buyLimit2 = maxPerAddress.sub(buyAmounts[_buyAddress]); 134 | uint256 buyLimit = buyLimit1 > buyLimit2 ? buyLimit2 : buyLimit1; 135 | uint256 buyAmount = _buyAmount > buyLimit ? buyLimit : _buyAmount; 136 | uint256 refundAmount = _buyAmount.sub(buyAmount); 137 | return (buyAmount, refundAmount); 138 | } 139 | 140 | function _isEndCollect() private view returns (bool) { 141 | return !enabled && block.timestamp> endTimestamp; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /contracts/sale/TokenLock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "zeppelin-solidity/contracts/token/ERC20/ERC20.sol"; 4 | import "../utils/MultiOwnable.sol"; 5 | 6 | contract TokenLock is MultiOwnable { 7 | ERC20 public token; 8 | mapping (address => uint256) public lockAmounts; 9 | mapping (address => uint256) public releaseTimestamps; 10 | 11 | constructor (address _token) public { 12 | token = ERC20(_token); 13 | } 14 | 15 | function getLockAmount(address _addr) external view returns (uint256) { 16 | return lockAmounts[_addr]; 17 | } 18 | 19 | function getReleaseBlock(address _addr) external view returns (uint256) { 20 | return releaseTimestamps[_addr]; 21 | } 22 | 23 | function lock(address _addr, uint256 _amount, uint256 _releaseTimestamp) external { 24 | require(owners[msg.sender]); 25 | require(_addr != address(0)); 26 | lockAmounts[_addr] = _amount; 27 | releaseTimestamps[_addr] = _releaseTimestamp; 28 | } 29 | 30 | function release(address _addr) external { 31 | require(owners[msg.sender] || msg.sender == _addr); 32 | require(block.timestamp >= releaseTimestamps[_addr]); 33 | uint256 amount = lockAmounts[_addr]; 34 | lockAmounts[_addr] = 0; 35 | releaseTimestamps[_addr] = 0; 36 | token.transfer(_addr, amount); 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /contracts/sale/TokenLockDistribute.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "zeppelin-solidity/contracts/token/ERC20/ERC20.sol"; 4 | import "zeppelin-solidity/contracts/ownership/Ownable.sol"; 5 | import "./TokenLock.sol"; 6 | 7 | contract TokenLockDistribute is Ownable { 8 | ERC20 public token; 9 | TokenLock public lock; 10 | 11 | constructor (address _token, address _lock) public { 12 | token = ERC20(_token); 13 | lock = TokenLock(_lock); 14 | } 15 | 16 | function distribute(address _to, uint256 _unlockedAmount, uint256 _lockedAmount, uint256 _releaseTimestamp) public onlyOwner { 17 | require(_to != address(0)); 18 | token.transfer(address(lock), _lockedAmount); 19 | lock.lock(_to, _lockedAmount, _releaseTimestamp); 20 | token.transfer(_to, _unlockedAmount); 21 | 22 | emit Distribute(_to, _unlockedAmount, _lockedAmount, _releaseTimestamp); 23 | } 24 | 25 | event Distribute(address indexed _to, uint256 _unlockedAmount, uint256 _lockedAmount, uint256 _releaseTimestamp); 26 | } 27 | 28 | -------------------------------------------------------------------------------- /contracts/sale/TransferBulk.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "zeppelin-solidity/contracts/token/ERC20/ERC20.sol"; 4 | import "zeppelin-solidity/contracts/ownership/Ownable.sol"; 5 | 6 | contract TransferBulk is Ownable { 7 | ERC20 public token; 8 | 9 | constructor (address _token) public { 10 | token = ERC20(_token); 11 | } 12 | 13 | function transferBulks(address[] _addrs, uint256[] _amounts) public onlyOwner { 14 | require(_addrs.length == _amounts.length); 15 | for (uint256 i = 0; i < _addrs.length; i++) { 16 | token.transfer(_addrs[i], _amounts[i]); 17 | } 18 | emit TransferBulkEvent(_addrs, _amounts); 19 | } 20 | 21 | event TransferBulkEvent(address[] _addrs, uint256[] _amounts); 22 | } 23 | -------------------------------------------------------------------------------- /contracts/token/FCTV.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | /** 4 | * @title SafeMath 5 | * @dev Math operations with safety checks that throw on error 6 | */ 7 | library SafeMath { 8 | 9 | /** 10 | * @dev Multiplies two numbers, throws on overflow. 11 | */ 12 | function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { 13 | // Gas optimization: this is cheaper than asserting 'a' not being zero, but the 14 | // benefit is lost if 'b' is also tested. 15 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 16 | if (a == 0) { 17 | return 0; 18 | } 19 | 20 | c = a * b; 21 | assert(c / a == b); 22 | return c; 23 | } 24 | 25 | /** 26 | * @dev Integer division of two numbers, truncating the quotient. 27 | */ 28 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 29 | // assert(b > 0); // Solidity automatically throws when dividing by 0 30 | // uint256 c = a / b; 31 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 32 | return a / b; 33 | } 34 | 35 | /** 36 | * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). 37 | */ 38 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 39 | assert(b <= a); 40 | return a - b; 41 | } 42 | 43 | /** 44 | * @dev Adds two numbers, throws on overflow. 45 | */ 46 | function add(uint256 a, uint256 b) internal pure returns (uint256 c) { 47 | c = a + b; 48 | assert(c >= a); 49 | return c; 50 | } 51 | } 52 | 53 | /** 54 | * @title ERC20Basic 55 | * @dev Simpler version of ERC20 interface 56 | * @dev see https://github.com/ethereum/EIPs/issues/179 57 | */ 58 | contract ERC20Basic { 59 | function totalSupply() public view returns (uint256); 60 | function balanceOf(address who) public view returns (uint256); 61 | function transfer(address to, uint256 value) public returns (bool); 62 | event Transfer(address indexed from, address indexed to, uint256 value); 63 | } 64 | 65 | /** 66 | * @title Basic token 67 | * @dev Basic version of StandardToken, with no allowances. 68 | */ 69 | contract BasicToken is ERC20Basic { 70 | using SafeMath for uint256; 71 | 72 | mapping(address => uint256) balances; 73 | 74 | uint256 totalSupply_; 75 | 76 | /** 77 | * @dev total number of tokens in existence 78 | */ 79 | function totalSupply() public view returns (uint256) { 80 | return totalSupply_; 81 | } 82 | 83 | /** 84 | * @dev transfer token for a specified address 85 | * @param _to The address to transfer to. 86 | * @param _value The amount to be transferred. 87 | */ 88 | function transfer(address _to, uint256 _value) public returns (bool) { 89 | require(_to != address(0)); 90 | require(_value <= balances[msg.sender]); 91 | 92 | balances[msg.sender] = balances[msg.sender].sub(_value); 93 | balances[_to] = balances[_to].add(_value); 94 | emit Transfer(msg.sender, _to, _value); 95 | return true; 96 | } 97 | 98 | /** 99 | * @dev Gets the balance of the specified address. 100 | * @param _owner The address to query the the balance of. 101 | * @return An uint256 representing the amount owned by the passed address. 102 | */ 103 | function balanceOf(address _owner) public view returns (uint256) { 104 | return balances[_owner]; 105 | } 106 | 107 | } 108 | 109 | /** 110 | * @title ERC20 interface 111 | * @dev see https://github.com/ethereum/EIPs/issues/20 112 | */ 113 | contract ERC20 is ERC20Basic { 114 | function allowance(address owner, address spender) 115 | public view returns (uint256); 116 | 117 | function transferFrom(address from, address to, uint256 value) 118 | public returns (bool); 119 | 120 | function approve(address spender, uint256 value) public returns (bool); 121 | event Approval( 122 | address indexed owner, 123 | address indexed spender, 124 | uint256 value 125 | ); 126 | } 127 | 128 | /** 129 | * @title Standard ERC20 token 130 | * 131 | * @dev Implementation of the basic standard token. 132 | * @dev https://github.com/ethereum/EIPs/issues/20 133 | * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol 134 | */ 135 | contract StandardToken is ERC20, BasicToken { 136 | 137 | mapping (address => mapping (address => uint256)) internal allowed; 138 | 139 | 140 | /** 141 | * @dev Transfer tokens from one address to another 142 | * @param _from address The address which you want to send tokens from 143 | * @param _to address The address which you want to transfer to 144 | * @param _value uint256 the amount of tokens to be transferred 145 | */ 146 | function transferFrom( 147 | address _from, 148 | address _to, 149 | uint256 _value 150 | ) 151 | public 152 | returns (bool) 153 | { 154 | require(_to != address(0)); 155 | require(_value <= balances[_from]); 156 | require(_value <= allowed[_from][msg.sender]); 157 | 158 | balances[_from] = balances[_from].sub(_value); 159 | balances[_to] = balances[_to].add(_value); 160 | allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); 161 | emit Transfer(_from, _to, _value); 162 | return true; 163 | } 164 | 165 | /** 166 | * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. 167 | * 168 | * Beware that changing an allowance with this method brings the risk that someone may use both the old 169 | * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this 170 | * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: 171 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 172 | * @param _spender The address which will spend the funds. 173 | * @param _value The amount of tokens to be spent. 174 | */ 175 | function approve(address _spender, uint256 _value) public returns (bool) { 176 | allowed[msg.sender][_spender] = _value; 177 | emit Approval(msg.sender, _spender, _value); 178 | return true; 179 | } 180 | 181 | /** 182 | * @dev Function to check the amount of tokens that an owner allowed to a spender. 183 | * @param _owner address The address which owns the funds. 184 | * @param _spender address The address which will spend the funds. 185 | * @return A uint256 specifying the amount of tokens still available for the spender. 186 | */ 187 | function allowance( 188 | address _owner, 189 | address _spender 190 | ) 191 | public 192 | view 193 | returns (uint256) 194 | { 195 | return allowed[_owner][_spender]; 196 | } 197 | 198 | /** 199 | * @dev Increase the amount of tokens that an owner allowed to a spender. 200 | * 201 | * approve should be called when allowed[_spender] == 0. To increment 202 | * allowed value is better to use this function to avoid 2 calls (and wait until 203 | * the first transaction is mined) 204 | * From MonolithDAO Token.sol 205 | * @param _spender The address which will spend the funds. 206 | * @param _addedValue The amount of tokens to increase the allowance by. 207 | */ 208 | function increaseApproval( 209 | address _spender, 210 | uint _addedValue 211 | ) 212 | public 213 | returns (bool) 214 | { 215 | allowed[msg.sender][_spender] = ( 216 | allowed[msg.sender][_spender].add(_addedValue)); 217 | emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); 218 | return true; 219 | } 220 | 221 | /** 222 | * @dev Decrease the amount of tokens that an owner allowed to a spender. 223 | * 224 | * approve should be called when allowed[_spender] == 0. To decrement 225 | * allowed value is better to use this function to avoid 2 calls (and wait until 226 | * the first transaction is mined) 227 | * From MonolithDAO Token.sol 228 | * @param _spender The address which will spend the funds. 229 | * @param _subtractedValue The amount of tokens to decrease the allowance by. 230 | */ 231 | function decreaseApproval( 232 | address _spender, 233 | uint _subtractedValue 234 | ) 235 | public 236 | returns (bool) 237 | { 238 | uint oldValue = allowed[msg.sender][_spender]; 239 | if (_subtractedValue > oldValue) { 240 | allowed[msg.sender][_spender] = 0; 241 | } else { 242 | allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue); 243 | } 244 | emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); 245 | return true; 246 | } 247 | 248 | } 249 | 250 | contract MultiOwnable { 251 | mapping (address => bool) owners; 252 | address unremovableOwner; 253 | 254 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 255 | event OwnershipExtended(address indexed host, address indexed guest); 256 | event OwnershipRemoved(address indexed removedOwner); 257 | 258 | modifier onlyOwner() { 259 | require(owners[msg.sender]); 260 | _; 261 | } 262 | 263 | constructor() public { 264 | owners[msg.sender] = true; 265 | unremovableOwner = msg.sender; 266 | } 267 | 268 | function addOwner(address guest) onlyOwner public { 269 | require(guest != address(0)); 270 | owners[guest] = true; 271 | emit OwnershipExtended(msg.sender, guest); 272 | } 273 | 274 | function removeOwner(address removedOwner) onlyOwner public { 275 | require(removedOwner != address(0)); 276 | require(unremovableOwner != removedOwner); 277 | delete owners[removedOwner]; 278 | emit OwnershipRemoved(removedOwner); 279 | } 280 | 281 | function transferOwnership(address newOwner) onlyOwner public { 282 | require(newOwner != address(0)); 283 | require(unremovableOwner != msg.sender); 284 | owners[newOwner] = true; 285 | delete owners[msg.sender]; 286 | emit OwnershipTransferred(msg.sender, newOwner); 287 | } 288 | 289 | function isOwner(address addr) public view returns(bool){ 290 | return owners[addr]; 291 | } 292 | } 293 | 294 | contract FCTV is StandardToken, MultiOwnable { 295 | 296 | using SafeMath for uint256; 297 | 298 | uint256 public constant TOTAL_CAP = 600000000; 299 | 300 | string public constant name = "[FCT] FirmaChain Token"; 301 | string public constant symbol = "FCT"; 302 | uint256 public constant decimals = 18; 303 | 304 | bool isTransferable = false; 305 | 306 | constructor() public { 307 | totalSupply_ = TOTAL_CAP.mul(10 ** decimals); 308 | balances[msg.sender] = totalSupply_; 309 | emit Transfer(address(0), msg.sender, balances[msg.sender]); 310 | } 311 | 312 | function unlock() external onlyOwner { 313 | isTransferable = true; 314 | } 315 | 316 | function lock() external onlyOwner { 317 | isTransferable = false; 318 | } 319 | 320 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { 321 | require(isTransferable || owners[msg.sender]); 322 | return super.transferFrom(_from, _to, _value); 323 | } 324 | 325 | function transfer(address _to, uint256 _value) public returns (bool) { 326 | require(isTransferable || owners[msg.sender]); 327 | return super.transfer(_to, _value); 328 | } 329 | 330 | // NOTE: _amount of 1 FCT is 10 ** decimals 331 | function mint(address _to, uint256 _amount) onlyOwner public returns (bool) { 332 | require(_to != address(0)); 333 | 334 | totalSupply_ = totalSupply_.add(_amount); 335 | balances[_to] = balances[_to].add(_amount); 336 | 337 | emit Mint(_to, _amount); 338 | emit Transfer(address(0), _to, _amount); 339 | 340 | return true; 341 | } 342 | 343 | // NOTE: _amount of 1 FCT is 10 ** decimals 344 | function burn(uint256 _amount) onlyOwner public { 345 | require(_amount <= balances[msg.sender]); 346 | 347 | totalSupply_ = totalSupply_.sub(_amount); 348 | balances[msg.sender] = balances[msg.sender].sub(_amount); 349 | 350 | emit Burn(msg.sender, _amount); 351 | emit Transfer(msg.sender, address(0), _amount); 352 | } 353 | 354 | event Mint(address indexed _to, uint256 _amount); 355 | event Burn(address indexed _from, uint256 _amount); 356 | } 357 | -------------------------------------------------------------------------------- /contracts/token/TestToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | /** 4 | * @title SafeMath 5 | * @dev Math operations with safety checks that throw on error 6 | */ 7 | library SafeMath { 8 | 9 | /** 10 | * @dev Multiplies two numbers, throws on overflow. 11 | */ 12 | function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { 13 | // Gas optimization: this is cheaper than asserting 'a' not being zero, but the 14 | // benefit is lost if 'b' is also tested. 15 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 16 | if (a == 0) { 17 | return 0; 18 | } 19 | 20 | c = a * b; 21 | assert(c / a == b); 22 | return c; 23 | } 24 | 25 | /** 26 | * @dev Integer division of two numbers, truncating the quotient. 27 | */ 28 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 29 | // assert(b > 0); // Solidity automatically throws when dividing by 0 30 | // uint256 c = a / b; 31 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 32 | return a / b; 33 | } 34 | 35 | /** 36 | * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). 37 | */ 38 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 39 | assert(b <= a); 40 | return a - b; 41 | } 42 | 43 | /** 44 | * @dev Adds two numbers, throws on overflow. 45 | */ 46 | function add(uint256 a, uint256 b) internal pure returns (uint256 c) { 47 | c = a + b; 48 | assert(c >= a); 49 | return c; 50 | } 51 | } 52 | 53 | /** 54 | * @title ERC20Basic 55 | * @dev Simpler version of ERC20 interface 56 | * @dev see https://github.com/ethereum/EIPs/issues/179 57 | */ 58 | contract ERC20Basic { 59 | function totalSupply() public view returns (uint256); 60 | function balanceOf(address who) public view returns (uint256); 61 | function transfer(address to, uint256 value) public returns (bool); 62 | event Transfer(address indexed from, address indexed to, uint256 value); 63 | } 64 | 65 | /** 66 | * @title Basic token 67 | * @dev Basic version of StandardToken, with no allowances. 68 | */ 69 | contract BasicToken is ERC20Basic { 70 | using SafeMath for uint256; 71 | 72 | mapping(address => uint256) balances; 73 | 74 | uint256 totalSupply_; 75 | 76 | /** 77 | * @dev total number of tokens in existence 78 | */ 79 | function totalSupply() public view returns (uint256) { 80 | return totalSupply_; 81 | } 82 | 83 | /** 84 | * @dev transfer token for a specified address 85 | * @param _to The address to transfer to. 86 | * @param _value The amount to be transferred. 87 | */ 88 | function transfer(address _to, uint256 _value) public returns (bool) { 89 | require(_to != address(0)); 90 | require(_value <= balances[msg.sender]); 91 | 92 | balances[msg.sender] = balances[msg.sender].sub(_value); 93 | balances[_to] = balances[_to].add(_value); 94 | emit Transfer(msg.sender, _to, _value); 95 | return true; 96 | } 97 | 98 | /** 99 | * @dev Gets the balance of the specified address. 100 | * @param _owner The address to query the the balance of. 101 | * @return An uint256 representing the amount owned by the passed address. 102 | */ 103 | function balanceOf(address _owner) public view returns (uint256) { 104 | return balances[_owner]; 105 | } 106 | 107 | } 108 | 109 | /** 110 | * @title ERC20 interface 111 | * @dev see https://github.com/ethereum/EIPs/issues/20 112 | */ 113 | contract ERC20 is ERC20Basic { 114 | function allowance(address owner, address spender) 115 | public view returns (uint256); 116 | 117 | function transferFrom(address from, address to, uint256 value) 118 | public returns (bool); 119 | 120 | function approve(address spender, uint256 value) public returns (bool); 121 | event Approval( 122 | address indexed owner, 123 | address indexed spender, 124 | uint256 value 125 | ); 126 | } 127 | 128 | /** 129 | * @title Standard ERC20 token 130 | * 131 | * @dev Implementation of the basic standard token. 132 | * @dev https://github.com/ethereum/EIPs/issues/20 133 | * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol 134 | */ 135 | contract StandardToken is ERC20, BasicToken { 136 | 137 | mapping (address => mapping (address => uint256)) internal allowed; 138 | 139 | 140 | /** 141 | * @dev Transfer tokens from one address to another 142 | * @param _from address The address which you want to send tokens from 143 | * @param _to address The address which you want to transfer to 144 | * @param _value uint256 the amount of tokens to be transferred 145 | */ 146 | function transferFrom( 147 | address _from, 148 | address _to, 149 | uint256 _value 150 | ) 151 | public 152 | returns (bool) 153 | { 154 | require(_to != address(0)); 155 | require(_value <= balances[_from]); 156 | require(_value <= allowed[_from][msg.sender]); 157 | 158 | balances[_from] = balances[_from].sub(_value); 159 | balances[_to] = balances[_to].add(_value); 160 | allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); 161 | emit Transfer(_from, _to, _value); 162 | return true; 163 | } 164 | 165 | /** 166 | * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. 167 | * 168 | * Beware that changing an allowance with this method brings the risk that someone may use both the old 169 | * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this 170 | * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: 171 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 172 | * @param _spender The address which will spend the funds. 173 | * @param _value The amount of tokens to be spent. 174 | */ 175 | function approve(address _spender, uint256 _value) public returns (bool) { 176 | allowed[msg.sender][_spender] = _value; 177 | emit Approval(msg.sender, _spender, _value); 178 | return true; 179 | } 180 | 181 | /** 182 | * @dev Function to check the amount of tokens that an owner allowed to a spender. 183 | * @param _owner address The address which owns the funds. 184 | * @param _spender address The address which will spend the funds. 185 | * @return A uint256 specifying the amount of tokens still available for the spender. 186 | */ 187 | function allowance( 188 | address _owner, 189 | address _spender 190 | ) 191 | public 192 | view 193 | returns (uint256) 194 | { 195 | return allowed[_owner][_spender]; 196 | } 197 | 198 | /** 199 | * @dev Increase the amount of tokens that an owner allowed to a spender. 200 | * 201 | * approve should be called when allowed[_spender] == 0. To increment 202 | * allowed value is better to use this function to avoid 2 calls (and wait until 203 | * the first transaction is mined) 204 | * From MonolithDAO Token.sol 205 | * @param _spender The address which will spend the funds. 206 | * @param _addedValue The amount of tokens to increase the allowance by. 207 | */ 208 | function increaseApproval( 209 | address _spender, 210 | uint _addedValue 211 | ) 212 | public 213 | returns (bool) 214 | { 215 | allowed[msg.sender][_spender] = ( 216 | allowed[msg.sender][_spender].add(_addedValue)); 217 | emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); 218 | return true; 219 | } 220 | 221 | /** 222 | * @dev Decrease the amount of tokens that an owner allowed to a spender. 223 | * 224 | * approve should be called when allowed[_spender] == 0. To decrement 225 | * allowed value is better to use this function to avoid 2 calls (and wait until 226 | * the first transaction is mined) 227 | * From MonolithDAO Token.sol 228 | * @param _spender The address which will spend the funds. 229 | * @param _subtractedValue The amount of tokens to decrease the allowance by. 230 | */ 231 | function decreaseApproval( 232 | address _spender, 233 | uint _subtractedValue 234 | ) 235 | public 236 | returns (bool) 237 | { 238 | uint oldValue = allowed[msg.sender][_spender]; 239 | if (_subtractedValue > oldValue) { 240 | allowed[msg.sender][_spender] = 0; 241 | } else { 242 | allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue); 243 | } 244 | emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); 245 | return true; 246 | } 247 | 248 | } 249 | 250 | contract MultiOwnable { 251 | mapping (address => bool) owners; 252 | address unremovableOwner; 253 | 254 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 255 | event OwnershipExtended(address indexed host, address indexed guest); 256 | event OwnershipRemoved(address indexed removedOwner); 257 | 258 | modifier onlyOwner() { 259 | require(owners[msg.sender]); 260 | _; 261 | } 262 | 263 | constructor() public { 264 | owners[msg.sender] = true; 265 | unremovableOwner = msg.sender; 266 | } 267 | 268 | function addOwner(address guest) onlyOwner public { 269 | require(guest != address(0)); 270 | owners[guest] = true; 271 | emit OwnershipExtended(msg.sender, guest); 272 | } 273 | 274 | function removeOwner(address removedOwner) onlyOwner public { 275 | require(removedOwner != address(0)); 276 | require(unremovableOwner != removedOwner); 277 | delete owners[removedOwner]; 278 | emit OwnershipRemoved(removedOwner); 279 | } 280 | 281 | function transferOwnership(address newOwner) onlyOwner public { 282 | require(newOwner != address(0)); 283 | require(unremovableOwner != msg.sender); 284 | owners[newOwner] = true; 285 | delete owners[msg.sender]; 286 | emit OwnershipTransferred(msg.sender, newOwner); 287 | } 288 | 289 | function isOwner(address addr) public view returns(bool){ 290 | return owners[addr]; 291 | } 292 | } 293 | 294 | contract TestToken is StandardToken, MultiOwnable { 295 | 296 | using SafeMath for uint256; 297 | 298 | uint256 public constant TOTAL_CAP = 600000000; 299 | 300 | string public constant name = "[Firma] TestToken"; 301 | string public constant symbol = "TET"; 302 | uint256 public constant decimals = 18; 303 | 304 | bool isTransferable = false; 305 | 306 | constructor() public { 307 | totalSupply_ = TOTAL_CAP.mul(10 ** decimals); 308 | balances[msg.sender] = totalSupply_; 309 | emit Transfer(address(0), msg.sender, balances[msg.sender]); 310 | } 311 | 312 | function unlock() external onlyOwner { 313 | isTransferable = true; 314 | } 315 | 316 | function lock() external onlyOwner { 317 | isTransferable = false; 318 | } 319 | 320 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { 321 | require(isTransferable || owners[msg.sender]); 322 | return super.transferFrom(_from, _to, _value); 323 | } 324 | 325 | function transfer(address _to, uint256 _value) public returns (bool) { 326 | require(isTransferable || owners[msg.sender]); 327 | return super.transfer(_to, _value); 328 | } 329 | 330 | // NOTE: _amount of 1 FCT is 10 ** decimals 331 | function mint(address _to, uint256 _amount) onlyOwner public returns (bool) { 332 | require(_to != address(0)); 333 | 334 | totalSupply_ = totalSupply_.add(_amount); 335 | balances[_to] = balances[_to].add(_amount); 336 | 337 | emit Mint(_to, _amount); 338 | emit Transfer(address(0), _to, _amount); 339 | 340 | return true; 341 | } 342 | 343 | // NOTE: _amount of 1 FCT is 10 ** decimals 344 | function burn(uint256 _amount) onlyOwner public { 345 | require(_amount <= balances[msg.sender]); 346 | 347 | totalSupply_ = totalSupply_.sub(_amount); 348 | balances[msg.sender] = balances[msg.sender].sub(_amount); 349 | 350 | emit Burn(msg.sender, _amount); 351 | emit Transfer(msg.sender, address(0), _amount); 352 | } 353 | 354 | event Mint(address indexed _to, uint256 _amount); 355 | event Burn(address indexed _from, uint256 _amount); 356 | } 357 | -------------------------------------------------------------------------------- /contracts/token/[deprecated]FCT.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | /** 4 | * @title SafeMath 5 | * @dev Math operations with safety checks that throw on error 6 | */ 7 | library SafeMath { 8 | 9 | /** 10 | * @dev Multiplies two numbers, throws on overflow. 11 | */ 12 | function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { 13 | // Gas optimization: this is cheaper than asserting 'a' not being zero, but the 14 | // benefit is lost if 'b' is also tested. 15 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 16 | if (a == 0) { 17 | return 0; 18 | } 19 | 20 | c = a * b; 21 | assert(c / a == b); 22 | return c; 23 | } 24 | 25 | /** 26 | * @dev Integer division of two numbers, truncating the quotient. 27 | */ 28 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 29 | // assert(b > 0); // Solidity automatically throws when dividing by 0 30 | // uint256 c = a / b; 31 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 32 | return a / b; 33 | } 34 | 35 | /** 36 | * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). 37 | */ 38 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 39 | assert(b <= a); 40 | return a - b; 41 | } 42 | 43 | /** 44 | * @dev Adds two numbers, throws on overflow. 45 | */ 46 | function add(uint256 a, uint256 b) internal pure returns (uint256 c) { 47 | c = a + b; 48 | assert(c >= a); 49 | return c; 50 | } 51 | } 52 | 53 | /** 54 | * @title ERC20Basic 55 | * @dev Simpler version of ERC20 interface 56 | * @dev see https://github.com/ethereum/EIPs/issues/179 57 | */ 58 | contract ERC20Basic { 59 | function totalSupply() public view returns (uint256); 60 | function balanceOf(address who) public view returns (uint256); 61 | function transfer(address to, uint256 value) public returns (bool); 62 | event Transfer(address indexed from, address indexed to, uint256 value); 63 | } 64 | 65 | /** 66 | * @title Basic token 67 | * @dev Basic version of StandardToken, with no allowances. 68 | */ 69 | contract BasicToken is ERC20Basic { 70 | using SafeMath for uint256; 71 | 72 | mapping(address => uint256) balances; 73 | 74 | uint256 totalSupply_; 75 | 76 | /** 77 | * @dev total number of tokens in existence 78 | */ 79 | function totalSupply() public view returns (uint256) { 80 | return totalSupply_; 81 | } 82 | 83 | /** 84 | * @dev transfer token for a specified address 85 | * @param _to The address to transfer to. 86 | * @param _value The amount to be transferred. 87 | */ 88 | function transfer(address _to, uint256 _value) public returns (bool) { 89 | require(_to != address(0)); 90 | require(_value <= balances[msg.sender]); 91 | 92 | balances[msg.sender] = balances[msg.sender].sub(_value); 93 | balances[_to] = balances[_to].add(_value); 94 | emit Transfer(msg.sender, _to, _value); 95 | return true; 96 | } 97 | 98 | /** 99 | * @dev Gets the balance of the specified address. 100 | * @param _owner The address to query the the balance of. 101 | * @return An uint256 representing the amount owned by the passed address. 102 | */ 103 | function balanceOf(address _owner) public view returns (uint256) { 104 | return balances[_owner]; 105 | } 106 | 107 | } 108 | 109 | /** 110 | * @title ERC20 interface 111 | * @dev see https://github.com/ethereum/EIPs/issues/20 112 | */ 113 | contract ERC20 is ERC20Basic { 114 | function allowance(address owner, address spender) 115 | public view returns (uint256); 116 | 117 | function transferFrom(address from, address to, uint256 value) 118 | public returns (bool); 119 | 120 | function approve(address spender, uint256 value) public returns (bool); 121 | event Approval( 122 | address indexed owner, 123 | address indexed spender, 124 | uint256 value 125 | ); 126 | } 127 | 128 | /** 129 | * @title Standard ERC20 token 130 | * 131 | * @dev Implementation of the basic standard token. 132 | * @dev https://github.com/ethereum/EIPs/issues/20 133 | * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol 134 | */ 135 | contract StandardToken is ERC20, BasicToken { 136 | 137 | mapping (address => mapping (address => uint256)) internal allowed; 138 | 139 | 140 | /** 141 | * @dev Transfer tokens from one address to another 142 | * @param _from address The address which you want to send tokens from 143 | * @param _to address The address which you want to transfer to 144 | * @param _value uint256 the amount of tokens to be transferred 145 | */ 146 | function transferFrom( 147 | address _from, 148 | address _to, 149 | uint256 _value 150 | ) 151 | public 152 | returns (bool) 153 | { 154 | require(_to != address(0)); 155 | require(_value <= balances[_from]); 156 | require(_value <= allowed[_from][msg.sender]); 157 | 158 | balances[_from] = balances[_from].sub(_value); 159 | balances[_to] = balances[_to].add(_value); 160 | allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); 161 | emit Transfer(_from, _to, _value); 162 | return true; 163 | } 164 | 165 | /** 166 | * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. 167 | * 168 | * Beware that changing an allowance with this method brings the risk that someone may use both the old 169 | * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this 170 | * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: 171 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 172 | * @param _spender The address which will spend the funds. 173 | * @param _value The amount of tokens to be spent. 174 | */ 175 | function approve(address _spender, uint256 _value) public returns (bool) { 176 | allowed[msg.sender][_spender] = _value; 177 | emit Approval(msg.sender, _spender, _value); 178 | return true; 179 | } 180 | 181 | /** 182 | * @dev Function to check the amount of tokens that an owner allowed to a spender. 183 | * @param _owner address The address which owns the funds. 184 | * @param _spender address The address which will spend the funds. 185 | * @return A uint256 specifying the amount of tokens still available for the spender. 186 | */ 187 | function allowance( 188 | address _owner, 189 | address _spender 190 | ) 191 | public 192 | view 193 | returns (uint256) 194 | { 195 | return allowed[_owner][_spender]; 196 | } 197 | 198 | /** 199 | * @dev Increase the amount of tokens that an owner allowed to a spender. 200 | * 201 | * approve should be called when allowed[_spender] == 0. To increment 202 | * allowed value is better to use this function to avoid 2 calls (and wait until 203 | * the first transaction is mined) 204 | * From MonolithDAO Token.sol 205 | * @param _spender The address which will spend the funds. 206 | * @param _addedValue The amount of tokens to increase the allowance by. 207 | */ 208 | function increaseApproval( 209 | address _spender, 210 | uint _addedValue 211 | ) 212 | public 213 | returns (bool) 214 | { 215 | allowed[msg.sender][_spender] = ( 216 | allowed[msg.sender][_spender].add(_addedValue)); 217 | emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); 218 | return true; 219 | } 220 | 221 | /** 222 | * @dev Decrease the amount of tokens that an owner allowed to a spender. 223 | * 224 | * approve should be called when allowed[_spender] == 0. To decrement 225 | * allowed value is better to use this function to avoid 2 calls (and wait until 226 | * the first transaction is mined) 227 | * From MonolithDAO Token.sol 228 | * @param _spender The address which will spend the funds. 229 | * @param _subtractedValue The amount of tokens to decrease the allowance by. 230 | */ 231 | function decreaseApproval( 232 | address _spender, 233 | uint _subtractedValue 234 | ) 235 | public 236 | returns (bool) 237 | { 238 | uint oldValue = allowed[msg.sender][_spender]; 239 | if (_subtractedValue > oldValue) { 240 | allowed[msg.sender][_spender] = 0; 241 | } else { 242 | allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue); 243 | } 244 | emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); 245 | return true; 246 | } 247 | 248 | } 249 | 250 | contract MultiOwnable { 251 | mapping (address => bool) owners; 252 | address unremovableOwner; 253 | 254 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 255 | event OwnershipExtended(address indexed host, address indexed guest); 256 | event OwnershipRemoved(address indexed removedOwner); 257 | 258 | modifier onlyOwner() { 259 | require(owners[msg.sender]); 260 | _; 261 | } 262 | 263 | constructor() public { 264 | owners[msg.sender] = true; 265 | unremovableOwner = msg.sender; 266 | } 267 | 268 | function addOwner(address guest) onlyOwner public { 269 | require(guest != address(0)); 270 | owners[guest] = true; 271 | emit OwnershipExtended(msg.sender, guest); 272 | } 273 | 274 | function removeOwner(address removedOwner) onlyOwner public { 275 | require(removedOwner != address(0)); 276 | require(unremovableOwner != removedOwner); 277 | delete owners[removedOwner]; 278 | emit OwnershipRemoved(removedOwner); 279 | } 280 | 281 | function transferOwnership(address newOwner) onlyOwner public { 282 | require(newOwner != address(0)); 283 | require(unremovableOwner != msg.sender); 284 | owners[newOwner] = true; 285 | delete owners[msg.sender]; 286 | emit OwnershipTransferred(msg.sender, newOwner); 287 | } 288 | 289 | function isOwner(address addr) public view returns(bool){ 290 | return owners[addr]; 291 | } 292 | } 293 | 294 | contract FCT is StandardToken, MultiOwnable { 295 | 296 | using SafeMath for uint256; 297 | 298 | uint256 public constant TOTAL_CAP = 2200000000; 299 | 300 | string public constant name = "FirmaChain Token"; 301 | string public constant symbol = "FCT"; 302 | uint256 public constant decimals = 18; 303 | 304 | bool isTransferable = false; 305 | 306 | constructor() public { 307 | totalSupply_ = TOTAL_CAP.mul(10 ** decimals); 308 | balances[msg.sender] = totalSupply_; 309 | emit Transfer(address(0), msg.sender, balances[msg.sender]); 310 | } 311 | 312 | function unlock() external onlyOwner { 313 | isTransferable = true; 314 | } 315 | 316 | function lock() external onlyOwner { 317 | isTransferable = false; 318 | } 319 | 320 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { 321 | require(isTransferable || owners[msg.sender]); 322 | return super.transferFrom(_from, _to, _value); 323 | } 324 | 325 | function transfer(address _to, uint256 _value) public returns (bool) { 326 | require(isTransferable || owners[msg.sender]); 327 | return super.transfer(_to, _value); 328 | } 329 | 330 | // NOTE: _amount of 1 FCT is 10 ** decimals 331 | function mint(address _to, uint256 _amount) onlyOwner public returns (bool) { 332 | require(_to != address(0)); 333 | 334 | totalSupply_ = totalSupply_.add(_amount); 335 | balances[_to] = balances[_to].add(_amount); 336 | 337 | emit Mint(_to, _amount); 338 | emit Transfer(address(0), _to, _amount); 339 | 340 | return true; 341 | } 342 | 343 | // NOTE: _amount of 1 FCT is 10 ** decimals 344 | function burn(uint256 _amount) onlyOwner public { 345 | require(_amount <= balances[msg.sender]); 346 | 347 | totalSupply_ = totalSupply_.sub(_amount); 348 | balances[msg.sender] = balances[msg.sender].sub(_amount); 349 | 350 | emit Burn(msg.sender, _amount); 351 | emit Transfer(msg.sender, address(0), _amount); 352 | } 353 | 354 | event Mint(address indexed _to, uint256 _amount); 355 | event Burn(address indexed _from, uint256 _amount); 356 | } 357 | -------------------------------------------------------------------------------- /contracts/utils/MultiOwnable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | contract MultiOwnable { 4 | mapping (address => bool) owners; 5 | address unremovableOwner; 6 | 7 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 8 | event OwnershipExtended(address indexed host, address indexed guest); 9 | event OwnershipRemoved(address indexed removedOwner); 10 | 11 | modifier onlyOwner() { 12 | require(owners[msg.sender]); 13 | _; 14 | } 15 | 16 | constructor() public { 17 | owners[msg.sender] = true; 18 | unremovableOwner = msg.sender; 19 | } 20 | 21 | function addOwner(address guest) onlyOwner public { 22 | require(guest != address(0)); 23 | owners[guest] = true; 24 | emit OwnershipExtended(msg.sender, guest); 25 | } 26 | 27 | function removeOwner(address removedOwner) onlyOwner public { 28 | require(removedOwner != address(0)); 29 | require(unremovableOwner != removedOwner); 30 | delete owners[removedOwner]; 31 | emit OwnershipRemoved(removedOwner); 32 | } 33 | 34 | function transferOwnership(address newOwner) onlyOwner public { 35 | require(newOwner != address(0)); 36 | require(unremovableOwner != msg.sender); 37 | owners[newOwner] = true; 38 | delete owners[msg.sender]; 39 | emit OwnershipTransferred(msg.sender, newOwner); 40 | } 41 | 42 | function isOwner(address addr) public view returns(bool){ 43 | return owners[addr]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /contracts/utils/TokenSwap.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "zeppelin-solidity/contracts/token/ERC20/ERC20.sol"; 4 | import "zeppelin-solidity/contracts/math/SafeMath.sol"; 5 | import "zeppelin-solidity/contracts/ownership/Ownable.sol"; 6 | 7 | contract TokenSwap is Ownable{ 8 | using SafeMath for uint256; 9 | address public wallet; 10 | ERC20 public token; 11 | mapping (address => uint256) public swapAmount; 12 | mapping (address => string) public swapFirmaNetAddress; 13 | 14 | constructor (address _token) public { 15 | token = ERC20(_token); 16 | wallet = msg.sender; 17 | } 18 | 19 | 20 | function registerSwap(string _firmaNetAddress, uint256 balance) public payable { 21 | uint256 my_balance = token.balanceOf(msg.sender); 22 | require(balance > 0, "Should be more than 0"); 23 | require(balance <= my_balance, "Should be equal or less than what I have"); 24 | uint256 allowBalance = token.allowance(msg.sender, address(this)); 25 | require(balance <= allowBalance, "Should be equal or less than what I allowed"); 26 | require(token.transferFrom(msg.sender, address(this), balance), "'transferFrom' function call failed"); 27 | 28 | swapAmount[msg.sender] = swapAmount[msg.sender].add(balance); 29 | swapFirmaNetAddress[msg.sender] = _firmaNetAddress; 30 | emit Swap(msg.sender, balance, _firmaNetAddress); 31 | } 32 | event Swap(address sender, uint256 balance, string firmaNetAddress); 33 | 34 | 35 | function changeAddress(string _firmaNetAddress) public { 36 | swapFirmaNetAddress[msg.sender] = _firmaNetAddress; 37 | 38 | emit ChangeAddress(msg.sender, _firmaNetAddress); 39 | } 40 | event ChangeAddress(address sender, string firmaNetAddress); 41 | 42 | 43 | function checkSwapAmount(address _addr) public view returns (uint256) { 44 | return swapAmount[_addr]; 45 | } 46 | 47 | function checkFirmaAddress(address _addr) public view returns (string) { 48 | return swapFirmaNetAddress[_addr]; 49 | } 50 | 51 | 52 | function withdraw() public onlyOwner { 53 | token.transfer(msg.sender, token.balanceOf(address(this))); 54 | msg.sender.transfer(address(this).balance); 55 | } 56 | } -------------------------------------------------------------------------------- /exec/DeployBulkTransferFCTV.js: -------------------------------------------------------------------------------- 1 | let list=`` 2 | 3 | const FCTV = artifacts.require('FCTV'); 4 | const FCTVTransferBulk = artifacts.require('FCTVTransferBulk'); 5 | 6 | function toWei(n) { 7 | return new web3.BigNumber(String(web3.toWei(n, 'ether'))); 8 | } 9 | 10 | module.exports = async function(callback) { 11 | //let owner = "0x9730ad937b270ee1ef7cacc38dbce30a33048ef9"; //Ropsten 12 | let owner = "0xc6e0ee2975a9e245319a9452811845144fee1338" //Mainnet 13 | // TODO: input address 14 | let token = await FCTV.at("0xe1bad922f84b198a08292fb600319300ae32471b"); 15 | let bulk = await FCTVTransferBulk.at("0x0da79331d7a3bf6f714b36e0275509c429a2dd20"); 16 | 17 | const BULK_INDEX = 2; 18 | const BULK_COUNT = 100; 19 | let addrs = []; 20 | let weis = []; 21 | let lists = list.split("\n"); 22 | for(let i = 0; i < lists.length; i++) { 23 | let parts = lists[i].split("\t"); 24 | addrs.push(parts[0]); 25 | weis.push(toWei(parts[1])); 26 | } 27 | console.log("addr") 28 | console.log(addrs.slice(BULK_INDEX * BULK_COUNT, (BULK_INDEX + 1) * BULK_COUNT).length); 29 | console.log(addrs.slice(BULK_INDEX * BULK_COUNT, (BULK_INDEX + 1) * BULK_COUNT)); 30 | console.log("weis") 31 | console.log(weis.slice(BULK_INDEX * BULK_COUNT, (BULK_INDEX + 1) * BULK_COUNT).length); 32 | console.log(weis.slice(BULK_INDEX * BULK_COUNT, (BULK_INDEX + 1) * BULK_COUNT)); 33 | console.log("=======================") 34 | 35 | try { 36 | let result = await bulk.transferBulks(addrs.slice(BULK_INDEX * BULK_COUNT, (BULK_INDEX + 1) * BULK_COUNT), weis.slice(BULK_INDEX * BULK_COUNT, (BULK_INDEX + 1) * BULK_COUNT), {from: owner}); 37 | console.log(result); 38 | } catch(e){ 39 | console.log("error:", e) 40 | } 41 | console.log("Complete" + BULK_INDEX); 42 | } 43 | 44 | -------------------------------------------------------------------------------- /exec/DeployFCTV.js: -------------------------------------------------------------------------------- 1 | const FCTV = artifacts.require('FCTV'); 2 | 3 | function toWei(n) { 4 | return new web3.BigNumber(String(web3.toWei(n, 'ether'))); 5 | } 6 | 7 | module.exports = async function(callback) { 8 | //let owner = "0x9730ad937b270ee1ef7cacc38dbce30a33048ef9"; //Ropsten 9 | let owner = "0xc6e0ee2975a9e245319a9452811845144fee1338" //Mainnet 10 | try{ 11 | let token = await FCTV.new({from: owner}); 12 | }catch(e){ 13 | console.log(e) 14 | } 15 | console.log("token", token.address); 16 | } 17 | -------------------------------------------------------------------------------- /exec/DeploySimpleContract.js: -------------------------------------------------------------------------------- 1 | const simple_contract = artifacts.require('simple_contract'); 2 | 3 | function toWei(n) { 4 | return new web3.BigNumber(String(web3.toWei(n, 'ether'))); 5 | } 6 | 7 | module.exports = async function(callback) { 8 | //let owner = "0x9730ad937b270ee1ef7cacc38dbce30a33048ef9"; //Ropsten 9 | let owner = "0xc6e0ee2975a9e245319a9452811845144fee1338" //Mainnet 10 | try{ 11 | let aa = await simple_contract.new({from: owner}); 12 | }catch(e){ 13 | console.log(e) 14 | } 15 | console.log("token", aa); 16 | } 17 | -------------------------------------------------------------------------------- /exec/DeploySwapContract.js: -------------------------------------------------------------------------------- 1 | const TokenSwap = artifacts.require('TokenSwap'); 2 | 3 | function toWei(n) { 4 | return new web3.BigNumber(String(web3.toWei(n, 'ether'))); 5 | } 6 | 7 | module.exports = async function(callback) { 8 | let owner = "0xc6e0ee2975a9e245319a9452811845144fee1338"; //Ropsten 9 | //let owner = "0xc6e0ee2975a9e245319a9452811845144fee1338" //Mainnet 10 | 11 | try{ 12 | let aa = await TokenSwap.new("0xcacc32f1438e07f333051dd265c326ded0b6c0a8", {from: owner}); 13 | }catch(e){ 14 | console.log(e) 15 | } 16 | console.log("token swap", aa); 17 | } 18 | -------------------------------------------------------------------------------- /exec/DeployTestSwap.js: -------------------------------------------------------------------------------- 1 | const TokenSwap = artifacts.require('TokenSwap'); 2 | const FCTV = artifacts.require('FCTV'); 3 | 4 | function toWei(n) { 5 | return new web3.BigNumber(String(web3.toWei(n, 'ether'))); 6 | } 7 | 8 | module.exports = async function(callback) { 9 | let owner = "0xc6e0ee2975a9e245319a9452811845144fee1338"; //Ropsten 10 | //let owner = "0xc6e0ee2975a9e245319a9452811845144fee1338" //Mainnet 11 | 12 | try{ 13 | let swap_address = "0x0d5734c6406d90205e5c5f9fa95bb1301c168e6f" 14 | let token = await FCTV.at("0xcacc32f1438e07f333051dd265c326ded0b6c0a8") 15 | let swap = await TokenSwap.at(swap_address) 16 | 17 | let balance = await token.balanceOf("0xc6e0ee2975a9e245319a9452811845144fee1338") 18 | console.log("my balance", balance); 19 | 20 | let b = await swap.checkSwapAmount("0xc6e0ee2975a9e245319a9452811845144fee1338"); 21 | let c = await swap.checkFirmaAddress("0xc6e0ee2975a9e245319a9452811845144fee1338"); 22 | console.log("amount", b ) 23 | console.log("address", c ) 24 | 25 | let transfer_balance = toWei(132542) 26 | let allow = await token.approve(swap_address, transfer_balance) 27 | let a = await swap.registerSwap("firma1vm9hdhf4e02pt85pk634ucqtv54t0uelcjsvtv", transfer_balance); 28 | console.log("---------------------") 29 | console.log("swap", a) 30 | console.log("---------------------") 31 | 32 | b = await swap.checkSwapAmount("0xc6e0ee2975a9e245319a9452811845144fee1338"); 33 | c = await swap.checkFirmaAddress("0xc6e0ee2975a9e245319a9452811845144fee1338"); 34 | console.log("amount", b ) 35 | console.log("address", c ) 36 | 37 | let withdraw = await swap.withdraw(); 38 | console.log("withdraw", withdraw) 39 | 40 | } catch(err) { 41 | console.log(err, 'err') 42 | } 43 | 44 | console.log("token swap end") 45 | } 46 | -------------------------------------------------------------------------------- /exec/DeployTestToken.js: -------------------------------------------------------------------------------- 1 | const TestToken = artifacts.require('TestToken'); 2 | 3 | function toWei(n) { 4 | return new web3.BigNumber(String(web3.toWei(n, 'ether'))); 5 | } 6 | 7 | module.exports = async function(callback) { 8 | let owner = "0xC6E0ee2975a9E245319A9452811845144fEE1338"; //Ropsten 9 | //let owner = "0xc6e0ee2975a9e245319a9452811845144fee1338" //Mainnet 10 | let token = await TestToken.new({from: owner}); 11 | 12 | console.log("token", token.address); 13 | } 14 | -------------------------------------------------------------------------------- /exec/DistributeFCTV.js: -------------------------------------------------------------------------------- 1 | const FCTV = artifacts.require('FCTV'); 2 | 3 | function toWei(n) { 4 | return new web3.BigNumber(String(web3.toWei(n, 'ether'))); 5 | } 6 | 7 | module.exports = async function(callback) { 8 | //Mainnet 9 | const owner = "0xC6E0ee2975a9E245319A9452811845144fEE1338"; 10 | const token = await FCTV.at("0xe1bad922f84b198a08292fb600319300ae32471b"); 11 | //Ropsten 12 | // format: [address, unlockamount, lockamount] 13 | // example: ["0x6dF0473924D34e6608F9E8439742B9d1D5f30c89", 1897026.74, 758810.70], 14 | let transferData = [ 15 | ["0x3cf1a9ab722694efecb81225071439f1aba5df39", 1000000.00] 16 | ]; 17 | for(let i = 0; i < transferData.length; i++) { 18 | let addr = transferData[i][0]; 19 | let amount = toWei(transferData[i][1]); 20 | 21 | console.log("Transfering to", addr); 22 | token.transfer(addr, amount, {from: owner}).then((tx)=> { 23 | console.log("===================================================") 24 | console.log("addr : ", addr) 25 | console.log(tx) 26 | console.log("Complete"); 27 | console.log("===================================================") 28 | }); 29 | 30 | await new Promise(r=>setTimeout(r, 5000)); 31 | } 32 | console.log("End"); 33 | } -------------------------------------------------------------------------------- /exec/SignMessage.js: -------------------------------------------------------------------------------- 1 | const FCTV = artifacts.require('FCTV'); 2 | 3 | function toWei(n) { 4 | return new web3.BigNumber(String(web3.toWei(n, 'ether'))); 5 | } 6 | 7 | module.exports = async function(callback) { 8 | try{ 9 | var accounts = await web3.eth.getAccounts() 10 | console.log(accounts) 11 | 12 | const owner = "0xC6E0ee2975a9E245319A9452811845144fEE1338"; 13 | const message = "[Etherscan.io 12/09/2019 14:24:10] I, hereby verify that I am the owner/creator of the address [0xe1bad922f84b198a08292fb600319300ae32471b]"; 14 | 15 | let token = await FCTV.at("0xe1bad922f84b198a08292fb600319300ae32471b"); 16 | 17 | let a = await web3.eth.sign(message, owner); 18 | console.log("sign hash", a) 19 | console.log("End"); 20 | } catch(err) { 21 | console.log("err", err) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | var FCT = artifacts.require('FCT'); 2 | var FCTV = artifacts.require('FCTV'); 3 | var TokenLock = artifacts.require('TokenLock'); 4 | var TokenLockDistribute = artifacts.require('TokenLockDistribute'); 5 | 6 | module.exports = async (deployer, network) => { 7 | deployer.then( async() => { 8 | // await deployer.deploy(FCT); 9 | // await deployer.deploy(TokenLock, FCT.address) 10 | // await deployer.deploy(TokenLockDistribute, FCT.address, TokenLock.address) 11 | await deployer.deploy(FCTV); 12 | }) 13 | }; 14 | -------------------------------------------------------------------------------- /migrations/3_deploy_bulk.js: -------------------------------------------------------------------------------- 1 | var TokenSwap = artifacts.require('TokenSwap'); 2 | var FCTV = artifacts.require('FCTV'); 3 | 4 | module.exports = function(deployer) { 5 | deployer.then( async() => { 6 | await deployer.deploy(FCTV); 7 | await deployer.deploy(TokenSwap, FCTV.address); 8 | }) 9 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "FirmaChainContract", 3 | "version": "1.0.0", 4 | "description": "This is a FirmaChain Token. MIT License", 5 | "main": "truffle.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "truffle test --network ganache", 11 | "test:ropsten": "truffle test --network ropsten" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "chai": "^4.1.2", 17 | "chai-as-promised": "^7.1.1", 18 | "chai-bignumber": "^2.0.2", 19 | "mocha-logger": "^1.0.5", 20 | "zeppelin-solidity": "1.10.0" 21 | }, 22 | "devDependencies": { 23 | "eth-gas-reporter": "^0.1.10", 24 | "truffle-hdwallet-provider": "0.0.5", 25 | "truffle-ledger-provider": "0.0.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/FCT.js: -------------------------------------------------------------------------------- 1 | const util = require("./test-util") 2 | const FCT = artifacts.require('FCT'); 3 | const BigNumber = web3.BigNumber 4 | const L = require('mocha-logger') 5 | 6 | require('chai') 7 | .use(require('chai-bignumber')(BigNumber)) 8 | .use(require('chai-as-promised')) 9 | .should(); 10 | 11 | contract('[deprecated] FirmaChain Token', function(accounts) { 12 | 13 | let fcti; 14 | const owner = accounts[0] 15 | const subOwner = accounts[1] 16 | const guest = accounts[2] 17 | 18 | const amount = 1; 19 | 20 | const _name = 'FirmaChain Token'; 21 | const _symbol = 'FCT'; 22 | const _decimals = 18; 23 | 24 | before(async () => { 25 | fcti = await FCT.deployed(); 26 | }) 27 | 28 | it('has a name', async () => { 29 | const name = await fcti.name() 30 | assert.equal(name,_name); 31 | }); 32 | 33 | it('has a symbol', async () => { 34 | const symbol = await fcti.symbol() 35 | assert.equal(symbol,_symbol); 36 | }); 37 | 38 | it('has 18 decimals', async () => { 39 | const decimals = await fcti.decimals() 40 | assert.equal(decimals,_decimals); 41 | }); 42 | 43 | it('should same total supply', async() =>{ 44 | const balance = await fcti.balanceOf(owner); 45 | const totalSupply = await fcti.totalSupply(); 46 | 47 | balance.should.be.bignumber.equal(totalSupply) 48 | }) 49 | 50 | it('could add sub owner', async () => { 51 | await fcti.addOwner(subOwner, { from: owner }); 52 | const value = await fcti.isOwner.call(subOwner) 53 | value.should.be.a('boolean').equal(true); 54 | 55 | }); 56 | 57 | it('transfer unlock', async () => { 58 | await fcti.unlock() 59 | }); 60 | 61 | it('shloud transfer correctly', async () => { 62 | const tx = await fcti.transfer(guest, amount) 63 | const balance = await fcti.balanceOf(guest); 64 | 65 | balance.should.be.bignumber.equal(new BigNumber(amount)) 66 | }); 67 | 68 | it('shloud reject tranfer when lack of balance', async () => { 69 | const balance = await fcti.balanceOf(guest); 70 | await fcti.transfer(owner, balance + amount, {from:guest}).should.be.rejected 71 | }); 72 | 73 | it('transfer lock', async () => { 74 | await fcti.lock() 75 | }); 76 | 77 | it('should reject to transfer', async () => { 78 | const beforeBalance = await fcti.balanceOf(guest); 79 | await fcti.transfer(owner, amount, {from:guest}).should.be.rejected; 80 | const afterBalance = await fcti.balanceOf(guest); 81 | 82 | beforeBalance.should.be.bignumber.equal(afterBalance) 83 | }); 84 | 85 | // Mint 86 | it("should mint correctly", async () => { 87 | const beforeBalance = await fcti.balanceOf(subOwner); 88 | const beforeTotalSupply = await fcti.totalSupply(); 89 | 90 | await fcti.mint(subOwner, util.wei(amount), { from: subOwner }); 91 | const afterBalance = await fcti.balanceOf(subOwner); 92 | const afterTotalSupply = await fcti.totalSupply(); 93 | 94 | 95 | const balanceGap = afterBalance.minus(beforeBalance); 96 | balanceGap.should.be.bignumber.equal(util.wei(amount), 'there are some problems in minting process'); 97 | const supplyGap = new BigNumber(afterTotalSupply.minus(beforeTotalSupply)); 98 | supplyGap.should.be.bignumber.equal(util.wei(amount), 'there are some problems in minting process'); 99 | }); 100 | 101 | // Burn 102 | it("should burn correctly", async () => { 103 | const beforeBalance = await fcti.balanceOf(subOwner); 104 | const beforeTotalSupply = await fcti.totalSupply(); 105 | 106 | await fcti.burn(util.wei(amount), { from: subOwner }); 107 | 108 | const afterBalance = await fcti.balanceOf(subOwner); 109 | const afterTotalSupply = await fcti.totalSupply(); 110 | 111 | const balanceGap = beforeBalance.minus(afterBalance); 112 | balanceGap.should.be.bignumber.equal(util.wei(amount), 'there are some problems in burning process'); 113 | 114 | const supplyGap = beforeTotalSupply.minus(afterTotalSupply); 115 | supplyGap.should.be.bignumber.equal(util.wei(amount), 'there are some problems in burning process'); 116 | }); 117 | 118 | 119 | it('should remove sub-owner', async () => { 120 | await fcti.removeOwner(subOwner, { from: subOwner }); 121 | const value = await fcti.isOwner.call(subOwner) 122 | value.should.be.a('boolean').equal(false); 123 | }); 124 | 125 | it('should be reject remove real-owner', async () => { 126 | await fcti.removeOwner(owner,{ from: owner }).should.be.rejected; 127 | }); 128 | 129 | }); 130 | -------------------------------------------------------------------------------- /test/FCTV.js: -------------------------------------------------------------------------------- 1 | const util = require("./test-util") 2 | const FCTV = artifacts.require('FCTV'); 3 | const BigNumber = web3.BigNumber 4 | const L = require('mocha-logger') 5 | 6 | require('chai') 7 | .use(require('chai-bignumber')(BigNumber)) 8 | .use(require('chai-as-promised')) 9 | .should(); 10 | 11 | contract('[FCT] FirmaChain Token', function(accounts) { 12 | 13 | let fcti; 14 | const owner = accounts[0] 15 | const subOwner = accounts[1] 16 | const guest = accounts[2] 17 | 18 | const amount = 1; 19 | 20 | const _name = '[FCT] FirmaChain Token'; 21 | const _symbol = 'FCT'; 22 | const _decimals = 18; 23 | 24 | before(async () => { 25 | fcti = await FCTV.deployed(); 26 | }) 27 | 28 | it('has a name', async () => { 29 | const name = await fcti.name() 30 | assert.equal(name,_name); 31 | }); 32 | 33 | it('has a symbol', async () => { 34 | const symbol = await fcti.symbol() 35 | assert.equal(symbol,_symbol); 36 | }); 37 | 38 | it('has 18 decimals', async () => { 39 | const decimals = await fcti.decimals() 40 | assert.equal(decimals,_decimals); 41 | }); 42 | 43 | it('should same total supply', async() =>{ 44 | const balance = await fcti.balanceOf(owner); 45 | const totalSupply = await fcti.totalSupply(); 46 | 47 | balance.should.be.bignumber.equal(totalSupply) 48 | }) 49 | 50 | it('could add sub owner', async () => { 51 | await fcti.addOwner(subOwner, { from: owner }); 52 | const value = await fcti.isOwner.call(subOwner) 53 | value.should.be.a('boolean').equal(true); 54 | 55 | }); 56 | 57 | it('transfer unlock', async () => { 58 | await fcti.unlock() 59 | }); 60 | 61 | it('shloud transfer correctly', async () => { 62 | const tx = await fcti.transfer(guest, amount) 63 | const balance = await fcti.balanceOf(guest); 64 | 65 | balance.should.be.bignumber.equal(new BigNumber(amount)) 66 | }); 67 | 68 | it('shloud reject tranfer when lack of balance', async () => { 69 | const balance = await fcti.balanceOf(guest); 70 | await fcti.transfer(owner, balance + amount, {from:guest}).should.be.rejected 71 | }); 72 | 73 | it('transfer lock', async () => { 74 | await fcti.lock() 75 | }); 76 | 77 | it('should reject to transfer', async () => { 78 | const beforeBalance = await fcti.balanceOf(guest); 79 | await fcti.transfer(owner, amount, {from:guest}).should.be.rejected; 80 | const afterBalance = await fcti.balanceOf(guest); 81 | 82 | beforeBalance.should.be.bignumber.equal(afterBalance) 83 | }); 84 | 85 | // Mint 86 | it("should mint correctly", async () => { 87 | const beforeBalance = await fcti.balanceOf(subOwner); 88 | const beforeTotalSupply = await fcti.totalSupply(); 89 | 90 | await fcti.mint(subOwner, util.wei(amount), { from: subOwner }); 91 | const afterBalance = await fcti.balanceOf(subOwner); 92 | const afterTotalSupply = await fcti.totalSupply(); 93 | 94 | 95 | const balanceGap = afterBalance.minus(beforeBalance); 96 | balanceGap.should.be.bignumber.equal(util.wei(amount), 'there are some problems in minting process'); 97 | const supplyGap = new BigNumber(afterTotalSupply.minus(beforeTotalSupply)); 98 | supplyGap.should.be.bignumber.equal(util.wei(amount), 'there are some problems in minting process'); 99 | }); 100 | 101 | // Burn 102 | it("should burn correctly", async () => { 103 | const beforeBalance = await fcti.balanceOf(subOwner); 104 | const beforeTotalSupply = await fcti.totalSupply(); 105 | 106 | await fcti.burn(util.wei(amount), { from: subOwner }); 107 | 108 | const afterBalance = await fcti.balanceOf(subOwner); 109 | const afterTotalSupply = await fcti.totalSupply(); 110 | 111 | const balanceGap = beforeBalance.minus(afterBalance); 112 | balanceGap.should.be.bignumber.equal(util.wei(amount), 'there are some problems in burning process'); 113 | 114 | const supplyGap = beforeTotalSupply.minus(afterTotalSupply); 115 | supplyGap.should.be.bignumber.equal(util.wei(amount), 'there are some problems in burning process'); 116 | }); 117 | 118 | 119 | it('should remove sub-owner', async () => { 120 | await fcti.removeOwner(subOwner, { from: subOwner }); 121 | const value = await fcti.isOwner.call(subOwner) 122 | value.should.be.a('boolean').equal(false); 123 | }); 124 | 125 | it('should be reject remove real-owner', async () => { 126 | await fcti.removeOwner(owner,{ from: owner }).should.be.rejected; 127 | }); 128 | 129 | }); 130 | -------------------------------------------------------------------------------- /test/Poc.js: -------------------------------------------------------------------------------- 1 | const util = require("./test-util"); 2 | const Poc = artifacts.require('Poc'); 3 | const BigNumber = web3.BigNumber; 4 | const L = require('mocha-logger'); 5 | 6 | require('chai') 7 | .use(require('chai-bignumber')(BigNumber)) 8 | .use(require('chai-as-promised')) 9 | .should(); 10 | 11 | contract('Proof of Concept', function(accounts) { 12 | let poc; 13 | let partiesOfContractBefore; 14 | let partiesOfContractAfter; 15 | 16 | const owner = accounts[0]; 17 | const parties = accounts.slice(0, 2); 18 | const parties3 = accounts.slice(0, 3); 19 | 20 | const contractId0 = new web3.BigNumber("0xa7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8ffff"); 21 | const contractId = new web3.BigNumber("0xa7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"); 22 | const contractId3 = new web3.BigNumber("0xa7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f80002"); 23 | const contractIdRoot = new web3.BigNumber("0xa7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f81100"); 24 | const contractIdSub = new web3.BigNumber("0xa7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f81101"); 25 | const contractSign = new web3.BigNumber("0xa7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f80000"); 26 | const contractSignWrong = new web3.BigNumber("0xa7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f80001"); 27 | 28 | before(async () => { 29 | poc = await Poc.new({from: owner}); 30 | }); 31 | 32 | describe('Expiration test', () => { 33 | it('Create New Contract with short lifetime', async () => { 34 | await poc.newContract(contractId0, parties, 1).should.be.fulfilled; 35 | }); 36 | }); 37 | 38 | describe('Creation condition test', () => { 39 | it('Reject contract in a person', async () => { 40 | await poc.newContract(contractId, [owner], 100).should.be.rejected; 41 | }); 42 | 43 | it('Reject contract whose lifetime is zero', async () => { 44 | await poc.newContract(contractId, [owner], 0).should.be.rejected; 45 | }); 46 | 47 | it('Reject contract with duplicated parties', async () => { 48 | await poc.newContract(contractId, [owner, owner], 0).should.be.rejected; 49 | await poc.newContract(contractId, [parties[0], parties[1], parties[0]], 0).should.be.rejected; 50 | }); 51 | }); 52 | 53 | describe('Sign & extra data test', () => { 54 | it('Create New Contract among two', async () => { 55 | await poc.newContract(contractId, parties, 100).should.be.fulfilled; 56 | }); 57 | 58 | it('Reject duplicated signs', async () => { 59 | await poc.signContract(contractId, contractSign, "extraData", {from: parties[0]}).should.be.fulfilled; 60 | await poc.signContract(contractId, contractSign, "extraData", {from: parties[0]}).should.be.rejected; 61 | }); 62 | 63 | it('Reject sign by person not in party', async () => { 64 | await poc.signContract(contractId, contractSign, "extraData", {from: parties3[2]}).should.be.rejected; 65 | }); 66 | 67 | it('Complete contract two sign', async () => { 68 | let resultBefore = await poc.getStatus(contractId); 69 | resultBefore.should.be.bignumber.equal(0); 70 | await poc.signContract(contractId, contractSign, "extraData", {from: parties[1]}).should.be.fulfilled; 71 | let resultAfter = await poc.getStatus(contractId); 72 | resultAfter.should.be.bignumber.equal(1); 73 | }); 74 | 75 | it('Get extra data', async () => { 76 | let extraData = await poc.extraData(contractId, 1); 77 | extraData.should.equal("extraData"); 78 | }); 79 | }); 80 | 81 | describe('Sign integration test', () => { 82 | it('Fail to accept contract with different sign', async () => { 83 | await poc.newContract(contractId3, parties3, 100).should.be.fulfilled; 84 | partiesOfContractBefore = await poc.getParties(contractId3); 85 | await poc.signContract(contractId3, contractSign, "extraData", {from: parties3[0]}).should.be.fulfilled; 86 | await poc.signContract(contractId3, contractSignWrong, "extraData", {from: parties3[1]}).should.be.fulfilled; 87 | await poc.signContract(contractId3, contractSign, "extraData", {from: parties3[2]}).should.be.fulfilled; 88 | let result = await poc.getStatus(contractId3); 89 | result.should.be.bignumber.equal(-1); 90 | }); 91 | }); 92 | 93 | describe('Contract overwrite test', () => { 94 | it('Reject contract with duplicated approved contract id', async () => { 95 | await poc.newContract(contractId, parties, 100).should.be.rejected; 96 | }); 97 | 98 | it('Accept overwrited contract', async () => { 99 | await poc.newContract(contractId3, parties, 100).should.be.fulfilled; 100 | }); 101 | 102 | it('Accept overwrited contract whose lifetime is over', async () => { 103 | await poc.newContract(contractId0, parties, 1).should.be.fulfilled; 104 | }); 105 | 106 | it('Check the removal of old data', async () => { 107 | let partiesOfContractAfter = await poc.getParties(contractId3); 108 | JSON.stringify(partiesOfContractBefore).should.be.not.equal(JSON.stringify(partiesOfContractAfter)); 109 | }); 110 | }); 111 | 112 | describe('Subcontract test', () => { 113 | it('Make new contract', async () => { 114 | await poc.newContract(contractIdRoot, parties, 100).should.be.fulfilled; 115 | await poc.newSubContract(contractIdSub, contractIdRoot, 100).should.be.fulfilled; 116 | let partiesOfContractRoot = await poc.getParties(contractIdRoot); 117 | let partiesOfContractSub = await poc.getParties(contractIdSub); 118 | JSON.stringify(parties).should.be.equal(JSON.stringify(partiesOfContractRoot)); 119 | JSON.stringify(partiesOfContractRoot).should.be.equal(JSON.stringify(partiesOfContractSub)); 120 | }); 121 | 122 | it('Check contract tree', async () => { 123 | let expectedHistory = [contractIdRoot, contractIdSub]; 124 | let actualHistory = await poc.getContractHistory(contractIdSub); 125 | JSON.stringify(actualHistory).should.be.equal(JSON.stringify(expectedHistory)); 126 | }); 127 | }); 128 | 129 | }); 130 | 131 | -------------------------------------------------------------------------------- /test/PreSaleI.js: -------------------------------------------------------------------------------- 1 | const util = require("./test-util"); 2 | const FCT = artifacts.require('FCT'); 3 | const PreSaleI = artifacts.require('PreSaleI'); 4 | const BigNumber = web3.BigNumber; 5 | const L = require('mocha-logger'); 6 | 7 | require('chai') 8 | .use(require('chai-bignumber')(BigNumber)) 9 | .use(require('chai-as-promised')) 10 | .should(); 11 | 12 | contract('Pre Sale I', function(accounts) { 13 | const owner = accounts[0]; 14 | const whitelisteds = accounts.slice(1, 6); 15 | const nonWhitelisted = accounts[7]; 16 | 17 | const wallet = owner; 18 | const exchangeRate = 20000 + 4000; 19 | const minValue = util.wei(0.005); 20 | const maxTotal = util.wei(1); 21 | const maxTotalPerAddress = util.wei(0.5); 22 | 23 | const testUnitAmount = 0.1; 24 | const underMinValue = 0.001; 25 | const epsilon = 0.1; // NOTE: regarded as transaction fee 26 | 27 | let token; 28 | let sale; 29 | 30 | before(async() => { 31 | token = await FCT.new({from: owner}); 32 | await util.sendTransaction({from: owner, to: whitelisteds[0], value: maxTotal}); 33 | await util.sendTransaction({from: owner, to: whitelisteds[1], value: maxTotal}); 34 | await util.sendTransaction({from: owner, to: whitelisteds[2], value: maxTotal}); 35 | await util.sendTransaction({from: owner, to: whitelisteds[3], value: maxTotalPerAddress}); 36 | await util.sendTransaction({from: owner, to: nonWhitelisted, value: util.wei(testUnitAmount)}); 37 | }); 38 | 39 | describe('Check block number', async() => { 40 | let startBlockNumber; 41 | let endBlockNumber; 42 | let sale; 43 | 44 | before(async() => { 45 | startBlockNumber = await util.getBlockNumber() + 10; 46 | endBlockNumber = startBlockNumber + 10; 47 | sale = await PreSaleI.new(token.address, wallet, exchangeRate, minValue, maxTotal, maxTotalPerAddress, startBlockNumber, endBlockNumber, {from: owner}); 48 | await sale.addAddressToWhitelist(whitelisteds[0], {from: owner}); 49 | }); 50 | 51 | it('Should reject before start block number', async() => { 52 | await sale.toggleEnabled({from: owner}); 53 | await sale.sendTransaction({from: whitelisteds[0], value: util.wei(testUnitAmount)}).should.be.rejected; 54 | let nowBlockNumber = await util.getBlockNumber(); 55 | if (nowBlockNumber >= startBlockNumber) { 56 | console.log("Possibly incomplete test"); 57 | } 58 | }); 59 | 60 | it('Should accept during block number range', async() => { 61 | let nowBlockNumber = await util.getBlockNumber(); 62 | while(nowBlockNumber < startBlockNumber) { 63 | //util.sendTransaction({from: owner, to: whitelisteds[0], value: 0}); // Do the meaningless in order to mine blocks during test 64 | nowBlockNumber = await util.getBlockNumber(); 65 | } 66 | await sale.sendTransaction({from: whitelisteds[0], value: util.wei(testUnitAmount)}).should.be.fulfilled; 67 | }); 68 | 69 | it('Should reject after end block number', async() => { 70 | let nowBlockNumber = await util.getBlockNumber(); 71 | while(nowBlockNumber < endBlockNumber) { 72 | //util.sendTransaction({from: owner, to: whitelisteds[0], value: 0}); // Do the meaningless in order to mine blocks during test 73 | nowBlockNumber = await util.getBlockNumber(); 74 | } 75 | await sale.sendTransaction({from: whitelisteds[0], value: util.wei(testUnitAmount)}).should.be.rejected; 76 | }); 77 | }); 78 | 79 | describe('Check functionality', async() => { 80 | let startBlockNumber; 81 | let endBlockNumber; 82 | let sale; 83 | 84 | before(async() => { 85 | startBlockNumber = await util.getBlockNumber(); 86 | endBlockNumber = startBlockNumber + 50; 87 | sale = await PreSaleI.new(token.address, wallet, exchangeRate, minValue, maxTotal, maxTotalPerAddress, startBlockNumber, endBlockNumber, {from: owner}); 88 | await token.addOwner(sale.address, {from: owner}).should.be.fulfilled; 89 | await token.transfer(sale.address, maxTotal * exchangeRate, {from: owner}); 90 | await sale.addAddressesToWhitelist(whitelisteds, {from: owner}); 91 | }); 92 | 93 | describe('During sale', async() => { 94 | it('Should reject during not enabled', async() => { 95 | await sale.sendTransaction({from: whitelisteds[1], value: util.wei(testUnitAmount)}).should.be.rejected; 96 | await sale.toggleEnabled({from: owner}); 97 | await sale.sendTransaction({from: whitelisteds[1], value: util.wei(testUnitAmount)}).should.be.fulfilled; 98 | }); 99 | 100 | it('Should reject non white list user', async() => { 101 | await sale.sendTransaction({from: nonWhitelisted, value: util.wei(testUnitAmount)}).should.be.rejected; 102 | }); 103 | 104 | it('Should reject value below minumum limit', async() => { 105 | await sale.sendTransaction({from: whitelisteds[1], value: util.wei(underMinValue)}).should.be.rejected; 106 | }); 107 | 108 | it('Check refund and limit per address', async() => { 109 | let beforeEther = await util.getBalance(whitelisteds[0]); 110 | await sale.sendTransaction({from: whitelisteds[0], value: maxTotalPerAddress}); 111 | let afterEther = await util.getBalance(whitelisteds[0]); 112 | beforeEther.minus(afterEther).should.be.bignumber.below(maxTotalPerAddress - util.wei(testUnitAmount) + util.wei(epsilon)); 113 | await sale.sendTransaction({from: whitelisteds[0], value: minValue}).should.be.rejected; 114 | }); 115 | 116 | it('Check total limit', async() => { 117 | await sale.sendTransaction({from: whitelisteds[2], value: maxTotalPerAddress}).should.be.fulfilled; 118 | await sale.sendTransaction({from: whitelisteds[2], value: maxTotalPerAddress}).should.be.rejected; 119 | await sale.sendTransaction({from: whitelisteds[3], value: maxTotalPerAddress}).should.be.rejected; 120 | }); 121 | }); 122 | 123 | describe('After sale', async() => { 124 | it('Wait until end block number', async() => { 125 | let nowBlockNumber = await util.getBlockNumber(); 126 | console.log("Check block number", nowBlockNumber, endBlockNumber); 127 | while(nowBlockNumber <= endBlockNumber) { 128 | //util.sendTransaction({from: owner, to: whitelisteds[0], value: 0}); // Do the meaningless in order to mine blocks during test 129 | nowBlockNumber = await util.getBlockNumber(); 130 | } 131 | }); 132 | 133 | it('Reject refund and deliver before not enabled', async() => { 134 | await sale.refund(whitelisteds[1], {from: owner}).should.be.rejected; 135 | await sale.deliver(whitelisteds[1], {from: owner}).should.be.rejected; 136 | }); 137 | 138 | it('Accept refund after enabled', async() => { 139 | await sale.toggleEnabled({from: owner}); 140 | let beforeEther = await util.getBalance(whitelisteds[1]); 141 | let amount = await sale.buyAmounts(whitelisteds[1]); 142 | await sale.refund(whitelisteds[1], {from: owner}).should.be.fulfilled; 143 | let afterEther = await util.getBalance(whitelisteds[1]); 144 | afterEther.minus(beforeEther).should.be.bignumber.equal(amount); 145 | }); 146 | 147 | it('Reject terminate before delivery is done', async() => { 148 | await sale.terminate({from: owner}).should.be.rejected; 149 | }); 150 | 151 | it('Check to deliver the exact amount', async() => { 152 | let beforeAmount = await token.balanceOf(whitelisteds[0]); 153 | let deliverAmount = await sale.buyAmounts(whitelisteds[0]) * exchangeRate; 154 | await sale.deliver(whitelisteds[0], {from: owner}).should.be.fulfilled; 155 | let afterAmount = await token.balanceOf(whitelisteds[0]); 156 | afterAmount.minus(beforeAmount).should.be.bignumber.equal(deliverAmount); 157 | }); 158 | 159 | it('Reject to deliver twice', async() => { 160 | await sale.deliver(whitelisteds[0], {from: owner}).should.be.rejected; 161 | await sale.deliver(whitelisteds[1], {from: owner}).should.be.rejected; 162 | await sale.deliver(whitelisteds[2], {from: owner}).should.be.fulfilled; 163 | }); 164 | 165 | it('Terminate', async() => { 166 | let saleEther = await util.getBalance(sale.address); 167 | let saleToken = await token.balanceOf(sale.address); 168 | let beforeEther = await util.getBalance(wallet); 169 | let beforeToken = await token.balanceOf(wallet); 170 | await sale.terminate({from: owner}).should.be.fulfilled; 171 | let afterEther = await util.getBalance(wallet); 172 | let afterToken = await token.balanceOf(wallet); 173 | saleEther.minus(afterEther.minus(beforeEther)).should.be.bignumber.below(util.wei(epsilon)); 174 | afterToken.minus(beforeToken).should.be.bignumber.equal(saleToken); 175 | }); 176 | }); 177 | }); 178 | }); 179 | 180 | -------------------------------------------------------------------------------- /test/TokenLock.js: -------------------------------------------------------------------------------- 1 | const util = require("./test-util"); 2 | const FCT = artifacts.require('FCT'); 3 | const TokenLock = artifacts.require('TokenLock'); 4 | const TokenLockDistribute = artifacts.require('TokenLockDistribute'); 5 | const BigNumber = web3.BigNumber; 6 | const L = require('mocha-logger'); 7 | 8 | require('chai') 9 | .use(require('chai-bignumber')(BigNumber)) 10 | .use(require('chai-as-promised')) 11 | .should(); 12 | 13 | contract('Token Distribute And Lock', function(accounts) { 14 | let token; 15 | let lock; 16 | let distribute; 17 | const owner = accounts[0]; 18 | const user = accounts[1]; 19 | const user2 = accounts[2]; 20 | before(async () => { 21 | token = await FCT.deployed(); 22 | lock = await TokenLock.deployed(); 23 | distribute = await TokenLockDistribute.deployed(); 24 | }); 25 | 26 | it('Distribute contract is added as owner in lock contract', async() => { 27 | await lock.addOwner(distribute.address, {from: owner}).should.be.fulfilled; 28 | }); 29 | 30 | it('Lock contract is added as owner in token contract', async() => { 31 | await token.addOwner(lock.address, {from: owner}).should.be.fulfilled; 32 | }); 33 | 34 | it('Distribute contract is added as owner in token contract', async() => { 35 | await token.addOwner(distribute.address, {from: owner}).should.be.fulfilled; 36 | }); 37 | 38 | let releaseTimestamp1; 39 | let beforeBalance1; 40 | const totalAmount = 1000; 41 | const lockedAmount = 100; 42 | const unlockedAmount = totalAmount - lockedAmount; 43 | it('Owner distributes FCT with locked amount to user', async() => { 44 | beforeBalance1 = await token.balanceOf(user); 45 | await token.transfer(distribute.address, totalAmount, {from: owner}).should.be.fulfilled; 46 | 47 | releaseTimestamp1 = Math.floor(Date.now() / 1000) + 180; 48 | await distribute.distribute(user, unlockedAmount, lockedAmount, releaseTimestamp1, {from: owner}).should.be.fulfilled; 49 | const afterBalance = await token.balanceOf(user); 50 | afterBalance.minus(beforeBalance1).should.be.bignumber.equal(unlockedAmount); 51 | 52 | await lock.release(user, {from: owner}).should.be.rejected; 53 | }); 54 | 55 | let releaseTimestamp2; 56 | let beforeBalance2; 57 | it('Owner distributes FCT with locked amount to the another user', async() => { 58 | beforeBalance2 = await token.balanceOf(user2); 59 | await token.transfer(distribute.address, totalAmount, {from: owner}).should.be.fulfilled; 60 | 61 | releaseTimestamp2 = Math.floor(Date.now() / 1000); 62 | await distribute.distribute(user2, unlockedAmount, lockedAmount, releaseTimestamp2, {from: owner}).should.be.fulfilled; 63 | const afterBalance = await token.balanceOf(user2); 64 | afterBalance.minus(beforeBalance2).should.be.bignumber.equal(unlockedAmount); 65 | }); 66 | 67 | it('Owner waits block to release', async() => { 68 | // NOTE: Wait blocks for network 69 | let nowBlock = await util.getBlock("latest"); 70 | while(nowBlock.timestamp < releaseTimestamp1) { 71 | nowBlock = await util.getBlock("latest"); 72 | } 73 | await lock.release(user, {from: owner}).should.be.fulfilled; 74 | const finalBalance = await token.balanceOf(user); 75 | finalBalance.minus(beforeBalance1).should.be.bignumber.equal(totalAmount); 76 | }); 77 | 78 | it('User can claim to release his own', async() => { 79 | await lock.release(user2, {from: user2}).should.be.fulfilled; 80 | const finalBalance = await token.balanceOf(user2); 81 | finalBalance.minus(beforeBalance2).should.be.bignumber.equal(totalAmount); 82 | }); 83 | }); 84 | 85 | -------------------------------------------------------------------------------- /test/test-util.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | wei:function(n) { 3 | return new web3.BigNumber(String(web3.toWei(n, 'ether'))); 4 | }, 5 | getBlock: function(value) { 6 | return new Promise((resolve, reject) => { 7 | web3.eth.getBlock(value, (e, r) => e ? reject(e) : resolve(r)) 8 | }); 9 | }, 10 | getBlockNumber: function() { 11 | return new Promise((resolve, reject) => { 12 | web3.eth.getBlockNumber((e, r) => e ? reject(e) : resolve(r)) 13 | }); 14 | }, 15 | sendTransaction: function(value) { 16 | return new Promise((resolve, reject) => { 17 | web3.eth.sendTransaction(value, (e, r) => e ? reject(e) : resolve(r)) 18 | }); 19 | }, 20 | getBalance: function(value) { 21 | return new Promise((resolve, reject) => { 22 | web3.eth.getBalance(value, (e, r) => e ? reject(e) : resolve(r)) 23 | }); 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /truffle.example.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | ganache: { 4 | host: "127.0.0.1", 5 | port: 7545, 6 | network_id: "*" // Match any network id 7 | }, 8 | }, 9 | mocha: { 10 | }, 11 | solc: { 12 | optimizer: { 13 | enabled: true, 14 | runs: 200 15 | } 16 | } 17 | }; 18 | --------------------------------------------------------------------------------