-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Federico Martín Alconada Verzini
committed
Nov 21, 2017
1 parent
2e0a775
commit 1385a57
Showing
17 changed files
with
1,158 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
Copyright 2017 The qiibee Team | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use files included in this repository except in compliance | ||
with the License. You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
![](https://avatars3.githubusercontent.com/u/31820267?v=4&s=100) | ||
|
||
QBX Token | ||
======================= | ||
|
||
[![Build Status](https://travis-ci.org/qiibee/qb-contracts.svg?branch=master)](https://travis-ci.org/qiibee/qb-contracts) | ||
[![Coverage Status](https://coveralls.io/repos/github/qiibee/qb-contracts/badge.svg?branch=master)](https://coveralls.io/github/qiibee/qb-contracts?branch=master) | ||
|
||
QBX is the token (ERC20 based) of the qiibee protocol. | ||
|
||
|
||
## Requirements | ||
|
||
Node v7.6 or higher (versions before 7.6 do not support async/await that is used in the QiibeeToken tests) | ||
|
||
## Install | ||
|
||
```sh | ||
npm install | ||
``` | ||
|
||
## Main Contracts | ||
|
||
- [QiibeeToken](contracts/QiibeeToken.sol) | ||
- [QiibeeCrowdsale](contracts/QiibeeCrowdsale.sol) | ||
- [QiibeePresale](contracts/QiibeePresale.sol) | ||
|
||
## Test | ||
|
||
* To run all tests: `npm test` | ||
|
||
* To run a specific test: `npm test -- test/Crowdsale.js` | ||
|
||
There are also two environment variables (`GEN_TESTS_QTY` and `GEN_TESTS_TIMEOUT`) that regulate the duration/depth of the property-based tests, so for example: | ||
|
||
```sh | ||
GEN_TESTS_QTY=50 GEN_TESTS_TIMEOUT=300 npm test | ||
``` | ||
|
||
will make the property-based tests in `test/QiibeeCrowdsaleGenTest.js` `test/QiibeePresaleGenTest.js` to run 50 examples in a maximum of 5 minutes | ||
|
||
## License | ||
|
||
qiibee Token is open source and distributed under the Apache License v2.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
pragma solidity ^0.4.11; | ||
|
||
import "zeppelin-solidity/contracts/lifecycle/Pausable.sol"; | ||
import "zeppelin-solidity/contracts/crowdsale/RefundVault.sol"; | ||
// import "./QiibeeToken.sol"; | ||
|
||
/** | ||
@title Crowdsale for the QBX Token Generation Event | ||
Implementation of kind of an 'abstract' Crowdsale. This contract will be | ||
used by QiibeePresale.sol and QiibeeCrowdsale.sol | ||
This Crowdsale is capped and has a spam prevention technique: | ||
* investors can make purchases with a minimum request inverval of X seconds given by minBuyingRequestInterval. | ||
* investors are limited in the gas price | ||
In case of the goal not being reached by purchases made during crowdsale period funds sent will | ||
be made available to be claimed by the originating addresses. | ||
The function buyTokens() does not mint tokens. This function should be overriden to add that logic. | ||
*/ | ||
|
||
contract QiibeeToken { | ||
function mintVestedTokens(address _to, | ||
uint256 _value, | ||
uint64 _start, | ||
uint64 _cliff, | ||
uint64 _vesting, | ||
bool _revokable, | ||
bool _burnsOnRevoke, | ||
address _wallet | ||
) returns (bool); | ||
function mint(address _to, uint256 _amount) returns (bool); | ||
function transferOwnership(address _wallet); | ||
function pause(); | ||
function unpause(); | ||
function finishMinting() returns (bool); | ||
} | ||
|
||
contract Crowdsale is Pausable { | ||
|
||
using SafeMath for uint256; | ||
|
||
uint256 public startTime; | ||
uint256 public endTime; | ||
|
||
uint256 public cap; // max amount of funds to be raised in wei | ||
uint256 public goal; // min amount of funds to be raised in wei | ||
RefundVault public vault; // refund vault used to hold funds while crowdsale is running | ||
|
||
uint256 public rate; // how many token units a buyer gets per wei | ||
|
||
QiibeeToken public token; // token being sold | ||
uint256 public tokensSold; // qbx minted (and sold) | ||
uint256 public weiRaised; // raised money in wei | ||
mapping (address => uint256) public balances; // balance of wei invested per investor | ||
|
||
// spam prevention | ||
mapping (address => uint256) public lastCallTime; // last call times by address | ||
uint256 public maxGasPrice; // max gas price per transaction | ||
uint256 public minBuyingRequestInterval; // min request interval for purchases from a single source (in seconds) | ||
|
||
bool public isFinalized = false; // whether the crowdsale has finished or not | ||
|
||
address public wallet; // address where funds are collected | ||
|
||
/* | ||
* @dev event for change wallet logging | ||
* @param wallet new wallet address | ||
*/ | ||
event WalletChange(address wallet); | ||
|
||
/** | ||
* event for token purchase logging | ||
* @param purchaser who paid for the tokens | ||
* @param beneficiary who got the tokens | ||
* @param value in wei paid for purchase | ||
* @param amount amount of tokens purchased | ||
*/ | ||
event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount); | ||
|
||
event Finalized(); | ||
|
||
/* | ||
* @dev Constructor. Creates the token in a paused state | ||
* @param _startTime see `startTimestamp` | ||
* @param _endTime see `endTimestamp` | ||
* @param _rate see `see rate` | ||
* @param _goal see `see goal` | ||
* @param _cap see `see cap` | ||
* @param _maxGasPrice see `see maxGasPrice` | ||
* @param _minBuyingRequestInterval see `see minBuyingRequestInterval` | ||
* @param _wallet see `wallet` | ||
*/ | ||
function Crowdsale ( | ||
uint256 _startTime, | ||
uint256 _endTime, | ||
uint256 _rate, | ||
uint256 _goal, | ||
uint256 _cap, | ||
uint256 _maxGasPrice, | ||
uint256 _minBuyingRequestInterval, | ||
address _wallet | ||
) | ||
{ | ||
require(_startTime >= now); | ||
require(_endTime >= _startTime); | ||
require(_rate > 0); | ||
require(_cap > 0); | ||
require(_goal > 0); | ||
require(_goal <= _cap); | ||
require(_maxGasPrice > 0); | ||
require(_minBuyingRequestInterval > 0); | ||
require(_wallet != address(0)); | ||
|
||
|
||
startTime = _startTime; | ||
endTime = _endTime; | ||
rate = _rate; | ||
cap = _cap; | ||
goal = _goal; | ||
maxGasPrice = _maxGasPrice; | ||
minBuyingRequestInterval = _minBuyingRequestInterval; | ||
wallet = _wallet; | ||
|
||
// token = new QiibeeToken(); | ||
vault = new RefundVault(wallet); | ||
|
||
// token.pause(); | ||
|
||
} | ||
|
||
/* | ||
* @dev fallback function can be used to buy tokens | ||
*/ | ||
function () payable whenNotPaused { | ||
buyTokens(msg.sender); | ||
} | ||
|
||
/* | ||
* @dev Low level token purchase function. | ||
* @param beneficiary address where tokens are sent to | ||
*/ | ||
function buyTokens(address beneficiary) public payable whenNotPaused { | ||
require(beneficiary != address(0)); | ||
require(validPurchase()); | ||
|
||
uint256 weiAmount = msg.value; | ||
|
||
// calculate token amount to be created | ||
uint256 tokens = weiAmount.mul(rate); | ||
|
||
// update state | ||
weiRaised = weiRaised.add(weiAmount); | ||
tokensSold = tokensSold.add(tokens); | ||
lastCallTime[msg.sender] = now; | ||
|
||
token.mint(beneficiary, tokens); | ||
TokenPurchase(msg.sender, beneficiary, weiAmount, tokens); | ||
|
||
forwardFunds(); | ||
} | ||
|
||
/* | ||
* @return true if investors can buy at the moment | ||
*/ | ||
function validPurchase() internal constant returns (bool) { | ||
bool withinFrequency = now.sub(lastCallTime[msg.sender]) >= minBuyingRequestInterval; | ||
bool withinGasPrice = tx.gasprice <= maxGasPrice; | ||
bool withinPeriod = now >= startTime && now <= endTime; | ||
bool withinCap = weiRaised.add(msg.value) <= cap; | ||
bool nonZeroPurchase = msg.value != 0; | ||
return withinFrequency && withinGasPrice && withinPeriod && withinCap && nonZeroPurchase; | ||
} | ||
|
||
/* | ||
* @return true if crowdsale event has ended | ||
*/ | ||
function hasEnded() public constant returns (bool) { | ||
bool capReached = weiRaised >= cap; | ||
return now > endTime || capReached; | ||
} | ||
|
||
/* | ||
* @return true if crowdsale goal has reached | ||
*/ | ||
function goalReached() public constant returns (bool) { | ||
return weiRaised >= goal; | ||
} | ||
|
||
/* | ||
* In addition to sending the funds, we want to call the RefundVault deposit function | ||
*/ | ||
function forwardFunds() internal { | ||
vault.deposit.value(msg.value)(msg.sender); | ||
} | ||
|
||
/* | ||
* if crowdsale is unsuccessful, investors can claim refunds here | ||
*/ | ||
function claimRefund() public { | ||
require(isFinalized); | ||
require(!goalReached()); | ||
|
||
vault.refund(msg.sender); | ||
} | ||
|
||
/** | ||
* @dev Must be called after crowdsale ends, to do some extra finalization | ||
* work. Calls the contract's finalization function. | ||
*/ | ||
function finalize() public onlyOwner { | ||
require(!isFinalized); | ||
require(hasEnded()); | ||
|
||
finalization(); | ||
Finalized(); | ||
|
||
isFinalized = true; | ||
} | ||
|
||
/** | ||
* @dev Can be overridden to add finalization logic. The overriding function | ||
* should call super.finalization() to ensure the chain of finalization is | ||
* executed entirely. | ||
*/ | ||
function finalization() internal { | ||
if (goalReached()) { | ||
vault.close(); | ||
} else { | ||
vault.enableRefunds(); | ||
} | ||
} | ||
|
||
/* | ||
* @dev Changes the current wallet for a new one. Only the owner can call this function. | ||
* @param _wallet new wallet | ||
*/ | ||
function setWallet(address _wallet) onlyOwner public { | ||
require(_wallet != address(0)); | ||
wallet = _wallet; | ||
WalletChange(_wallet); | ||
} | ||
|
||
/** | ||
@dev changes the token owner | ||
*/ | ||
//TODO: EXECUTE BEFORE START CROWDSALE | ||
function setToken(address tokenAddress) onlyOwner { | ||
require(now < startTime); | ||
token = QiibeeToken(tokenAddress); | ||
} | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
pragma solidity ^0.4.11; | ||
|
||
import "zeppelin-solidity/contracts/ownership/Ownable.sol"; | ||
import "./QiibeeToken.sol"; | ||
|
||
// interface | ||
contract QiibeeMigrationTokenInterface { | ||
function createToken(address _target, uint256 _amount); | ||
function finalizeMigration(); | ||
function totalSupply() returns (uint256); | ||
} | ||
|
||
contract MigrationAgent is Ownable { | ||
|
||
address public qbxSourceToken; | ||
address public qbxTargetToken; | ||
uint256 public tokenSupply; | ||
|
||
function MigrationAgent(address _qbxSourceToken) { | ||
require(QiibeeToken(_qbxSourceToken).migrationAgent() == address(0)); | ||
tokenSupply = QiibeeToken(_qbxSourceToken).totalSupply(); | ||
qbxSourceToken = _qbxSourceToken; | ||
} | ||
|
||
function safetyInvariantCheck(uint256 _value) internal { | ||
require(QiibeeToken(qbxSourceToken).totalSupply() + QiibeeMigrationTokenInterface(qbxTargetToken).totalSupply() == tokenSupply - _value); | ||
} | ||
|
||
function setTargetToken(address _qbxTargetToken) public onlyOwner { | ||
require(qbxTargetToken == address(0)); //Allow this change once only | ||
qbxTargetToken = _qbxTargetToken; | ||
} | ||
|
||
function migrateFrom(address _from, uint256 _value) public { | ||
require(msg.sender == qbxSourceToken); | ||
require(qbxTargetToken != address(0)); | ||
|
||
safetyInvariantCheck(_value); // qbxSourceToken has already been updated, but corresponding QBX have not been created in the qbxTargetToken contract yet | ||
QiibeeMigrationTokenInterface(qbxTargetToken).createToken(_from, _value); | ||
safetyInvariantCheck(0); // totalSupply invariant must hold | ||
} | ||
|
||
function finalizeMigration() public onlyOwner { | ||
require(qbxTargetToken != address(0)); | ||
require(QiibeeToken(qbxSourceToken).totalSupply() == 0); //only finlize if all tokens have been migrated | ||
safetyInvariantCheck(0); | ||
QiibeeMigrationTokenInterface(qbxTargetToken).finalizeMigration(); | ||
|
||
qbxSourceToken = address(0); | ||
qbxTargetToken = address(0); | ||
tokenSupply = 0; | ||
} | ||
} |
Oops, something went wrong.