├── .gitignore ├── LICENSE ├── README.md ├── contracts ├── Crowdsale.sol ├── ERC20.sol ├── Migrations.sol ├── Ownable.sol ├── Pausable.sol ├── PullPayment.sol ├── RLC.sol ├── SafeMath.sol └── TokenSpender.sol ├── documentation ├── Crowdfundingv2.1.pdf ├── iEx.ecTermsandConditionsENGLISHVERSION.pdf └── review-openzeppelin ├── migrations ├── 1_initial_migration.js └── 2_deploy_contracts.js ├── package.json ├── testrpc.sh └── truffle.js /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 iEx.ec 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rlc-token 2 | RLC Token for the iEx.ec project 3 | 4 | Thanks to Beyond the Void, for helping us shaping the crowdsale 5 | contract, Open Zeppelin and SmartPool for the security audit and 6 | François Branciard for the testing. 7 | 8 | The RLC token is deployed at 0x607F4C5BB672230e8672085532f7e901544a7375 9 | 10 | The Crowdsale contract is deployed at 0xec33fB8D7c781F6FeaF2Dd46D521D4F292320200 11 | 12 | 13 | ## To test 14 | This is a truffle 3 repo 15 | Launch `testrpc` on another terminal 16 | Launch `truffle test` 17 | 18 | 19 | ## Deployment 20 | Launch migrations script `truffle migrate` 21 | and the run this on truffle console: 22 | RLC.at(RLC.address).transfer(Crowdsale.address,87000000000000000) 23 | RLC.at(RLC.address).transferOwnership(Crowdsale.address) 24 | 25 | -------------------------------------------------------------------------------- /contracts/Crowdsale.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | import "./SafeMath.sol"; 3 | import "./RLC.sol"; 4 | import "./PullPayment.sol"; 5 | import "./Pausable.sol"; 6 | 7 | /* 8 | Crowdsale Smart Contract for the iEx.ec project 9 | 10 | This smart contract collects ETH and BTC, and in return emits RLC tokens to the backers 11 | 12 | Thanks to BeyondTheVoid and TokenMarket who helped us shaping this code. 13 | 14 | */ 15 | 16 | contract Crowdsale is SafeMath, PullPayment, Pausable { 17 | 18 | struct Backer { 19 | uint weiReceived; // Amount of ETH given 20 | string btc_address; //store the btc address for full traceability 21 | uint satoshiReceived; // Amount of BTC given 22 | uint rlcSent; 23 | } 24 | 25 | RLC public rlc; // RLC contract reference 26 | address public owner; // Contract owner (iEx.ec team) 27 | address public multisigETH; // Multisig contract that will receive the ETH 28 | address public BTCproxy; // address of the BTC Proxy 29 | 30 | uint public RLCPerETH; // Number of RLC per ETH 31 | uint public RLCPerSATOSHI; // Number of RLC per SATOSHI 32 | uint public ETHReceived; // Number of ETH received 33 | uint public BTCReceived; // Number of BTC received 34 | uint public RLCSentToETH; // Number of RLC sent to ETH contributors 35 | uint public RLCSentToBTC; // Number of RLC sent to BTC contributors 36 | uint public startBlock; // Crowdsale start block 37 | uint public endBlock; // Crowdsale end block 38 | uint public minCap; // Minimum number of RLC to sell 39 | uint public maxCap; // Maximum number of RLC to sell 40 | bool public maxCapReached; // Max cap has been reached 41 | uint public minInvestETH; // Minimum amount to invest 42 | uint public minInvestBTC; // Minimum amount to invest 43 | bool public crowdsaleClosed;// Is crowdsale still on going 44 | 45 | address public bounty; // address at which the bounty RLC will be sent 46 | address public reserve; // address at which the contingency reserve will be sent 47 | address public team; // address at which the team RLC will be sent 48 | 49 | uint public rlc_bounty; // amount of bounties RLC 50 | uint public rlc_reserve; // amount of the contingency reserve 51 | uint public rlc_team; // amount of the team RLC 52 | mapping(address => Backer) public backers; //backersETH indexed by their ETH address 53 | 54 | modifier onlyBy(address a){ 55 | if (msg.sender != a) throw; 56 | _; 57 | } 58 | 59 | modifier minCapNotReached() { 60 | if ((now= minCap ) throw; 61 | _; 62 | } 63 | 64 | modifier respectTimeFrame() { 65 | if ((now < startBlock) || (now > endBlock )) throw; 66 | _; 67 | } 68 | 69 | /* 70 | * Event 71 | */ 72 | event ReceivedETH(address addr, uint value); 73 | event ReceivedBTC(address addr, string from, uint value, string txid); 74 | event RefundBTC(string to, uint value); 75 | event Logs(address indexed from, uint amount, string value); 76 | 77 | /* 78 | * Constructor 79 | */ 80 | //function Crowdsale() { 81 | function Crowdsale() { 82 | owner = msg.sender; 83 | BTCproxy = 0x75c6cceb1a33f177369053f8a0e840de96b4ed0e; 84 | rlc = RLC(0x607F4C5BB672230e8672085532f7e901544a7375); 85 | multisigETH = 0xAe307e3871E5A321c0559FBf0233A38c937B826A; 86 | team = 0xd65380D773208a6Aa49472Bf55186b855B393298; 87 | reserve = 0x24F6b37770C6067D05ACc2aD2C42d1Bafde95d48; 88 | bounty = 0x8226a24dA0870Fb8A128E4Fc15228a9c4a5baC29; 89 | RLCSentToETH = 0; 90 | RLCSentToBTC = 0; 91 | minInvestETH = 1 ether; 92 | minInvestBTC = 5000000; // approx 50 USD or 0.05000000 BTC 93 | startBlock = 0 ; // should wait for the call of the function start 94 | endBlock = 0; // should wait for the call of the function start 95 | RLCPerETH = 200000000000; // will be update every 10min based on the kraken ETHBTC 96 | RLCPerSATOSHI = 50000; // 5000 RLC par BTC == 50,000 RLC per satoshi 97 | minCap=12000000000000000; 98 | maxCap=60000000000000000; 99 | rlc_bounty=1700000000000000; // max 6000000 RLC 100 | rlc_reserve=1700000000000000; // max 6000000 RLC 101 | rlc_team=12000000000000000; 102 | } 103 | 104 | /* 105 | * The fallback function corresponds to a donation in ETH 106 | */ 107 | function() payable { 108 | if (now > endBlock) throw; 109 | receiveETH(msg.sender); 110 | } 111 | 112 | /* 113 | * To call to start the crowdsale 114 | */ 115 | function start() onlyBy(owner) { 116 | startBlock = now ; 117 | endBlock = now + 30 days; 118 | } 119 | 120 | /* 121 | * Receives a donation in ETH 122 | */ 123 | function receiveETH(address beneficiary) internal stopInEmergency respectTimeFrame { 124 | if (msg.value < minInvestETH) throw; //don't accept funding under a predefined threshold 125 | uint rlcToSend = bonus(safeMul(msg.value,RLCPerETH)/(1 ether)); //compute the number of RLC to send 126 | if (safeAdd(rlcToSend, safeAdd(RLCSentToETH, RLCSentToBTC)) > maxCap) throw; 127 | 128 | Backer backer = backers[beneficiary]; 129 | if (!rlc.transfer(beneficiary, rlcToSend)) throw; // Do the RLC transfer right now 130 | backer.rlcSent = safeAdd(backer.rlcSent, rlcToSend); 131 | backer.weiReceived = safeAdd(backer.weiReceived, msg.value); // Update the total wei collected during the crowdfunding for this backer 132 | ETHReceived = safeAdd(ETHReceived, msg.value); // Update the total wei collected during the crowdfunding 133 | RLCSentToETH = safeAdd(RLCSentToETH, rlcToSend); 134 | 135 | emitRLC(rlcToSend); // compute the variable part 136 | ReceivedETH(beneficiary,ETHReceived); // send the corresponding contribution event 137 | } 138 | 139 | /* 140 | * receives a donation in BTC 141 | */ 142 | function receiveBTC(address beneficiary, string btc_address, uint value, string txid) stopInEmergency respectTimeFrame onlyBy(BTCproxy) returns (bool res){ 143 | if (value < minInvestBTC) throw; // this verif is also made on the btcproxy 144 | 145 | uint rlcToSend = bonus(safeMul(value,RLCPerSATOSHI)); //compute the number of RLC to send 146 | if (safeAdd(rlcToSend, safeAdd(RLCSentToETH, RLCSentToBTC)) > maxCap) { // check if we are not reaching the maxCap by accepting this donation 147 | RefundBTC(btc_address , value); 148 | return false; 149 | } 150 | 151 | Backer backer = backers[beneficiary]; 152 | if (!rlc.transfer(beneficiary, rlcToSend)) throw; // Do the transfer right now 153 | backer.rlcSent = safeAdd(backer.rlcSent , rlcToSend); 154 | backer.btc_address = btc_address; 155 | backer.satoshiReceived = safeAdd(backer.satoshiReceived, value); 156 | BTCReceived = safeAdd(BTCReceived, value); // Update the total satoshi collected during the crowdfunding for this backer 157 | RLCSentToBTC = safeAdd(RLCSentToBTC, rlcToSend); // Update the total satoshi collected during the crowdfunding 158 | emitRLC(rlcToSend); 159 | ReceivedBTC(beneficiary, btc_address, BTCReceived, txid); 160 | return true; 161 | } 162 | 163 | /* 164 | *Compute the variable part 165 | */ 166 | function emitRLC(uint amount) internal { 167 | rlc_bounty = safeAdd(rlc_bounty, amount/10); 168 | rlc_team = safeAdd(rlc_team, amount/20); 169 | rlc_reserve = safeAdd(rlc_reserve, amount/10); 170 | Logs(msg.sender ,amount, "emitRLC"); 171 | } 172 | 173 | /* 174 | *Compute the RLC bonus according to the investment period 175 | */ 176 | function bonus(uint amount) internal constant returns (uint) { 177 | if (now < safeAdd(startBlock, 10 days)) return (safeAdd(amount, amount/5)); // bonus 20% 178 | if (now < safeAdd(startBlock, 20 days)) return (safeAdd(amount, amount/10)); // bonus 10% 179 | return amount; 180 | } 181 | 182 | /* 183 | * When mincap is not reach backer can call the approveAndCall function of the RLC token contract 184 | * with this crowdsale contract on parameter with all the RLC they get in order to be refund 185 | */ 186 | function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) minCapNotReached public { 187 | if (msg.sender != address(rlc)) throw; 188 | if (_extraData.length != 0) throw; // no extradata needed 189 | if (_value != backers[_from].rlcSent) throw; // compare value from backer balance 190 | if (!rlc.transferFrom(_from, address(this), _value)) throw ; // get the token back to the crowdsale contract 191 | if (!rlc.burn(_value)) throw ; // token sent for refund are burnt 192 | uint ETHToSend = backers[_from].weiReceived; 193 | backers[_from].weiReceived=0; 194 | uint BTCToSend = backers[_from].satoshiReceived; 195 | backers[_from].satoshiReceived = 0; 196 | if (ETHToSend > 0) { 197 | asyncSend(_from,ETHToSend); // pull payment to get refund in ETH 198 | } 199 | if (BTCToSend > 0) 200 | RefundBTC(backers[_from].btc_address ,BTCToSend); // event message to manually refund BTC 201 | } 202 | 203 | /* 204 | * Update the rate RLC per ETH, computed externally by using the ETHBTC index on kraken every 10min 205 | */ 206 | function setRLCPerETH(uint rate) onlyBy(BTCproxy) { 207 | RLCPerETH=rate; 208 | } 209 | 210 | /* 211 | * Finalize the crowdsale, should be called after the refund period 212 | */ 213 | function finalize() onlyBy(owner) { 214 | // check 215 | if (RLCSentToETH + RLCSentToBTC < maxCap - 5000000000000 && now < endBlock) throw; // cannot finalise before 30 day until maxcap is reached minus 1BTC 216 | if (RLCSentToETH + RLCSentToBTC < minCap && now < endBlock + 15 days) throw ; // if mincap is not reached donors have 15days to get refund before we can finalise 217 | if (!multisigETH.send(this.balance)) throw; // moves the remaining ETH to the multisig address 218 | if (rlc_reserve > 6000000000000000){ // moves RLC to the team, reserve and bounty address 219 | if(!rlc.transfer(reserve,6000000000000000)) throw; // max cap 6000000RLC 220 | rlc_reserve = 6000000000000000; 221 | } else { 222 | if(!rlc.transfer(reserve,rlc_reserve)) throw; 223 | } 224 | if (rlc_bounty > 6000000000000000){ 225 | if(!rlc.transfer(bounty,6000000000000000)) throw; // max cap 6000000RLC 226 | rlc_bounty = 6000000000000000; 227 | } else { 228 | if(!rlc.transfer(bounty,rlc_bounty)) throw; 229 | } 230 | if (!rlc.transfer(team,rlc_team)) throw; 231 | uint RLCEmitted = rlc_reserve + rlc_bounty + rlc_team + RLCSentToBTC + RLCSentToETH; 232 | if (RLCEmitted < rlc.totalSupply()) // burn the rest of RLC 233 | rlc.burn(rlc.totalSupply() - RLCEmitted); 234 | rlc.unlock(); 235 | crowdsaleClosed = true; 236 | } 237 | 238 | /* 239 | * Failsafe drain 240 | */ 241 | function drain() onlyBy(owner) { 242 | if (!owner.send(this.balance)) throw; 243 | } 244 | } 245 | 246 | -------------------------------------------------------------------------------- /contracts/ERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract ERC20 { 4 | uint public totalSupply; 5 | function balanceOf(address who) constant returns (uint); 6 | function allowance(address owner, address spender) constant returns (uint); 7 | 8 | function transfer(address to, uint value) returns (bool ok); 9 | function transferFrom(address from, address to, uint value) returns (bool ok); 10 | function approve(address spender, uint value) returns (bool ok); 11 | event Transfer(address indexed from, address indexed to, uint value); 12 | event Approval(address indexed owner, address indexed spender, uint value); 13 | } 14 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import "./Ownable.sol"; 4 | 5 | contract Migrations is Ownable { 6 | uint public lastCompletedMigration; 7 | 8 | function setCompleted(uint completed) onlyOwner { 9 | lastCompletedMigration = completed; 10 | } 11 | 12 | function upgrade(address newAddress) onlyOwner { 13 | Migrations upgraded = Migrations(newAddress); 14 | upgraded.setCompleted(lastCompletedMigration); 15 | } 16 | } -------------------------------------------------------------------------------- /contracts/Ownable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract Ownable { 4 | address public owner; 5 | 6 | function Ownable() { 7 | owner = msg.sender; 8 | } 9 | 10 | modifier onlyOwner() { 11 | if (msg.sender == owner) 12 | _; 13 | } 14 | 15 | function transferOwnership(address newOwner) onlyOwner { 16 | if (newOwner != address(0)) owner = newOwner; 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /contracts/Pausable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | import "./Ownable.sol"; 5 | 6 | 7 | /* 8 | * Pausable 9 | * Abstract contract that allows children to implement an 10 | * emergency stop mechanism. 11 | */ 12 | 13 | contract Pausable is Ownable { 14 | bool public stopped; 15 | 16 | modifier stopInEmergency { 17 | if (stopped) { 18 | throw; 19 | } 20 | _; 21 | } 22 | 23 | modifier onlyInEmergency { 24 | if (!stopped) { 25 | throw; 26 | } 27 | _; 28 | } 29 | 30 | // called by the owner on emergency, triggers stopped state 31 | function emergencyStop() external onlyOwner { 32 | stopped = true; 33 | } 34 | 35 | // called by the owner on end of emergency, returns to normal state 36 | function release() external onlyOwner onlyInEmergency { 37 | stopped = false; 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /contracts/PullPayment.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity ^0.4.8; 3 | 4 | 5 | /* 6 | * PullPayment 7 | * Base contract supporting async send for pull payments. 8 | * Inherit from this contract and use asyncSend instead of send. 9 | */ 10 | contract PullPayment { 11 | mapping(address => uint) public payments; 12 | event RefundETH(address to, uint value); 13 | // store sent amount as credit to be pulled, called by payer 14 | function asyncSend(address dest, uint amount) internal { 15 | payments[dest] += amount; 16 | } 17 | 18 | // withdraw accumulated balance, called by payee 19 | function withdrawPayments() { 20 | address payee = msg.sender; 21 | uint payment = payments[payee]; 22 | 23 | if (payment == 0) { 24 | throw; 25 | } 26 | 27 | if (this.balance < payment) { 28 | throw; 29 | } 30 | 31 | payments[payee] = 0; 32 | 33 | if (!payee.send(payment)) { 34 | throw; 35 | } 36 | RefundETH(payee,payment); 37 | } 38 | } -------------------------------------------------------------------------------- /contracts/RLC.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import "./Ownable.sol"; 4 | import "./SafeMath.sol"; 5 | import "./ERC20.sol"; 6 | import "./TokenSpender.sol"; 7 | contract RLC is ERC20, SafeMath, Ownable { 8 | 9 | /* Public variables of the token */ 10 | string public name; //fancy name 11 | string public symbol; 12 | uint8 public decimals; //How many decimals to show. 13 | string public version = 'v0.1'; 14 | uint public initialSupply; 15 | uint public totalSupply; 16 | bool public locked; 17 | //uint public unlockBlock; 18 | 19 | mapping(address => uint) balances; 20 | mapping (address => mapping (address => uint)) allowed; 21 | 22 | // lock transfer during the ICO 23 | modifier onlyUnlocked() { 24 | if (msg.sender != owner && locked) throw; 25 | _; 26 | } 27 | 28 | /* 29 | * The RLC Token created with the time at which the crowdsale end 30 | */ 31 | 32 | function RLC() { 33 | // lock the transfer function during the crowdsale 34 | locked = true; 35 | //unlockBlock= now + 45 days; // (testnet) - for mainnet put the block number 36 | 37 | initialSupply = 87000000000000000; 38 | totalSupply = initialSupply; 39 | balances[msg.sender] = initialSupply;// Give the creator all initial tokens 40 | name = 'iEx.ec Network Token'; // Set the name for display purposes 41 | symbol = 'RLC'; // Set the symbol for display purposes 42 | decimals = 9; // Amount of decimals for display purposes 43 | } 44 | 45 | function unlock() onlyOwner { 46 | locked = false; 47 | } 48 | 49 | function burn(uint256 _value) returns (bool){ 50 | balances[msg.sender] = safeSub(balances[msg.sender], _value) ; 51 | totalSupply = safeSub(totalSupply, _value); 52 | Transfer(msg.sender, 0x0, _value); 53 | return true; 54 | } 55 | 56 | function transfer(address _to, uint _value) onlyUnlocked returns (bool) { 57 | balances[msg.sender] = safeSub(balances[msg.sender], _value); 58 | balances[_to] = safeAdd(balances[_to], _value); 59 | Transfer(msg.sender, _to, _value); 60 | return true; 61 | } 62 | 63 | function transferFrom(address _from, address _to, uint _value) onlyUnlocked returns (bool) { 64 | var _allowance = allowed[_from][msg.sender]; 65 | 66 | balances[_to] = safeAdd(balances[_to], _value); 67 | balances[_from] = safeSub(balances[_from], _value); 68 | allowed[_from][msg.sender] = safeSub(_allowance, _value); 69 | Transfer(_from, _to, _value); 70 | return true; 71 | } 72 | 73 | function balanceOf(address _owner) constant returns (uint balance) { 74 | return balances[_owner]; 75 | } 76 | 77 | function approve(address _spender, uint _value) returns (bool) { 78 | allowed[msg.sender][_spender] = _value; 79 | Approval(msg.sender, _spender, _value); 80 | return true; 81 | } 82 | 83 | /* Approve and then comunicate the approved contract in a single tx */ 84 | function approveAndCall(address _spender, uint256 _value, bytes _extraData){ 85 | TokenSpender spender = TokenSpender(_spender); 86 | if (approve(_spender, _value)) { 87 | spender.receiveApproval(msg.sender, _value, this, _extraData); 88 | } 89 | } 90 | 91 | function allowance(address _owner, address _spender) constant returns (uint remaining) { 92 | return allowed[_owner][_spender]; 93 | } 94 | 95 | } 96 | 97 | -------------------------------------------------------------------------------- /contracts/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract SafeMath { 4 | function safeMul(uint a, uint b) internal returns (uint) { 5 | uint c = a * b; 6 | assert(a == 0 || c / a == b); 7 | return c; 8 | } 9 | 10 | function safeDiv(uint a, uint b) internal returns (uint) { 11 | assert(b > 0); 12 | uint c = a / b; 13 | assert(a == b * c + a % b); 14 | return c; 15 | } 16 | 17 | function safeSub(uint a, uint b) internal returns (uint) { 18 | assert(b <= a); 19 | return a - b; 20 | } 21 | 22 | function safeAdd(uint a, uint b) internal returns (uint) { 23 | uint c = a + b; 24 | assert(c>=a && c>=b); 25 | return c; 26 | } 27 | 28 | function max64(uint64 a, uint64 b) internal constant returns (uint64) { 29 | return a >= b ? a : b; 30 | } 31 | 32 | function min64(uint64 a, uint64 b) internal constant returns (uint64) { 33 | return a < b ? a : b; 34 | } 35 | 36 | function max256(uint256 a, uint256 b) internal constant returns (uint256) { 37 | return a >= b ? a : b; 38 | } 39 | 40 | function min256(uint256 a, uint256 b) internal constant returns (uint256) { 41 | return a < b ? a : b; 42 | } 43 | 44 | function assert(bool assertion) internal { 45 | if (!assertion) { 46 | throw; 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /contracts/TokenSpender.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract TokenSpender { 4 | function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData); 5 | } -------------------------------------------------------------------------------- /documentation/Crowdfundingv2.1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iExecBlockchainComputing/rlc-token/83cb61a1c61357edb56e63a6ef7fa3f2edbe51fa/documentation/Crowdfundingv2.1.pdf -------------------------------------------------------------------------------- /documentation/iEx.ecTermsandConditionsENGLISHVERSION.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iExecBlockchainComputing/rlc-token/83cb61a1c61357edb56e63a6ef7fa3f2edbe51fa/documentation/iEx.ecTermsandConditionsENGLISHVERSION.pdf -------------------------------------------------------------------------------- /documentation/review-openzeppelin: -------------------------------------------------------------------------------- 1 | ****************************************************************** 2 | ** iEx.ec RLC Token Audit / OpenZeppelin ** 3 | ****************************************************************** 4 | 5 | 6 | The iEx.ec team asked us to review and audit their new RLC token contract code. We looked at their contracts and now publish our results. 7 | The audited contracts are at their iExecBlockchainComputing/rlc-token GitHub repo. The version used for this report is commit 3d9aa99ba33bb035c59740a621b1f21cd45cbac5. The main contract files are Crowdsale.sol and RLC.sol. 8 | 9 | Here’s our assessment and recommendations, in order of importance: 10 | 11 | * Severe 12 | We haven’t found any severe security problems with the code. 13 | 14 | 15 | * Potential problems 16 | 17 | - Use safe math 18 | 19 | There are many unchecked math operations in the code. It’s always better to be safe and perform checked operations. Consider using a safe math library, or performing pre-condition checks on any math operation. 20 | 21 | >> corrected in #27db1c2fd774bf1f9ed64a8373911d3ac975773a 22 | 23 | - Timestamp usage 24 | 25 | There’s a problem with using timestamps and now (alias for block.timestamp) for contract logic, based on the fact that miners can perform some manipulation. In general, it’s better not to rely on timestamps for contract logic. The solutions is to use block.number instead, and approximate dates with expected block heights and time periods with expected block amounts. 26 | 27 | The Crowdsale.sol and RLC.sol contracts use timestamps in several places (for example, lines 69, 116, 145, and 295 of Crowdsale.sol and lines 37 and 49 of RLC.sol). The risk of miner manipulation, though, is really low. The potential damage is also limited: miners could only slightly manipulate when crowdfunding ends and starts. We recommend the team to consider the potential risk of this manipulation and switch to block.number if necessary. 28 | For more info on this topic, see this stack exchange question. 29 | 30 | >> we decided to keep the usage of timestamp because the it has very limited impact on the crowdsale process and we favor readability of the smart contract 31 | 32 | * Warnings 33 | 34 | - Use of send 35 | Use of send is always risky and should be analyzed in detail. Two occurrences found in line 255 and line 297 of Crowdsale.sol . 36 | - Always check send return value: OK. 37 | - Consider calling send at the end of the function: Warning. Use at line 297 could be moved down. 38 | - Favor pull payments over push payments: line 255 is a pull payment, line 297 is a push payment. Consider changing line 297 to use asyncSend. 39 | For more info on this problem, see this note. 40 | 41 | >> The only push payment is on a onlyowner payment 42 | 43 | Usage of magic constants 44 | There are several magic constants in the contract code. Some examples are: 45 | https://github.com/iExecBlockchainComputing/rlc-token/blob/3d9aa99ba33bb035c59740a621b1f21cd45cbac5/contracts/Crowdsale.sol#L295 46 | https://github.com/iExecBlockchainComputing/rlc-token/blob/3d9aa99ba33bb035c59740a621b1f21cd45cbac5/contracts/Crowdsale.sol#L148 47 | Use of magic constants reduces code readability and makes it harder to understand code intention. We recommend extracting magic constants into contract constants. 48 | 49 | >> corrected in # 50 | 51 | - Bug Bounty 52 | Formal security audits are not enough to be safe. We recommend implementing an automated contract-based bug bounty and setting a period of time where security researchers from around the globe can try to break the contract’s invariants. For more info on how to implement automated bug bounties with OpenZeppelin, see this guide. 53 | 54 | >> a bug bounty program has been launched 55 | 56 | - Unused isMaxCapReached 57 | isMaxCapReached is not used in Crowdsale.sol, but seems like it could be used in line 186 or in the finalize function. 58 | 59 | >> No more used after 4e94def18e33f867e24b024f5770d51f07fd6425 60 | 61 | - Avoid duplicated code 62 | Duplicate code makes it harder to understand the code’s intention and thus, auditing the code correctly. It also increases the risk of introducing hidden bugs when modifying one of the copies of some code and not the others. 63 | The logic in transfer, transferFrom, balanceOf, allowance and approve is very similar to the provided methods in StandardToken and could be refactored to avoid repetition. There is no need to rewrite the parent’s contract logic: consider using the super keyword to access StandardToken implementation of methods, like so: 64 | function transfer(address _to, uint _value) onlyUnlocked returns (bool success) { 65 | return super.transfer(_to, _value); 66 | } 67 | 68 | >> we decided not to follow this recommandation in order to favor code readbility 69 | 70 | - Furthermore, the burn method is a close rewrite of transfer, consider having burn call transfer instead. 71 | 72 | >> we decided not to follow this recommandation in order to favor code readbility 73 | 74 | Finally, Crowdsale.sol could use OpenZeppelin’s Ownable instead of implementing the same idea 75 | 76 | >> we decided not to follow this recommandation in order to favor code readbility, cause we have another modifier onlyBy() 77 | 78 | - Use latest version of Solidity 79 | Current code is written for an old version of solc (0.4.8). We recommend changing the solidity version pragma for the latest version (pragma solidity ^0.4.10;) to enforce latest compiler version to be used. 80 | 81 | >> corrected in # 82 | 83 | - Fail early and loudly 84 | A simple yet powerful programming good practice is to make your code fail as promptly as possible. And be loud about it. We want to avoid a contract failing silently, or continuing execution in an unstable or inconsistent state. Consider changing all precondition checks to throwing to follow this good practice. Some parts of the code do this correctly, but we’d like to see more consistency on this pattern. 85 | Some places where this could be improved are: 86 | https://github.com/iExecBlockchainComputing/rlc-token/blob/3d9aa99ba33bb035c59740a621b1f21cd45cbac5/contracts/Pausable.sol#L16 87 | https://github.com/iExecBlockchainComputing/rlc-token/blob/3d9aa99ba33bb035c59740a621b1f21cd45cbac5/contracts/Pausable.sol#L22 88 | https://github.com/iExecBlockchainComputing/rlc-token/blob/3d9aa99ba33bb035c59740a621b1f21cd45cbac5/contracts/Ownable.sol 89 | 90 | >> corrected in 4e94def18e33f867e24b024f5770d51f07fd6425 91 | 92 | - Remove the closeCrowdsaleForRefund function (as the comment above it says). Not removing this function would be a severe security problem, but we mention it as an informational note because of the comment above it, which means the team is aware of this. 93 | - Same with finalizeTEST. 94 | 95 | >> corrected in f48da7e3e3815fd78e75ef53dced66486519d8ba 96 | 97 | - Remove commented code on line 149 of Crowdsale.sol. 98 | Same with line 294. 99 | Same with lines 260–281. 100 | 101 | >> corrected in # 162fb7261ee3dbc13c2dfef89c69ffd3552ac15d 102 | 103 | - Remove unnecessary code in lines 101–104 of RLC.sol. Since solidity 0.4.0, contracts will throw by default when funds are sent, unless you add a payable fallback function. 104 | 105 | >> corrected in f48da7e3e3815fd78e75ef53dced66486519d8ba 106 | 107 | - Remove unnecessary variable burnAddress, and just set the balances to 0 to burn tokens. 108 | 109 | >> corrected in 88663507bb24baca23b79548214468cb5c320ca5 110 | 111 | - You probably want the unlock function in RLC to have the onlyOwner modifier. Even though there’s no real problem with other user to call the method, it’s recommended to have full control over when those important methods are called. 112 | Unlock function in RLC is unnecessary, and onlyUnlocked modifier could use unlockBlock directly for its logic. 113 | 114 | >> corrected in f48da7e3e3815fd78e75ef53dced66486519d8ba , The unlock method is now call in finalise 115 | 116 | - Fix indentation of lines 250 to 258 of Crowdsale.sol. 117 | 118 | >> corrected in # 162fb7261ee3dbc13c2dfef89c69ffd3552ac15d 119 | 120 | Some state variables of RLC.sol could be constant for added safety. 121 | Some functions on Crowdsale.sol could be declared constant. 122 | 123 | >> corrected in # bf1ccb5173d2a0a79eef7c566aef2880e595c664 124 | 125 | transferRLC function doesn’t add any functionality and may add confusion. 126 | 127 | >> corrected in # 4e94def18e33f867e24b024f5770d51f07fd6425 128 | 129 | Comments in lines 160 and 161 of Crowdsale.sol have typos: “collcted”. 130 | 131 | >> corrected in # d9c07a8d9aa7c82ab3ec52f7026f8a7ac393504a 132 | 133 | 134 | -------------------------------------------------------------------------------- /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 RLC = artifacts.require("./RLC.sol"); 2 | var Crowdsale = artifacts.require("./Crowdsale.sol"); 3 | 4 | 5 | 6 | module.exports = function(deployer, network) { 7 | var owner = web3.eth.accounts[0]; 8 | var btcproxy = web3.eth.accounts[1]; 9 | deployer.deploy(RLC, {from: owner}).then(function(){ 10 | return deployer.deploy(); 11 | }); 12 | }; 13 | 14 | /* ADD these step 15 | 16 | truffle console: 17 | RLC.at(RLC.address).transfer(Crowdsale.address,87000000000000000) 18 | RLC.at(RLC.address).balanceOf(Crowdsale.address) 19 | RLC.at(RLC.address).transferOwnership(Crowdsale.address) 20 | RLC.at(RLC.address).owner() 21 | 22 | 23 | */ 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rlc-token", 3 | "version": "1.0.1", 4 | "description": "", 5 | "main": "truffle.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/iExecBlockchainComputing/rlc-token.git" 15 | }, 16 | "author": "", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/iExecBlockchainComputing/rlc-token/issues" 20 | }, 21 | "homepage": "https://github.com/iExecBlockchainComputing/rlc-token#readme" 22 | } 23 | -------------------------------------------------------------------------------- /testrpc.sh: -------------------------------------------------------------------------------- 1 | testrpc --account="0x6a936b41ff2044aa470c693b1cba7af1f7ea10139d9d65dc403518df037515d5,10000000000000000000000000" --account="0x5ebd6d2fa870e35d48ee3f803dbbab34403aa42c67a3036dee721235a87cdd69,10000000000000000000000000" --account="0x56383562a52a451683c2cae3c09b2c1b2a173ab0cdfb8526006fb0b8654d54c3,10000000000000000000000000" --account="0xc73e31c9705af249fde8c4d01dc43e536f57c8fc9fd33612dfb83f3de7b4307e,10000000000000000000000000" --account="0xf9f8a80e31d63d91371324b43723ccf293af28213c8b6824162fed90b4f465a4,10000000000000000000000000" --account="0x571ed1b6eb404fcd7fc2562f6e0d5fa27c4be6db079f9e658ad4137f059472b3,10000000000000000000000000" -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | development: { 4 | host: "localhost", 5 | port: 8545, 6 | network_id: "*" // Match any network id 7 | }, 8 | ropsten: { 9 | host: "localhost", 10 | port: 8545, 11 | network_id: "*" // Match any network id 12 | } 13 | } 14 | }; 15 | --------------------------------------------------------------------------------