Commit 38536f42 by Francisco Giordano

delete contracts and tests not yet in zos

parent c63b203c
pragma solidity ^0.4.21;
import "./payment/PullPayment.sol";
import "./lifecycle/Destructible.sol";
/**
* @title Bounty
* @dev This bounty will pay out to a researcher if they break invariant logic of the contract.
*/
contract Bounty is PullPayment, Destructible {
bool public claimed;
mapping(address => address) public researchers;
event TargetCreated(address createdAddress);
/**
* @dev Fallback function allowing the contract to receive funds, if they haven't already been claimed.
*/
function() external payable {
require(!claimed);
}
/**
* @dev Create and deploy the target contract (extension of Target contract), and sets the
* msg.sender as a researcher
* @return A target contract
*/
function createTarget() public returns(Target) {
Target target = Target(deployContract());
researchers[target] = msg.sender;
emit TargetCreated(target);
return target;
}
/**
* @dev Sends the contract funds to the researcher that proved the contract is broken.
* @param target contract
*/
function claim(Target target) public {
address researcher = researchers[target];
require(researcher != 0);
// Check Target contract invariants
require(!target.checkInvariant());
asyncSend(researcher, address(this).balance);
claimed = true;
}
/**
* @dev Internal function to deploy the target contract.
* @return A target contract address
*/
function deployContract() internal returns(address);
}
/**
* @title Target
* @dev Your main contract should inherit from this class and implement the checkInvariant method.
*/
contract Target {
/**
* @dev Checks all values a contract assumes to be true all the time. If this function returns
* false, the contract is broken in some way and is in an inconsistent state.
* In order to win the bounty, security researchers will try to cause this broken state.
* @return True if all invariant values are correct, false otherwise.
*/
function checkInvariant() public returns(bool);
}
pragma solidity ^0.4.21;
/**
* @title DayLimit
* @dev Base contract that enables methods to be protected by placing a linear limit (specifiable)
* on a particular resource per calendar day. Is multiowned to allow the limit to be altered.
*/
contract DayLimit {
uint256 public dailyLimit;
uint256 public spentToday;
uint256 public lastDay;
/**
* @dev Constructor that sets the passed value as a dailyLimit.
* @param _limit uint256 to represent the daily limit.
*/
function DayLimit(uint256 _limit) public {
dailyLimit = _limit;
lastDay = today();
}
/**
* @dev sets the daily limit. Does not alter the amount already spent today.
* @param _newLimit uint256 to represent the new limit.
*/
function _setDailyLimit(uint256 _newLimit) internal {
dailyLimit = _newLimit;
}
/**
* @dev Resets the amount already spent today.
*/
function _resetSpentToday() internal {
spentToday = 0;
}
/**
* @dev Checks to see if there is enough resource to spend today. If true, the resource may be expended.
* @param _value uint256 representing the amount of resource to spend.
* @return A boolean that is True if the resource was spent and false otherwise.
*/
function underLimit(uint256 _value) internal returns (bool) {
// reset the spend limit if we're on a different day to last time.
if (today() > lastDay) {
spentToday = 0;
lastDay = today();
}
// check to see if there's enough left - if so, subtract and return true.
// overflow protection // dailyLimit check
if (spentToday + _value >= spentToday && spentToday + _value <= dailyLimit) {
spentToday += _value;
return true;
}
return false;
}
/**
* @dev Private function to determine today's index
* @return uint256 of today's index.
*/
function today() private view returns (uint256) {
// solium-disable-next-line security/no-block-members
return block.timestamp / 1 days;
}
/**
* @dev Simple modifier for daily limit.
*/
modifier limitedDaily(uint256 _value) {
require(underLimit(_value));
_;
}
}
pragma solidity ^0.4.21;
/**
* @title Eliptic curve signature operations
*
* @dev Based on https://gist.github.com/axic/5b33912c6f61ae6fd96d6c4a47afde6d
*
* TODO Remove this library once solidity supports passing a signature to ecrecover.
* See https://github.com/ethereum/solidity/issues/864
*
*/
library ECRecovery {
/**
* @dev Recover signer address from a message by using their signature
* @param hash bytes32 message, the hash is the signed message. What is recovered is the signer address.
* @param sig bytes signature, the signature is generated using web3.eth.sign()
*/
function recover(bytes32 hash, bytes sig)
internal
pure
returns (address)
{
bytes32 r;
bytes32 s;
uint8 v;
// Check the signature length
if (sig.length != 65) {
return (address(0));
}
// Divide the signature in r, s and v variables
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solium-disable-next-line security/no-inline-assembly
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := byte(0, mload(add(sig, 96)))
}
// Version of signature should be 27 or 28, but 0 and 1 are also possible versions
if (v < 27) {
v += 27;
}
// If the version is correct return the signer address
if (v != 27 && v != 28) {
return (address(0));
} else {
// solium-disable-next-line arg-overflow
return ecrecover(hash, v, r, s);
}
}
/**
* toEthSignedMessageHash
* @dev prefix a bytes32 value with "\x19Ethereum Signed Message:"
* @dev and hash the result
*/
function toEthSignedMessageHash(bytes32 hash)
internal
pure
returns (bytes32)
{
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(
"\x19Ethereum Signed Message:\n32",
hash
);
}
}
pragma solidity ^0.4.21;
/**
* @title LimitBalance
* @dev Simple contract to limit the balance of child contract.
* @dev Note this doesn't prevent other contracts to send funds by using selfdestruct(address);
* @dev See: https://github.com/ConsenSys/smart-contract-best-practices#remember-that-ether-can-be-forcibly-sent-to-an-account
*/
contract LimitBalance {
uint256 public limit;
/**
* @dev Constructor that sets the passed value as a limit.
* @param _limit uint256 to represent the limit.
*/
function LimitBalance(uint256 _limit) public {
limit = _limit;
}
/**
* @dev Checks if limit was reached. Case true, it throws.
*/
modifier limitedPayable() {
require(address(this).balance <= limit);
_;
}
}
pragma solidity ^0.4.21;
/*
* @title MerkleProof
* @dev Merkle proof verification
* @note Based on https://github.com/ameensol/merkle-tree-solidity/blob/master/src/MerkleProof.sol
*/
library MerkleProof {
/*
* @dev Verifies a Merkle proof proving the existence of a leaf in a Merkle tree. Assumes that each pair of leaves
* and each pair of pre-images is sorted.
* @param _proof Merkle proof containing sibling hashes on the branch from the leaf to the root of the Merkle tree
* @param _root Merkle root
* @param _leaf Leaf of Merkle tree
*/
function verifyProof(bytes32[] _proof, bytes32 _root, bytes32 _leaf) internal pure returns (bool) {
bytes32 computedHash = _leaf;
for (uint256 i = 0; i < _proof.length; i++) {
bytes32 proofElement = _proof[i];
if (computedHash < proofElement) {
// Hash(current computed hash + current element of the proof)
computedHash = keccak256(computedHash, proofElement);
} else {
// Hash(current element of the proof + current computed hash)
computedHash = keccak256(proofElement, computedHash);
}
}
// Check if the computed hash (root) is equal to the provided root
return computedHash == _root;
}
}
pragma solidity ^0.4.21;
/**
* @title Helps contracts guard agains reentrancy attacks.
* @author Remco Bloemen <remco@2π.com>
* @notice If you mark a function `nonReentrant`, you should also
* mark it `external`.
*/
contract ReentrancyGuard {
/**
* @dev We use a single lock for the whole contract.
*/
bool private reentrancyLock = false;
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* @notice If you mark a function `nonReentrant`, you should also
* mark it `external`. Calling one nonReentrant function from
* another is not supported. Instead, you can implement a
* `private` function doing the actual work, and a `external`
* wrapper marked as `nonReentrant`.
*/
modifier nonReentrant() {
require(!reentrancyLock);
reentrancyLock = true;
_;
reentrancyLock = false;
}
}
pragma solidity ^0.4.18;
import "../ownership/Ownable.sol";
import "../ownership/rbac/RBAC.sol";
import "../ECRecovery.sol";
/**
* @title SignatureBouncer
* @author PhABC and Shrugs
* @dev Bouncer allows users to submit a signature as a permission to do an action.
* @dev If the signature is from one of the authorized bouncer addresses, the signature
* @dev is valid. The owner of the contract adds/removes bouncers.
* @dev Bouncer addresses can be individual servers signing grants or different
* @dev users within a decentralized club that have permission to invite other members.
* @dev
* @dev This technique is useful for whitelists and airdrops; instead of putting all
* @dev valid addresses on-chain, simply sign a grant of the form
* @dev keccak256(`:contractAddress` + `:granteeAddress`) using a valid bouncer address.
* @dev Then restrict access to your crowdsale/whitelist/airdrop using the
* @dev `onlyValidSignature` modifier (or implement your own using isValidSignature).
* @dev
* @dev See the tests Bouncer.test.js for specific usage examples.
*/
contract SignatureBouncer is Ownable, RBAC {
using ECRecovery for bytes32;
string public constant ROLE_BOUNCER = "bouncer";
/**
* @dev requires that a valid signature of a bouncer was provided
*/
modifier onlyValidSignature(bytes _sig)
{
require(isValidSignature(msg.sender, _sig));
_;
}
/**
* @dev allows the owner to add additional bouncer addresses
*/
function addBouncer(address _bouncer)
onlyOwner
public
{
require(_bouncer != address(0));
addRole(_bouncer, ROLE_BOUNCER);
}
/**
* @dev allows the owner to remove bouncer addresses
*/
function removeBouncer(address _bouncer)
onlyOwner
public
{
require(_bouncer != address(0));
removeRole(_bouncer, ROLE_BOUNCER);
}
/**
* @dev is the signature of `this + sender` from a bouncer?
* @return bool
*/
function isValidSignature(address _address, bytes _sig)
internal
view
returns (bool)
{
return isValidDataHash(
keccak256(address(this), _address),
_sig
);
}
/**
* @dev internal function to convert a hash to an eth signed message
* @dev and then recover the signature and check it against the bouncer role
* @return bool
*/
function isValidDataHash(bytes32 hash, bytes _sig)
internal
view
returns (bool)
{
address signer = hash
.toEthSignedMessageHash()
.recover(_sig);
return hasRole(signer, ROLE_BOUNCER);
}
}
pragma solidity ^0.4.21;
import "../token/ERC20/ERC20.sol";
import "../math/SafeMath.sol";
/**
* @title Crowdsale
* @dev Crowdsale is a base contract for managing a token crowdsale,
* allowing investors to purchase tokens with ether. This contract implements
* such functionality in its most fundamental form and can be extended to provide additional
* functionality and/or custom behavior.
* The external interface represents the basic interface for purchasing tokens, and conform
* the base architecture for crowdsales. They are *not* intended to be modified / overriden.
* The internal interface conforms the extensible and modifiable surface of crowdsales. Override
* the methods to add functionality. Consider using 'super' where appropiate to concatenate
* behavior.
*/
contract Crowdsale {
using SafeMath for uint256;
// The token being sold
ERC20 public token;
// Address where funds are collected
address public wallet;
// How many token units a buyer gets per wei
uint256 public rate;
// Amount of wei raised
uint256 public weiRaised;
/**
* Event for token purchase logging
* @param purchaser who paid for the tokens
* @param beneficiary who got the tokens
* @param value weis paid for purchase
* @param amount amount of tokens purchased
*/
event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);
/**
* @param _rate Number of token units a buyer gets per wei
* @param _wallet Address where collected funds will be forwarded to
* @param _token Address of the token being sold
*/
function Crowdsale(uint256 _rate, address _wallet, ERC20 _token) public {
require(_rate > 0);
require(_wallet != address(0));
require(_token != address(0));
rate = _rate;
wallet = _wallet;
token = _token;
}
// -----------------------------------------
// Crowdsale external interface
// -----------------------------------------
/**
* @dev fallback function ***DO NOT OVERRIDE***
*/
function () external payable {
buyTokens(msg.sender);
}
/**
* @dev low level token purchase ***DO NOT OVERRIDE***
* @param _beneficiary Address performing the token purchase
*/
function buyTokens(address _beneficiary) public payable {
uint256 weiAmount = msg.value;
_preValidatePurchase(_beneficiary, weiAmount);
// calculate token amount to be created
uint256 tokens = _getTokenAmount(weiAmount);
// update state
weiRaised = weiRaised.add(weiAmount);
_processPurchase(_beneficiary, tokens);
emit TokenPurchase(
msg.sender,
_beneficiary,
weiAmount,
tokens
);
_updatePurchasingState(_beneficiary, weiAmount);
_forwardFunds();
_postValidatePurchase(_beneficiary, weiAmount);
}
// -----------------------------------------
// Internal interface (extensible)
// -----------------------------------------
/**
* @dev Validation of an incoming purchase. Use require statements to revert state when conditions are not met. Use super to concatenate validations.
* @param _beneficiary Address performing the token purchase
* @param _weiAmount Value in wei involved in the purchase
*/
function _preValidatePurchase(address _beneficiary, uint256 _weiAmount) internal {
require(_beneficiary != address(0));
require(_weiAmount != 0);
}
/**
* @dev Validation of an executed purchase. Observe state and use revert statements to undo rollback when valid conditions are not met.
* @param _beneficiary Address performing the token purchase
* @param _weiAmount Value in wei involved in the purchase
*/
function _postValidatePurchase(address _beneficiary, uint256 _weiAmount) internal {
// optional override
}
/**
* @dev Source of tokens. Override this method to modify the way in which the crowdsale ultimately gets and sends its tokens.
* @param _beneficiary Address performing the token purchase
* @param _tokenAmount Number of tokens to be emitted
*/
function _deliverTokens(address _beneficiary, uint256 _tokenAmount) internal {
token.transfer(_beneficiary, _tokenAmount);
}
/**
* @dev Executed when a purchase has been validated and is ready to be executed. Not necessarily emits/sends tokens.
* @param _beneficiary Address receiving the tokens
* @param _tokenAmount Number of tokens to be purchased
*/
function _processPurchase(address _beneficiary, uint256 _tokenAmount) internal {
_deliverTokens(_beneficiary, _tokenAmount);
}
/**
* @dev Override for extensions that require an internal state to check for validity (current user contributions, etc.)
* @param _beneficiary Address receiving the tokens
* @param _weiAmount Value in wei involved in the purchase
*/
function _updatePurchasingState(address _beneficiary, uint256 _weiAmount) internal {
// optional override
}
/**
* @dev Override to extend the way in which ether is converted to tokens.
* @param _weiAmount Value in wei to be converted into tokens
* @return Number of tokens that can be purchased with the specified _weiAmount
*/
function _getTokenAmount(uint256 _weiAmount) internal view returns (uint256) {
return _weiAmount.mul(rate);
}
/**
* @dev Determines how ETH is stored/forwarded on purchases.
*/
function _forwardFunds() internal {
wallet.transfer(msg.value);
}
}
pragma solidity ^0.4.21;
import "../../math/SafeMath.sol";
import "../../ownership/Ownable.sol";
import "../validation/TimedCrowdsale.sol";
/**
* @title FinalizableCrowdsale
* @dev Extension of Crowdsale where an owner can do extra work
* after finishing.
*/
contract FinalizableCrowdsale is TimedCrowdsale, Ownable {
using SafeMath for uint256;
bool public isFinalized = false;
event Finalized();
/**
* @dev Must be called after crowdsale ends, to do some extra finalization
* work. Calls the contract's finalization function.
*/
function finalize() onlyOwner public {
require(!isFinalized);
require(hasClosed());
finalization();
emit 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 {
}
}
pragma solidity ^0.4.21;
import "../validation/TimedCrowdsale.sol";
import "../../token/ERC20/ERC20.sol";
import "../../math/SafeMath.sol";
/**
* @title PostDeliveryCrowdsale
* @dev Crowdsale that locks tokens from withdrawal until it ends.
*/
contract PostDeliveryCrowdsale is TimedCrowdsale {
using SafeMath for uint256;
mapping(address => uint256) public balances;
/**
* @dev Withdraw tokens only after crowdsale ends.
*/
function withdrawTokens() public {
require(hasClosed());
uint256 amount = balances[msg.sender];
require(amount > 0);
balances[msg.sender] = 0;
_deliverTokens(msg.sender, amount);
}
/**
* @dev Overrides parent by storing balances instead of issuing tokens right away.
* @param _beneficiary Token purchaser
* @param _tokenAmount Amount of tokens purchased
*/
function _processPurchase(address _beneficiary, uint256 _tokenAmount) internal {
balances[_beneficiary] = balances[_beneficiary].add(_tokenAmount);
}
}
pragma solidity ^0.4.21;
import "../../math/SafeMath.sol";
import "./FinalizableCrowdsale.sol";
import "./utils/RefundVault.sol";
/**
* @title RefundableCrowdsale
* @dev Extension of Crowdsale contract that adds a funding goal, and
* the possibility of users getting a refund if goal is not met.
* Uses a RefundVault as the crowdsale's vault.
*/
contract RefundableCrowdsale is FinalizableCrowdsale {
using SafeMath for uint256;
// minimum amount of funds to be raised in weis
uint256 public goal;
// refund vault used to hold funds while crowdsale is running
RefundVault public vault;
/**
* @dev Constructor, creates RefundVault.
* @param _goal Funding goal
*/
function RefundableCrowdsale(uint256 _goal) public {
require(_goal > 0);
vault = new RefundVault(wallet);
goal = _goal;
}
/**
* @dev Investors can claim refunds here if crowdsale is unsuccessful
*/
function claimRefund() public {
require(isFinalized);
require(!goalReached());
vault.refund(msg.sender);
}
/**
* @dev Checks whether funding goal was reached.
* @return Whether funding goal was reached
*/
function goalReached() public view returns (bool) {
return weiRaised >= goal;
}
/**
* @dev vault finalization task, called when owner calls finalize()
*/
function finalization() internal {
if (goalReached()) {
vault.close();
} else {
vault.enableRefunds();
}
super.finalization();
}
/**
* @dev Overrides Crowdsale fund forwarding, sending funds to vault.
*/
function _forwardFunds() internal {
vault.deposit.value(msg.value)(msg.sender);
}
}
pragma solidity ^0.4.21;
import "../../../math/SafeMath.sol";
import "../../../ownership/Ownable.sol";
/**
* @title RefundVault
* @dev This contract is used for storing funds while a crowdsale
* is in progress. Supports refunding the money if crowdsale fails,
* and forwarding it if crowdsale is successful.
*/
contract RefundVault is Ownable {
using SafeMath for uint256;
enum State { Active, Refunding, Closed }
mapping (address => uint256) public deposited;
address public wallet;
State public state;
event Closed();
event RefundsEnabled();
event Refunded(address indexed beneficiary, uint256 weiAmount);
/**
* @param _wallet Vault address
*/
function RefundVault(address _wallet) public {
require(_wallet != address(0));
wallet = _wallet;
state = State.Active;
}
/**
* @param investor Investor address
*/
function deposit(address investor) onlyOwner public payable {
require(state == State.Active);
deposited[investor] = deposited[investor].add(msg.value);
}
function close() onlyOwner public {
require(state == State.Active);
state = State.Closed;
emit Closed();
wallet.transfer(address(this).balance);
}
function enableRefunds() onlyOwner public {
require(state == State.Active);
state = State.Refunding;
emit RefundsEnabled();
}
/**
* @param investor Investor address
*/
function refund(address investor) public {
require(state == State.Refunding);
uint256 depositedValue = deposited[investor];
deposited[investor] = 0;
investor.transfer(depositedValue);
emit Refunded(investor, depositedValue);
}
}
pragma solidity ^0.4.21;
import "../Crowdsale.sol";
import "../../token/ERC20/ERC20.sol";
import "../../math/SafeMath.sol";
/**
* @title AllowanceCrowdsale
* @dev Extension of Crowdsale where tokens are held by a wallet, which approves an allowance to the crowdsale.
*/
contract AllowanceCrowdsale is Crowdsale {
using SafeMath for uint256;
address public tokenWallet;
/**
* @dev Constructor, takes token wallet address.
* @param _tokenWallet Address holding the tokens, which has approved allowance to the crowdsale
*/
function AllowanceCrowdsale(address _tokenWallet) public {
require(_tokenWallet != address(0));
tokenWallet = _tokenWallet;
}
/**
* @dev Checks the amount of tokens left in the allowance.
* @return Amount of tokens left in the allowance
*/
function remainingTokens() public view returns (uint256) {
return token.allowance(tokenWallet, this);
}
/**
* @dev Overrides parent behavior by transferring tokens from wallet.
* @param _beneficiary Token purchaser
* @param _tokenAmount Amount of tokens purchased
*/
function _deliverTokens(address _beneficiary, uint256 _tokenAmount) internal {
token.transferFrom(tokenWallet, _beneficiary, _tokenAmount);
}
}
pragma solidity ^0.4.21;
import "../Crowdsale.sol";
import "../../token/ERC20/MintableToken.sol";
/**
* @title MintedCrowdsale
* @dev Extension of Crowdsale contract whose tokens are minted in each purchase.
* Token ownership should be transferred to MintedCrowdsale for minting.
*/
contract MintedCrowdsale is Crowdsale {
/**
* @dev Overrides delivery by minting tokens upon purchase.
* @param _beneficiary Token purchaser
* @param _tokenAmount Number of tokens to be minted
*/
function _deliverTokens(address _beneficiary, uint256 _tokenAmount) internal {
require(MintableToken(token).mint(_beneficiary, _tokenAmount));
}
}
pragma solidity ^0.4.21;
import "../validation/TimedCrowdsale.sol";
import "../../math/SafeMath.sol";
/**
* @title IncreasingPriceCrowdsale
* @dev Extension of Crowdsale contract that increases the price of tokens linearly in time.
* Note that what should be provided to the constructor is the initial and final _rates_, that is,
* the amount of tokens per wei contributed. Thus, the initial rate must be greater than the final rate.
*/
contract IncreasingPriceCrowdsale is TimedCrowdsale {
using SafeMath for uint256;
uint256 public initialRate;
uint256 public finalRate;
/**
* @dev Constructor, takes intial and final rates of tokens received per wei contributed.
* @param _initialRate Number of tokens a buyer gets per wei at the start of the crowdsale
* @param _finalRate Number of tokens a buyer gets per wei at the end of the crowdsale
*/
function IncreasingPriceCrowdsale(uint256 _initialRate, uint256 _finalRate) public {
require(_initialRate >= _finalRate);
require(_finalRate > 0);
initialRate = _initialRate;
finalRate = _finalRate;
}
/**
* @dev Returns the rate of tokens per wei at the present time.
* Note that, as price _increases_ with time, the rate _decreases_.
* @return The number of tokens a buyer gets per wei at a given time
*/
function getCurrentRate() public view returns (uint256) {
// solium-disable-next-line security/no-block-members
uint256 elapsedTime = block.timestamp.sub(openingTime);
uint256 timeRange = closingTime.sub(openingTime);
uint256 rateRange = initialRate.sub(finalRate);
return initialRate.sub(elapsedTime.mul(rateRange).div(timeRange));
}
/**
* @dev Overrides parent method taking into account variable rate.
* @param _weiAmount The value in wei to be converted into tokens
* @return The number of tokens _weiAmount wei will buy at present time
*/
function _getTokenAmount(uint256 _weiAmount) internal view returns (uint256) {
uint256 currentRate = getCurrentRate();
return currentRate.mul(_weiAmount);
}
}
pragma solidity ^0.4.21;
import "../../math/SafeMath.sol";
import "../Crowdsale.sol";
/**
* @title CappedCrowdsale
* @dev Crowdsale with a limit for total contributions.
*/
contract CappedCrowdsale is Crowdsale {
using SafeMath for uint256;
uint256 public cap;
/**
* @dev Constructor, takes maximum amount of wei accepted in the crowdsale.
* @param _cap Max amount of wei to be contributed
*/
function CappedCrowdsale(uint256 _cap) public {
require(_cap > 0);
cap = _cap;
}
/**
* @dev Checks whether the cap has been reached.
* @return Whether the cap was reached
*/
function capReached() public view returns (bool) {
return weiRaised >= cap;
}
/**
* @dev Extend parent behavior requiring purchase to respect the funding cap.
* @param _beneficiary Token purchaser
* @param _weiAmount Amount of wei contributed
*/
function _preValidatePurchase(address _beneficiary, uint256 _weiAmount) internal {
super._preValidatePurchase(_beneficiary, _weiAmount);
require(weiRaised.add(_weiAmount) <= cap);
}
}
pragma solidity ^0.4.21;
import "../../math/SafeMath.sol";
import "../Crowdsale.sol";
import "../../ownership/Ownable.sol";
/**
* @title IndividuallyCappedCrowdsale
* @dev Crowdsale with per-user caps.
*/
contract IndividuallyCappedCrowdsale is Crowdsale, Ownable {
using SafeMath for uint256;
mapping(address => uint256) public contributions;
mapping(address => uint256) public caps;
/**
* @dev Sets a specific user's maximum contribution.
* @param _beneficiary Address to be capped
* @param _cap Wei limit for individual contribution
*/
function setUserCap(address _beneficiary, uint256 _cap) external onlyOwner {
caps[_beneficiary] = _cap;
}
/**
* @dev Sets a group of users' maximum contribution.
* @param _beneficiaries List of addresses to be capped
* @param _cap Wei limit for individual contribution
*/
function setGroupCap(address[] _beneficiaries, uint256 _cap) external onlyOwner {
for (uint256 i = 0; i < _beneficiaries.length; i++) {
caps[_beneficiaries[i]] = _cap;
}
}
/**
* @dev Returns the cap of a specific user.
* @param _beneficiary Address whose cap is to be checked
* @return Current cap for individual user
*/
function getUserCap(address _beneficiary) public view returns (uint256) {
return caps[_beneficiary];
}
/**
* @dev Returns the amount contributed so far by a sepecific user.
* @param _beneficiary Address of contributor
* @return User contribution so far
*/
function getUserContribution(address _beneficiary) public view returns (uint256) {
return contributions[_beneficiary];
}
/**
* @dev Extend parent behavior requiring purchase to respect the user's funding cap.
* @param _beneficiary Token purchaser
* @param _weiAmount Amount of wei contributed
*/
function _preValidatePurchase(address _beneficiary, uint256 _weiAmount) internal {
super._preValidatePurchase(_beneficiary, _weiAmount);
require(contributions[_beneficiary].add(_weiAmount) <= caps[_beneficiary]);
}
/**
* @dev Extend parent behavior to update user contributions
* @param _beneficiary Token purchaser
* @param _weiAmount Amount of wei contributed
*/
function _updatePurchasingState(address _beneficiary, uint256 _weiAmount) internal {
super._updatePurchasingState(_beneficiary, _weiAmount);
contributions[_beneficiary] = contributions[_beneficiary].add(_weiAmount);
}
}
pragma solidity ^0.4.21;
import "../../math/SafeMath.sol";
import "../Crowdsale.sol";
/**
* @title TimedCrowdsale
* @dev Crowdsale accepting contributions only within a time frame.
*/
contract TimedCrowdsale is Crowdsale {
using SafeMath for uint256;
uint256 public openingTime;
uint256 public closingTime;
/**
* @dev Reverts if not in crowdsale time range.
*/
modifier onlyWhileOpen {
// solium-disable-next-line security/no-block-members
require(block.timestamp >= openingTime && block.timestamp <= closingTime);
_;
}
/**
* @dev Constructor, takes crowdsale opening and closing times.
* @param _openingTime Crowdsale opening time
* @param _closingTime Crowdsale closing time
*/
function TimedCrowdsale(uint256 _openingTime, uint256 _closingTime) public {
// solium-disable-next-line security/no-block-members
require(_openingTime >= block.timestamp);
require(_closingTime >= _openingTime);
openingTime = _openingTime;
closingTime = _closingTime;
}
/**
* @dev Checks whether the period in which the crowdsale is open has already elapsed.
* @return Whether crowdsale period has elapsed
*/
function hasClosed() public view returns (bool) {
// solium-disable-next-line security/no-block-members
return block.timestamp > closingTime;
}
/**
* @dev Extend parent behavior requiring to be within contributing period
* @param _beneficiary Token purchaser
* @param _weiAmount Amount of wei contributed
*/
function _preValidatePurchase(address _beneficiary, uint256 _weiAmount) internal onlyWhileOpen {
super._preValidatePurchase(_beneficiary, _weiAmount);
}
}
pragma solidity ^0.4.21;
import "../Crowdsale.sol";
import "../../ownership/Ownable.sol";
/**
* @title WhitelistedCrowdsale
* @dev Crowdsale in which only whitelisted users can contribute.
*/
contract WhitelistedCrowdsale is Crowdsale, Ownable {
mapping(address => bool) public whitelist;
/**
* @dev Reverts if beneficiary is not whitelisted. Can be used when extending this contract.
*/
modifier isWhitelisted(address _beneficiary) {
require(whitelist[_beneficiary]);
_;
}
/**
* @dev Adds single address to whitelist.
* @param _beneficiary Address to be added to the whitelist
*/
function addToWhitelist(address _beneficiary) external onlyOwner {
whitelist[_beneficiary] = true;
}
/**
* @dev Adds list of addresses to whitelist. Not overloaded due to limitations with truffle testing.
* @param _beneficiaries Addresses to be added to the whitelist
*/
function addManyToWhitelist(address[] _beneficiaries) external onlyOwner {
for (uint256 i = 0; i < _beneficiaries.length; i++) {
whitelist[_beneficiaries[i]] = true;
}
}
/**
* @dev Removes single address from whitelist.
* @param _beneficiary Address to be removed to the whitelist
*/
function removeFromWhitelist(address _beneficiary) external onlyOwner {
whitelist[_beneficiary] = false;
}
/**
* @dev Extend parent behavior requiring beneficiary to be in whitelist.
* @param _beneficiary Token beneficiary
* @param _weiAmount Amount of wei contributed
*/
function _preValidatePurchase(address _beneficiary, uint256 _weiAmount) internal isWhitelisted(_beneficiary) {
super._preValidatePurchase(_beneficiary, _weiAmount);
}
}
pragma solidity ^0.4.21;
import "../crowdsale/validation/CappedCrowdsale.sol";
import "../crowdsale/distribution/RefundableCrowdsale.sol";
import "../crowdsale/emission/MintedCrowdsale.sol";
import "../token/ERC20/MintableToken.sol";
/**
* @title SampleCrowdsaleToken
* @dev Very simple ERC20 Token that can be minted.
* It is meant to be used in a crowdsale contract.
*/
contract SampleCrowdsaleToken is MintableToken {
string public constant name = "Sample Crowdsale Token"; // solium-disable-line uppercase
string public constant symbol = "SCT"; // solium-disable-line uppercase
uint8 public constant decimals = 18; // solium-disable-line uppercase
}
/**
* @title SampleCrowdsale
* @dev This is an example of a fully fledged crowdsale.
* The way to add new features to a base crowdsale is by multiple inheritance.
* In this example we are providing following extensions:
* CappedCrowdsale - sets a max boundary for raised funds
* RefundableCrowdsale - set a min goal to be reached and returns funds if it's not met
*
* After adding multiple features it's good practice to run integration tests
* to ensure that subcontracts works together as intended.
*/
contract SampleCrowdsale is CappedCrowdsale, RefundableCrowdsale, MintedCrowdsale {
function SampleCrowdsale(
uint256 _openingTime,
uint256 _closingTime,
uint256 _rate,
address _wallet,
uint256 _cap,
MintableToken _token,
uint256 _goal
)
public
Crowdsale(_rate, _wallet, _token)
CappedCrowdsale(_cap)
TimedCrowdsale(_openingTime, _closingTime)
RefundableCrowdsale(_goal)
{
//As goal needs to be met for a successful crowdsale
//the value needs to less or equal than a cap which is limit for accepted funds
require(_goal <= _cap);
}
}
pragma solidity ^0.4.21;
import "../ownership/Heritable.sol";
/**
* @title SimpleSavingsWallet
* @dev Simplest form of savings wallet whose ownership can be claimed by a heir
* if owner dies.
* In this example, we take a very simple savings wallet providing two operations
* (to send and receive funds) and extend its capabilities by making it Heritable.
* The account that creates the contract is set as owner, who has the authority to
* choose an heir account. Heir account can reclaim the contract ownership in the
* case that the owner dies.
*/
contract SimpleSavingsWallet is Heritable {
event Sent(address indexed payee, uint256 amount, uint256 balance);
event Received(address indexed payer, uint256 amount, uint256 balance);
function SimpleSavingsWallet(uint256 _heartbeatTimeout) Heritable(_heartbeatTimeout) public {}
/**
* @dev wallet can receive funds.
*/
function () public payable {
emit Received(msg.sender, msg.value, address(this).balance);
}
/**
* @dev wallet can send funds
*/
function sendTo(address payee, uint256 amount) public onlyOwner {
require(payee != 0 && payee != address(this));
require(amount > 0);
payee.transfer(amount);
emit Sent(payee, amount, address(this).balance);
}
}
pragma solidity ^0.4.21;
import "../token/ERC20/StandardToken.sol";
/**
* @title SimpleToken
* @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator.
* Note they can later distribute these tokens as they wish using `transfer` and other
* `StandardToken` functions.
*/
contract SimpleToken is StandardToken {
string public constant name = "SimpleToken"; // solium-disable-line uppercase
string public constant symbol = "SIM"; // solium-disable-line uppercase
uint8 public constant decimals = 18; // solium-disable-line uppercase
uint256 public constant INITIAL_SUPPLY = 10000 * (10 ** uint256(decimals));
/**
* @dev Constructor that gives msg.sender all of existing tokens.
*/
function SimpleToken() public {
totalSupply_ = INITIAL_SUPPLY;
balances[msg.sender] = INITIAL_SUPPLY;
emit Transfer(0x0, msg.sender, INITIAL_SUPPLY);
}
}
pragma solidity ^0.4.21;
import "../ownership/Ownable.sol";
/**
* @title Destructible
* @dev Base contract that can be destroyed by owner. All funds in contract will be sent to the owner.
*/
contract Destructible is Ownable {
function Destructible() public payable { }
/**
* @dev Transfers the current balance to the owner and terminates the contract.
*/
function destroy() onlyOwner public {
selfdestruct(owner);
}
function destroyAndSend(address _recipient) onlyOwner public {
selfdestruct(_recipient);
}
}
pragma solidity ^0.4.21;
import "../ownership/Ownable.sol";
import "../token/ERC20/ERC20Basic.sol";
/**
* @title TokenDestructible:
* @author Remco Bloemen <remco@2π.com>
* @dev Base contract that can be destroyed by owner. All funds in contract including
* listed tokens will be sent to the owner.
*/
contract TokenDestructible is Ownable {
function TokenDestructible() public payable { }
/**
* @notice Terminate contract and refund to owner
* @param tokens List of addresses of ERC20 or ERC20Basic token contracts to
refund.
* @notice The called token contracts could try to re-enter this contract. Only
supply token contracts you trust.
*/
function destroy(address[] tokens) onlyOwner public {
// Transfer tokens to owner
for (uint256 i = 0; i < tokens.length; i++) {
ERC20Basic token = ERC20Basic(tokens[i]);
uint256 balance = token.balanceOf(this);
token.transfer(owner, balance);
}
// Transfer Eth to owner and terminate contract
selfdestruct(owner);
}
}
pragma solidity ^0.4.21;
/**
* @title Math
* @dev Assorted math operations
*/
library Math {
function max64(uint64 a, uint64 b) internal pure returns (uint64) {
return a >= b ? a : b;
}
function min64(uint64 a, uint64 b) internal pure returns (uint64) {
return a < b ? a : b;
}
function max256(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
function min256(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
}
pragma solidity ^0.4.21;
import "../token/ERC20/ERC20.sol";
import "../crowdsale/emission/AllowanceCrowdsale.sol";
contract AllowanceCrowdsaleImpl is AllowanceCrowdsale {
function AllowanceCrowdsaleImpl (
uint256 _rate,
address _wallet,
ERC20 _token,
address _tokenWallet
)
public
Crowdsale(_rate, _wallet, _token)
AllowanceCrowdsale(_tokenWallet)
{
}
}
pragma solidity ^0.4.18;
import "../access/SignatureBouncer.sol";
contract SignatureBouncerMock is SignatureBouncer {
function checkValidSignature(address _address, bytes _sig)
public
view
returns (bool)
{
return isValidSignature(_address, _sig);
}
function onlyWithValidSignature(bytes _sig)
onlyValidSignature(_sig)
public
view
{
}
}
pragma solidity ^0.4.21;
import "../token/ERC20/ERC20.sol";
import "../crowdsale/validation/CappedCrowdsale.sol";
contract CappedCrowdsaleImpl is CappedCrowdsale {
function CappedCrowdsaleImpl (
uint256 _rate,
address _wallet,
ERC20 _token,
uint256 _cap
)
public
Crowdsale(_rate, _wallet, _token)
CappedCrowdsale(_cap)
{
}
}
pragma solidity ^0.4.21;
import "../../contracts/DayLimit.sol";
contract DayLimitMock is DayLimit {
uint256 public totalSpending;
function DayLimitMock(uint256 _value) public DayLimit(_value) {
totalSpending = 0;
}
function attemptSpend(uint256 _value) external limitedDaily(_value) {
totalSpending += _value;
}
function setDailyLimit(uint256 _newLimit) external {
_setDailyLimit(_newLimit);
}
function resetSpentToday() external {
_resetSpentToday();
}
}
pragma solidity ^0.4.21;
import "../ECRecovery.sol";
contract ECRecoveryMock {
using ECRecovery for bytes32;
function recover(bytes32 hash, bytes sig)
public
pure
returns (address)
{
return hash.recover(sig);
}
function toEthSignedMessageHash(bytes32 hash)
public
pure
returns (bytes32)
{
return hash.toEthSignedMessageHash();
}
}
pragma solidity ^0.4.21;
import "../token/ERC20/BasicToken.sol";
contract ERC223ContractInterface {
function tokenFallback(address _from, uint256 _value, bytes _data) external;
}
contract ERC223TokenMock is BasicToken {
function ERC223TokenMock(address initialAccount, uint256 initialBalance) public {
balances[initialAccount] = initialBalance;
totalSupply_ = initialBalance;
}
// ERC223 compatible transfer function (except the name)
function transferERC223(address _to, uint256 _value, bytes _data) public
returns (bool success)
{
transfer(_to, _value);
bool isContract = false;
// solium-disable-next-line security/no-inline-assembly
assembly {
isContract := not(iszero(extcodesize(_to)))
}
if (isContract) {
ERC223ContractInterface receiver = ERC223ContractInterface(_to);
receiver.tokenFallback(msg.sender, _value, _data);
}
return true;
}
}
pragma solidity ^0.4.21;
import "../token/ERC827/ERC827Token.sol";
// mock class using ERC827 Token
contract ERC827TokenMock is ERC827Token {
function ERC827TokenMock(address initialAccount, uint256 initialBalance) public {
balances[initialAccount] = initialBalance;
totalSupply_ = initialBalance;
}
}
pragma solidity ^0.4.21;
import "../token/ERC20/MintableToken.sol";
import "../crowdsale/distribution/FinalizableCrowdsale.sol";
contract FinalizableCrowdsaleImpl is FinalizableCrowdsale {
function FinalizableCrowdsaleImpl (
uint256 _openingTime,
uint256 _closingTime,
uint256 _rate,
address _wallet,
MintableToken _token
)
public
Crowdsale(_rate, _wallet, _token)
TimedCrowdsale(_openingTime, _closingTime)
{
}
}
pragma solidity ^0.4.21;
// @title Force Ether into a contract.
// @notice even
// if the contract is not payable.
// @notice To use, construct the contract with the target as argument.
// @author Remco Bloemen <remco@neufund.org>
contract ForceEther {
function ForceEther() public payable { }
function destroyAndSend(address _recipient) public {
selfdestruct(_recipient);
}
}
pragma solidity ^0.4.21;
import "../../contracts/ownership/HasNoEther.sol";
contract HasNoEtherTest is HasNoEther {
// Constructor with explicit payable — should still fail
function HasNoEtherTest() public payable {
}
}
pragma solidity ^0.4.21;
import "../crowdsale/price/IncreasingPriceCrowdsale.sol";
import "../math/SafeMath.sol";
contract IncreasingPriceCrowdsaleImpl is IncreasingPriceCrowdsale {
function IncreasingPriceCrowdsaleImpl (
uint256 _openingTime,
uint256 _closingTime,
address _wallet,
ERC20 _token,
uint256 _initialRate,
uint256 _finalRate
)
public
Crowdsale(_initialRate, _wallet, _token)
TimedCrowdsale(_openingTime, _closingTime)
IncreasingPriceCrowdsale(_initialRate, _finalRate)
{
}
}
pragma solidity ^0.4.21;
import "../token/ERC20/ERC20.sol";
import "../crowdsale/validation/IndividuallyCappedCrowdsale.sol";
contract IndividuallyCappedCrowdsaleImpl is IndividuallyCappedCrowdsale {
function IndividuallyCappedCrowdsaleImpl (
uint256 _rate,
address _wallet,
ERC20 _token
)
public
Crowdsale(_rate, _wallet, _token)
{
}
}
pragma solidity ^0.4.21;
import {Bounty, Target} from "../../contracts/Bounty.sol";
contract InsecureTargetMock is Target {
function checkInvariant() public returns(bool) {
return false;
}
}
contract InsecureTargetBounty is Bounty {
function deployContract() internal returns (address) {
return new InsecureTargetMock();
}
}
pragma solidity ^0.4.21;
import "../LimitBalance.sol";
// mock class using LimitBalance
contract LimitBalanceMock is LimitBalance(1000) {
function limitedDeposit() public payable limitedPayable {
}
}
pragma solidity ^0.4.21;
import { MerkleProof } from "../MerkleProof.sol";
contract MerkleProofWrapper {
function verifyProof(bytes32[] _proof, bytes32 _root, bytes32 _leaf) public pure returns (bool) {
return MerkleProof.verifyProof(_proof, _root, _leaf);
}
}
pragma solidity ^0.4.21;
contract MessageHelper {
event Show(bytes32 b32, uint256 number, string text);
function showMessage( bytes32 message, uint256 number, string text ) public returns (bool) {
emit Show(message, number, text);
return true;
}
function fail() public {
require(false);
}
function call(address to, bytes data) public returns (bool) {
// solium-disable-next-line security/no-low-level-calls
if (to.call(data))
return true;
else
return false;
}
}
pragma solidity ^0.4.21;
import "../token/ERC20/MintableToken.sol";
import "../crowdsale/emission/MintedCrowdsale.sol";
contract MintedCrowdsaleImpl is MintedCrowdsale {
function MintedCrowdsaleImpl (
uint256 _rate,
address _wallet,
MintableToken _token
)
public
Crowdsale(_rate, _wallet, _token)
{
}
}
pragma solidity ^0.4.21;
import "../token/ERC20/ERC20.sol";
import "../crowdsale/distribution/PostDeliveryCrowdsale.sol";
contract PostDeliveryCrowdsaleImpl is PostDeliveryCrowdsale {
function PostDeliveryCrowdsaleImpl (
uint256 _openingTime,
uint256 _closingTime,
uint256 _rate,
address _wallet,
ERC20 _token
)
public
TimedCrowdsale(_openingTime, _closingTime)
Crowdsale(_rate, _wallet, _token)
{
}
}
pragma solidity ^0.4.21;
import "../payment/PullPayment.sol";
// mock class using PullPayment
contract PullPaymentMock is PullPayment {
function PullPaymentMock() public payable { }
// test helper function to call asyncSend
function callSend(address dest, uint256 amount) public {
asyncSend(dest, amount);
}
}
pragma solidity ^0.4.21;
import "../ownership/rbac/RBACWithAdmin.sol";
contract RBACMock is RBACWithAdmin {
string constant ROLE_ADVISOR = "advisor";
modifier onlyAdminOrAdvisor()
{
require(
hasRole(msg.sender, ROLE_ADMIN) ||
hasRole(msg.sender, ROLE_ADVISOR)
);
_;
}
function RBACMock(address[] _advisors)
public
{
addRole(msg.sender, ROLE_ADVISOR);
for (uint256 i = 0; i < _advisors.length; i++) {
addRole(_advisors[i], ROLE_ADVISOR);
}
}
function onlyAdminsCanDoThis()
onlyAdmin
view
external
{
}
function onlyAdvisorsCanDoThis()
onlyRole(ROLE_ADVISOR)
view
external
{
}
function eitherAdminOrAdvisorCanDoThis()
onlyAdminOrAdvisor
view
external
{
}
function nobodyCanDoThis()
onlyRole("unknown")
view
external
{
}
// admins can remove advisor's role
function removeAdvisor(address _addr)
onlyAdmin
public
{
// revert if the user isn't an advisor
// (perhaps you want to soft-fail here instead?)
checkRole(_addr, ROLE_ADVISOR);
// remove the advisor's role
removeRole(_addr, ROLE_ADVISOR);
}
}
pragma solidity ^0.4.21;
contract ReentrancyAttack {
function callSender(bytes4 data) public {
// solium-disable-next-line security/no-low-level-calls
require(msg.sender.call(data));
}
}
pragma solidity ^0.4.21;
import "../ReentrancyGuard.sol";
import "./ReentrancyAttack.sol";
contract ReentrancyMock is ReentrancyGuard {
uint256 public counter;
function ReentrancyMock() public {
counter = 0;
}
function callback() external nonReentrant {
count();
}
function countLocalRecursive(uint256 n) public nonReentrant {
if (n > 0) {
count();
countLocalRecursive(n - 1);
}
}
function countThisRecursive(uint256 n) public nonReentrant {
bytes4 func = bytes4(keccak256("countThisRecursive(uint256)"));
if (n > 0) {
count();
// solium-disable-next-line security/no-low-level-calls
bool result = address(this).call(func, n - 1);
require(result == true);
}
}
function countAndCall(ReentrancyAttack attacker) public nonReentrant {
count();
bytes4 func = bytes4(keccak256("callback()"));
attacker.callSender(func);
}
function count() private {
counter += 1;
}
}
pragma solidity ^0.4.21;
import "../token/ERC20/MintableToken.sol";
import "../crowdsale/distribution/RefundableCrowdsale.sol";
contract RefundableCrowdsaleImpl is RefundableCrowdsale {
function RefundableCrowdsaleImpl (
uint256 _openingTime,
uint256 _closingTime,
uint256 _rate,
address _wallet,
MintableToken _token,
uint256 _goal
)
public
Crowdsale(_rate, _wallet, _token)
TimedCrowdsale(_openingTime, _closingTime)
RefundableCrowdsale(_goal)
{
}
}
pragma solidity ^0.4.21;
import {Bounty, Target} from "../../contracts/Bounty.sol";
contract SecureTargetMock is Target {
function checkInvariant() public returns(bool) {
return true;
}
}
contract SecureTargetBounty is Bounty {
function deployContract() internal returns (address) {
return new SecureTargetMock();
}
}
pragma solidity ^0.4.21;
import "../token/ERC20/ERC20.sol";
import "../crowdsale/validation/TimedCrowdsale.sol";
contract TimedCrowdsaleImpl is TimedCrowdsale {
function TimedCrowdsaleImpl (
uint256 _openingTime,
uint256 _closingTime,
uint256 _rate,
address _wallet,
ERC20 _token
)
public
Crowdsale(_rate, _wallet, _token)
TimedCrowdsale(_openingTime, _closingTime)
{
}
}
pragma solidity ^0.4.21;
import "../ownership/Whitelist.sol";
contract WhitelistMock is Whitelist {
function onlyWhitelistedCanDoThis()
onlyWhitelisted
view
external
{
}
}
pragma solidity ^0.4.21;
import "../token/ERC20/ERC20.sol";
import "../crowdsale/validation/WhitelistedCrowdsale.sol";
contract WhitelistedCrowdsaleImpl is WhitelistedCrowdsale {
function WhitelistedCrowdsaleImpl (
uint256 _rate,
address _wallet,
ERC20 _token
)
public
Crowdsale(_rate, _wallet, _token)
{
}
}
pragma solidity ^0.4.21;
import "./Ownable.sol";
import "../token/ERC20/ERC20Basic.sol";
import "../token/ERC20/SafeERC20.sol";
/**
* @title Contracts that should be able to recover tokens
* @author SylTi
* @dev This allow a contract to recover any ERC20 token received in a contract by transferring the balance to the contract owner.
* This will prevent any accidental loss of tokens.
*/
contract CanReclaimToken is Ownable {
using SafeERC20 for ERC20Basic;
/**
* @dev Reclaim all ERC20Basic compatible tokens
* @param token ERC20Basic The address of the token contract
*/
function reclaimToken(ERC20Basic token) external onlyOwner {
uint256 balance = token.balanceOf(this);
token.safeTransfer(owner, balance);
}
}
pragma solidity ^0.4.21;
import "./Ownable.sol";
/**
* @title Claimable
* @dev Extension for the Ownable contract, where the ownership needs to be claimed.
* This allows the new owner to accept the transfer.
*/
contract Claimable is Ownable {
address public pendingOwner;
/**
* @dev Modifier throws if called by any account other than the pendingOwner.
*/
modifier onlyPendingOwner() {
require(msg.sender == pendingOwner);
_;
}
/**
* @dev Allows the current owner to set the pendingOwner address.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) onlyOwner public {
pendingOwner = newOwner;
}
/**
* @dev Allows the pendingOwner address to finalize the transfer.
*/
function claimOwnership() onlyPendingOwner public {
emit OwnershipTransferred(owner, pendingOwner);
owner = pendingOwner;
pendingOwner = address(0);
}
}
pragma solidity ^0.4.21;
import "./Ownable.sol";
/**
* @title Contactable token
* @dev Basic version of a contactable contract, allowing the owner to provide a string with their
* contact information.
*/
contract Contactable is Ownable {
string public contactInformation;
/**
* @dev Allows the owner to set a string with their contact information.
* @param info The contact information to attach to the contract.
*/
function setContactInformation(string info) onlyOwner public {
contactInformation = info;
}
}
pragma solidity ^0.4.21;
import "./Claimable.sol";
/**
* @title DelayedClaimable
* @dev Extension for the Claimable contract, where the ownership needs to be claimed before/after
* a certain block number.
*/
contract DelayedClaimable is Claimable {
uint256 public end;
uint256 public start;
/**
* @dev Used to specify the time period during which a pending
* owner can claim ownership.
* @param _start The earliest time ownership can be claimed.
* @param _end The latest time ownership can be claimed.
*/
function setLimits(uint256 _start, uint256 _end) onlyOwner public {
require(_start <= _end);
end = _end;
start = _start;
}
/**
* @dev Allows the pendingOwner address to finalize the transfer, as long as it is called within
* the specified start and end time.
*/
function claimOwnership() onlyPendingOwner public {
require((block.number <= end) && (block.number >= start));
emit OwnershipTransferred(owner, pendingOwner);
owner = pendingOwner;
pendingOwner = address(0);
end = 0;
}
}
pragma solidity ^0.4.21;
import "./Ownable.sol";
/**
* @title Contracts that should not own Contracts
* @author Remco Bloemen <remco@2π.com>
* @dev Should contracts (anything Ownable) end up being owned by this contract, it allows the owner
* of this contract to reclaim ownership of the contracts.
*/
contract HasNoContracts is Ownable {
/**
* @dev Reclaim ownership of Ownable contracts
* @param contractAddr The address of the Ownable to be reclaimed.
*/
function reclaimContract(address contractAddr) external onlyOwner {
Ownable contractInst = Ownable(contractAddr);
contractInst.transferOwnership(owner);
}
}
pragma solidity ^0.4.21;
import "./Ownable.sol";
/**
* @title Contracts that should not own Ether
* @author Remco Bloemen <remco@2π.com>
* @dev This tries to block incoming ether to prevent accidental loss of Ether. Should Ether end up
* in the contract, it will allow the owner to reclaim this ether.
* @notice Ether can still be send to this contract by:
* calling functions labeled `payable`
* `selfdestruct(contract_address)`
* mining directly to the contract address
*/
contract HasNoEther is Ownable {
/**
* @dev Constructor that rejects incoming Ether
* @dev The `payable` flag is added so we can access `msg.value` without compiler warning. If we
* leave out payable, then Solidity will allow inheriting contracts to implement a payable
* constructor. By doing it this way we prevent a payable constructor from working. Alternatively
* we could use assembly to access msg.value.
*/
function HasNoEther() public payable {
require(msg.value == 0);
}
/**
* @dev Disallows direct send by settings a default function without the `payable` flag.
*/
function() external {
}
/**
* @dev Transfer all Ether held by the contract to the owner.
*/
function reclaimEther() external onlyOwner {
// solium-disable-next-line security/no-send
assert(owner.send(address(this).balance));
}
}
pragma solidity ^0.4.21;
import "./CanReclaimToken.sol";
/**
* @title Contracts that should not own Tokens
* @author Remco Bloemen <remco@2π.com>
* @dev This blocks incoming ERC223 tokens to prevent accidental loss of tokens.
* Should tokens (any ERC20Basic compatible) end up in the contract, it allows the
* owner to reclaim the tokens.
*/
contract HasNoTokens is CanReclaimToken {
/**
* @dev Reject all ERC223 compatible tokens
* @param from_ address The address that is transferring the tokens
* @param value_ uint256 the amount of the specified token
* @param data_ Bytes The data passed from the caller.
*/
function tokenFallback(address from_, uint256 value_, bytes data_) external {
from_;
value_;
data_;
revert();
}
}
pragma solidity ^0.4.21;
import "./Ownable.sol";
/**
* @title Heritable
* @dev The Heritable contract provides ownership transfer capabilities, in the
* case that the current owner stops "heartbeating". Only the heir can pronounce the
* owner's death.
*/
contract Heritable is Ownable {
address private heir_;
// Time window the owner has to notify they are alive.
uint256 private heartbeatTimeout_;
// Timestamp of the owner's death, as pronounced by the heir.
uint256 private timeOfDeath_;
event HeirChanged(address indexed owner, address indexed newHeir);
event OwnerHeartbeated(address indexed owner);
event OwnerProclaimedDead(address indexed owner, address indexed heir, uint256 timeOfDeath);
event HeirOwnershipClaimed(address indexed previousOwner, address indexed newOwner);
/**
* @dev Throw an exception if called by any account other than the heir's.
*/
modifier onlyHeir() {
require(msg.sender == heir_);
_;
}
/**
* @notice Create a new Heritable Contract with heir address 0x0.
* @param _heartbeatTimeout time available for the owner to notify they are alive,
* before the heir can take ownership.
*/
function Heritable(uint256 _heartbeatTimeout) public {
setHeartbeatTimeout(_heartbeatTimeout);
}
function setHeir(address newHeir) public onlyOwner {
require(newHeir != owner);
heartbeat();
emit HeirChanged(owner, newHeir);
heir_ = newHeir;
}
/**
* @dev Use these getter functions to access the internal variables in
* an inherited contract.
*/
function heir() public view returns(address) {
return heir_;
}
function heartbeatTimeout() public view returns(uint256) {
return heartbeatTimeout_;
}
function timeOfDeath() public view returns(uint256) {
return timeOfDeath_;
}
/**
* @dev set heir = 0x0
*/
function removeHeir() public onlyOwner {
heartbeat();
heir_ = 0;
}
/**
* @dev Heir can pronounce the owners death. To claim the ownership, they will
* have to wait for `heartbeatTimeout` seconds.
*/
function proclaimDeath() public onlyHeir {
require(ownerLives());
emit OwnerProclaimedDead(owner, heir_, timeOfDeath_);
// solium-disable-next-line security/no-block-members
timeOfDeath_ = block.timestamp;
}
/**
* @dev Owner can send a heartbeat if they were mistakenly pronounced dead.
*/
function heartbeat() public onlyOwner {
emit OwnerHeartbeated(owner);
timeOfDeath_ = 0;
}
/**
* @dev Allows heir to transfer ownership only if heartbeat has timed out.
*/
function claimHeirOwnership() public onlyHeir {
require(!ownerLives());
// solium-disable-next-line security/no-block-members
require(block.timestamp >= timeOfDeath_ + heartbeatTimeout_);
emit OwnershipTransferred(owner, heir_);
emit HeirOwnershipClaimed(owner, heir_);
owner = heir_;
timeOfDeath_ = 0;
}
function setHeartbeatTimeout(uint256 newHeartbeatTimeout) internal onlyOwner {
require(ownerLives());
heartbeatTimeout_ = newHeartbeatTimeout;
}
function ownerLives() internal view returns (bool) {
return timeOfDeath_ == 0;
}
}
pragma solidity ^0.4.21;
import "./HasNoEther.sol";
import "./HasNoTokens.sol";
import "./HasNoContracts.sol";
/**
* @title Base contract for contracts that should not own things.
* @author Remco Bloemen <remco@2π.com>
* @dev Solves a class of errors where a contract accidentally becomes owner of Ether, Tokens or
* Owned contracts. See respective base contracts for details.
*/
contract NoOwner is HasNoEther, HasNoTokens, HasNoContracts {
}
pragma solidity ^0.4.21;
import "./Ownable.sol";
/**
* @title Whitelist
* @dev The Whitelist contract has a whitelist of addresses, and provides basic authorization control functions.
* @dev This simplifies the implementation of "user permissions".
*/
contract Whitelist is Ownable {
mapping(address => bool) public whitelist;
event WhitelistedAddressAdded(address addr);
event WhitelistedAddressRemoved(address addr);
/**
* @dev Throws if called by any account that's not whitelisted.
*/
modifier onlyWhitelisted() {
require(whitelist[msg.sender]);
_;
}
/**
* @dev add an address to the whitelist
* @param addr address
* @return true if the address was added to the whitelist, false if the address was already in the whitelist
*/
function addAddressToWhitelist(address addr) onlyOwner public returns(bool success) {
if (!whitelist[addr]) {
whitelist[addr] = true;
emit WhitelistedAddressAdded(addr);
success = true;
}
}
/**
* @dev add addresses to the whitelist
* @param addrs addresses
* @return true if at least one address was added to the whitelist,
* false if all addresses were already in the whitelist
*/
function addAddressesToWhitelist(address[] addrs) onlyOwner public returns(bool success) {
for (uint256 i = 0; i < addrs.length; i++) {
if (addAddressToWhitelist(addrs[i])) {
success = true;
}
}
}
/**
* @dev remove an address from the whitelist
* @param addr address
* @return true if the address was removed from the whitelist,
* false if the address wasn't in the whitelist in the first place
*/
function removeAddressFromWhitelist(address addr) onlyOwner public returns(bool success) {
if (whitelist[addr]) {
whitelist[addr] = false;
emit WhitelistedAddressRemoved(addr);
success = true;
}
}
/**
* @dev remove addresses from the whitelist
* @param addrs addresses
* @return true if at least one address was removed from the whitelist,
* false if all addresses weren't in the whitelist in the first place
*/
function removeAddressesFromWhitelist(address[] addrs) onlyOwner public returns(bool success) {
for (uint256 i = 0; i < addrs.length; i++) {
if (removeAddressFromWhitelist(addrs[i])) {
success = true;
}
}
}
}
pragma solidity ^0.4.21;
import "./Roles.sol";
/**
* @title RBAC (Role-Based Access Control)
* @author Matt Condon (@Shrugs)
* @dev Stores and provides setters and getters for roles and addresses.
* @dev Supports unlimited numbers of roles and addresses.
* @dev See //contracts/mocks/RBACMock.sol for an example of usage.
* This RBAC method uses strings to key roles. It may be beneficial
* for you to write your own implementation of this interface using Enums or similar.
* It's also recommended that you define constants in the contract, like ROLE_ADMIN below,
* to avoid typos.
*/
contract RBAC {
using Roles for Roles.Role;
mapping (string => Roles.Role) private roles;
event RoleAdded(address addr, string roleName);
event RoleRemoved(address addr, string roleName);
/**
* @dev reverts if addr does not have role
* @param addr address
* @param roleName the name of the role
* // reverts
*/
function checkRole(address addr, string roleName)
view
public
{
roles[roleName].check(addr);
}
/**
* @dev determine if addr has role
* @param addr address
* @param roleName the name of the role
* @return bool
*/
function hasRole(address addr, string roleName)
view
public
returns (bool)
{
return roles[roleName].has(addr);
}
/**
* @dev add a role to an address
* @param addr address
* @param roleName the name of the role
*/
function addRole(address addr, string roleName)
internal
{
roles[roleName].add(addr);
emit RoleAdded(addr, roleName);
}
/**
* @dev remove a role from an address
* @param addr address
* @param roleName the name of the role
*/
function removeRole(address addr, string roleName)
internal
{
roles[roleName].remove(addr);
emit RoleRemoved(addr, roleName);
}
/**
* @dev modifier to scope access to a single role (uses msg.sender as addr)
* @param roleName the name of the role
* // reverts
*/
modifier onlyRole(string roleName)
{
checkRole(msg.sender, roleName);
_;
}
/**
* @dev modifier to scope access to a set of roles (uses msg.sender as addr)
* @param roleNames the names of the roles to scope access to
* // reverts
*
* @TODO - when solidity supports dynamic arrays as arguments to modifiers, provide this
* see: https://github.com/ethereum/solidity/issues/2467
*/
// modifier onlyRoles(string[] roleNames) {
// bool hasAnyRole = false;
// for (uint8 i = 0; i < roleNames.length; i++) {
// if (hasRole(msg.sender, roleNames[i])) {
// hasAnyRole = true;
// break;
// }
// }
// require(hasAnyRole);
// _;
// }
}
pragma solidity ^0.4.21;
import "./RBAC.sol";
/**
* @title RBACWithAdmin
* @author Matt Condon (@Shrugs)
* @dev It's recommended that you define constants in the contract,
* @dev like ROLE_ADMIN below, to avoid typos.
*/
contract RBACWithAdmin is RBAC {
/**
* A constant role name for indicating admins.
*/
string public constant ROLE_ADMIN = "admin";
/**
* @dev modifier to scope access to admins
* // reverts
*/
modifier onlyAdmin()
{
checkRole(msg.sender, ROLE_ADMIN);
_;
}
/**
* @dev constructor. Sets msg.sender as admin by default
*/
function RBACWithAdmin()
public
{
addRole(msg.sender, ROLE_ADMIN);
}
/**
* @dev add a role to an address
* @param addr address
* @param roleName the name of the role
*/
function adminAddRole(address addr, string roleName)
onlyAdmin
public
{
addRole(addr, roleName);
}
/**
* @dev remove a role from an address
* @param addr address
* @param roleName the name of the role
*/
function adminRemoveRole(address addr, string roleName)
onlyAdmin
public
{
removeRole(addr, roleName);
}
}
pragma solidity ^0.4.21;
/**
* @title Roles
* @author Francisco Giordano (@frangio)
* @dev Library for managing addresses assigned to a Role.
* See RBAC.sol for example usage.
*/
library Roles {
struct Role {
mapping (address => bool) bearer;
}
/**
* @dev give an address access to this role
*/
function add(Role storage role, address addr)
internal
{
role.bearer[addr] = true;
}
/**
* @dev remove an address' access to this role
*/
function remove(Role storage role, address addr)
internal
{
role.bearer[addr] = false;
}
/**
* @dev check if an address has this role
* // reverts
*/
function check(Role storage role, address addr)
view
internal
{
require(has(role, addr));
}
/**
* @dev check if an address has this role
* @return bool
*/
function has(Role storage role, address addr)
view
internal
returns (bool)
{
return role.bearer[addr];
}
}
pragma solidity ^0.4.21;
import "../math/SafeMath.sol";
/**
* @title PullPayment
* @dev Base contract supporting async send for pull payments. Inherit from this
* contract and use asyncSend instead of send or transfer.
*/
contract PullPayment {
using SafeMath for uint256;
mapping(address => uint256) public payments;
uint256 public totalPayments;
/**
* @dev Withdraw accumulated balance, called by payee.
*/
function withdrawPayments() public {
address payee = msg.sender;
uint256 payment = payments[payee];
require(payment != 0);
require(address(this).balance >= payment);
totalPayments = totalPayments.sub(payment);
payments[payee] = 0;
payee.transfer(payment);
}
/**
* @dev Called by the payer to store the sent amount as credit to be pulled.
* @param dest The destination address of the funds.
* @param amount The amount to transfer.
*/
function asyncSend(address dest, uint256 amount) internal {
payments[dest] = payments[dest].add(amount);
totalPayments = totalPayments.add(amount);
}
}
pragma solidity ^0.4.21;
import "../math/SafeMath.sol";
/**
* @title SplitPayment
* @dev Base contract that supports multiple payees claiming funds sent to this contract
* according to the proportion they own.
*/
contract SplitPayment {
using SafeMath for uint256;
uint256 public totalShares = 0;
uint256 public totalReleased = 0;
mapping(address => uint256) public shares;
mapping(address => uint256) public released;
address[] public payees;
/**
* @dev Constructor
*/
function SplitPayment(address[] _payees, uint256[] _shares) public payable {
require(_payees.length == _shares.length);
for (uint256 i = 0; i < _payees.length; i++) {
addPayee(_payees[i], _shares[i]);
}
}
/**
* @dev payable fallback
*/
function () public payable {}
/**
* @dev Claim your share of the balance.
*/
function claim() public {
address payee = msg.sender;
require(shares[payee] > 0);
uint256 totalReceived = address(this).balance.add(totalReleased);
uint256 payment = totalReceived.mul(shares[payee]).div(totalShares).sub(released[payee]);
require(payment != 0);
require(address(this).balance >= payment);
released[payee] = released[payee].add(payment);
totalReleased = totalReleased.add(payment);
payee.transfer(payment);
}
/**
* @dev Add a new payee to the contract.
* @param _payee The address of the payee to add.
* @param _shares The number of shares owned by the payee.
*/
function addPayee(address _payee, uint256 _shares) internal {
require(_payee != address(0));
require(_shares > 0);
require(shares[_payee] == 0);
payees.push(_payee);
shares[_payee] = _shares;
totalShares = totalShares.add(_shares);
}
}
pragma solidity ^0.4.21;
import "./MintableToken.sol";
/**
* @title Capped token
* @dev Mintable token with a token cap.
*/
contract CappedToken is MintableToken {
uint256 public cap;
function CappedToken(uint256 _cap) public {
require(_cap > 0);
cap = _cap;
}
/**
* @dev Function to mint tokens
* @param _to The address that will receive the minted tokens.
* @param _amount The amount of tokens to mint.
* @return A boolean that indicates if the operation was successful.
*/
function mint(address _to, uint256 _amount) onlyOwner canMint public returns (bool) {
require(totalSupply_.add(_amount) <= cap);
return super.mint(_to, _amount);
}
}
pragma solidity ^0.4.21;
import "./ERC721.sol";
/**
* @title ERC-721 methods shipped in OpenZeppelin v1.7.0, removed in the latest version of the standard
* @dev Only use this interface for compatibility with previously deployed contracts
* @dev Use ERC721 for interacting with new contracts which are standard-compliant
*/
contract DeprecatedERC721 is ERC721 {
function takeOwnership(uint256 _tokenId) public;
function transfer(address _to, uint256 _tokenId) public;
function tokensOf(address _owner) public view returns (uint256[]);
}
pragma solidity ^0.4.21;
import "../ERC20/ERC20.sol";
/**
* @title ERC827 interface, an extension of ERC20 token standard
*
* @dev Interface of a ERC827 token, following the ERC20 standard with extra
* @dev methods to transfer value and data and execute calls in transfers and
* @dev approvals.
*/
contract ERC827 is ERC20 {
function approve(address _spender, uint256 _value, bytes _data) public returns (bool);
function transfer(address _to, uint256 _value, bytes _data) public returns (bool);
function transferFrom(
address _from,
address _to,
uint256 _value,
bytes _data
)
public
returns (bool);
}
/* solium-disable security/no-low-level-calls */
pragma solidity ^0.4.21;
import "./ERC827.sol";
import "../ERC20/StandardToken.sol";
/**
* @title ERC827, an extension of ERC20 token standard
*
* @dev Implementation the ERC827, following the ERC20 standard with extra
* @dev methods to transfer value and data and execute calls in transfers and
* @dev approvals.
*
* @dev Uses OpenZeppelin StandardToken.
*/
contract ERC827Token is ERC827, StandardToken {
/**
* @dev Addition to ERC20 token methods. It allows to
* @dev approve the transfer of value and execute a call with the sent data.
*
* @dev Beware that changing an allowance with this method brings the risk that
* @dev someone may use both the old and the new allowance by unfortunate
* @dev transaction ordering. One possible solution to mitigate this race condition
* @dev is to first reduce the spender's allowance to 0 and set the desired value
* @dev afterwards:
* @dev https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* @param _spender The address that will spend the funds.
* @param _value The amount of tokens to be spent.
* @param _data ABI-encoded contract call to call `_to` address.
*
* @return true if the call function was executed successfully
*/
function approve(address _spender, uint256 _value, bytes _data) public returns (bool) {
require(_spender != address(this));
super.approve(_spender, _value);
require(_spender.call(_data));
return true;
}
/**
* @dev Addition to ERC20 token methods. Transfer tokens to a specified
* @dev address and execute a call with the sent data on the same transaction
*
* @param _to address The address which you want to transfer to
* @param _value uint256 the amout of tokens to be transfered
* @param _data ABI-encoded contract call to call `_to` address.
*
* @return true if the call function was executed successfully
*/
function transfer(address _to, uint256 _value, bytes _data) public returns (bool) {
require(_to != address(this));
super.transfer(_to, _value);
require(_to.call(_data));
return true;
}
/**
* @dev Addition to ERC20 token methods. Transfer tokens from one address to
* @dev another and make a contract call on the same transaction
*
* @param _from The address which you want to send tokens from
* @param _to The address which you want to transfer to
* @param _value The amout of tokens to be transferred
* @param _data ABI-encoded contract call to call `_to` address.
*
* @return true if the call function was executed successfully
*/
function transferFrom(
address _from,
address _to,
uint256 _value,
bytes _data
)
public returns (bool)
{
require(_to != address(this));
super.transferFrom(_from, _to, _value);
require(_to.call(_data));
return true;
}
/**
* @dev Addition to StandardToken methods. Increase the amount of tokens that
* @dev an owner allowed to a spender and execute a call with the sent data.
*
* @dev approve should be called when allowed[_spender] == 0. To increment
* @dev allowed value is better to use this function to avoid 2 calls (and wait until
* @dev the first transaction is mined)
* @dev From MonolithDAO Token.sol
*
* @param _spender The address which will spend the funds.
* @param _addedValue The amount of tokens to increase the allowance by.
* @param _data ABI-encoded contract call to call `_spender` address.
*/
function increaseApproval(address _spender, uint _addedValue, bytes _data) public returns (bool) {
require(_spender != address(this));
super.increaseApproval(_spender, _addedValue);
require(_spender.call(_data));
return true;
}
/**
* @dev Addition to StandardToken methods. Decrease the amount of tokens that
* @dev an owner allowed to a spender and execute a call with the sent data.
*
* @dev approve should be called when allowed[_spender] == 0. To decrement
* @dev allowed value is better to use this function to avoid 2 calls (and wait until
* @dev the first transaction is mined)
* @dev From MonolithDAO Token.sol
*
* @param _spender The address which will spend the funds.
* @param _subtractedValue The amount of tokens to decrease the allowance by.
* @param _data ABI-encoded contract call to call `_spender` address.
*/
function decreaseApproval(address _spender, uint _subtractedValue, bytes _data) public returns (bool) {
require(_spender != address(this));
super.decreaseApproval(_spender, _subtractedValue);
require(_spender.call(_data));
return true;
}
}
let sendReward = function (sender, receiver, value) {
web3.eth.sendTransaction({
from: sender,
to: receiver,
value: value,
});
};
var SecureTargetBounty = artifacts.require('SecureTargetBounty');
var InsecureTargetBounty = artifacts.require('InsecureTargetBounty');
function awaitEvent (event, handler) {
return new Promise((resolve, reject) => {
function wrappedHandler (...args) {
Promise.resolve(handler(...args)).then(resolve).catch(reject);
}
event.watch(wrappedHandler);
});
}
contract('Bounty', function (accounts) {
it('sets reward', async function () {
let owner = accounts[0];
let reward = web3.toWei(1, 'ether');
let bounty = await SecureTargetBounty.new();
sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
});
it('empties itself when destroyed', async function () {
let owner = accounts[0];
let reward = web3.toWei(1, 'ether');
let bounty = await SecureTargetBounty.new();
sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
await bounty.destroy();
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber());
});
describe('Against secure contract', function () {
it('cannot claim reward', async function () {
let owner = accounts[0];
let researcher = accounts[1];
let reward = web3.toWei(1, 'ether');
let bounty = await SecureTargetBounty.new();
let event = bounty.TargetCreated({});
let watcher = async function (err, result) {
event.stopWatching();
if (err) { throw err; }
var targetAddress = result.args.createdAddress;
sendReward(owner, bounty.address, reward);
assert.equal(reward,
web3.eth.getBalance(bounty.address).toNumber());
try {
await bounty.claim(targetAddress, { from: researcher });
assert.isTrue(false); // should never reach here
} catch (error) {
let reClaimedBounty = await bounty.claimed.call();
assert.isFalse(reClaimedBounty);
}
try {
await bounty.withdrawPayments({ from: researcher });
assert.isTrue(false); // should never reach here
} catch (err) {
assert.equal(reward,
web3.eth.getBalance(bounty.address).toNumber());
}
};
await bounty.createTarget({ from: researcher });
await awaitEvent(event, watcher);
});
});
describe('Against broken contract', function () {
it('claims reward', async function () {
let owner = accounts[0];
let researcher = accounts[1];
let reward = web3.toWei(1, 'ether');
let bounty = await InsecureTargetBounty.new();
let event = bounty.TargetCreated({});
let watcher = async function (err, result) {
event.stopWatching();
if (err) { throw err; }
let targetAddress = result.args.createdAddress;
sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
await bounty.claim(targetAddress, { from: researcher });
let claim = await bounty.claimed.call();
assert.isTrue(claim);
await bounty.withdrawPayments({ from: researcher });
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber());
};
await bounty.createTarget({ from: researcher });
await awaitEvent(event, watcher);
});
});
});
import latestTime from './helpers/latestTime';
import { increaseTimeTo, duration } from './helpers/increaseTime';
import assertRevert from './helpers/assertRevert';
const DayLimitMock = artifacts.require('DayLimitMock');
contract('DayLimit', function (accounts) {
let dayLimit;
let initLimit = 10;
beforeEach(async function () {
this.startTime = latestTime();
dayLimit = await DayLimitMock.new(initLimit);
});
it('should construct with the passed daily limit', async function () {
let dailyLimit = await dayLimit.dailyLimit();
assert.equal(initLimit, dailyLimit);
});
it('should be able to spend if daily limit is not reached', async function () {
await dayLimit.attemptSpend(8);
let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await dayLimit.attemptSpend(2);
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 10);
});
it('should prevent spending if daily limit is reached', async function () {
await dayLimit.attemptSpend(8);
let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await assertRevert(dayLimit.attemptSpend(3));
});
it('should allow spending if daily limit is reached and then set higher', async function () {
await dayLimit.attemptSpend(8);
let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await assertRevert(dayLimit.attemptSpend(3));
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await dayLimit.setDailyLimit(15);
await dayLimit.attemptSpend(3);
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 11);
});
it('should allow spending if daily limit is reached and then amount spent is reset', async function () {
await dayLimit.attemptSpend(8);
let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await assertRevert(dayLimit.attemptSpend(3));
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await dayLimit.resetSpentToday();
await dayLimit.attemptSpend(3);
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 3);
});
it('should allow spending if daily limit is reached and then the next has come', async function () {
let limit = 10;
let dayLimit = await DayLimitMock.new(limit);
await dayLimit.attemptSpend(8);
let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await assertRevert(dayLimit.attemptSpend(3));
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await increaseTimeTo(this.startTime + duration.days(1));
await dayLimit.attemptSpend(3);
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 3);
});
});
import increaseTime from './helpers/increaseTime';
import expectThrow from './helpers/expectThrow';
import assertRevert from './helpers/assertRevert';
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
const Heritable = artifacts.require('Heritable');
contract('Heritable', function (accounts) {
let heritable;
let owner;
beforeEach(async function () {
heritable = await Heritable.new(4141);
owner = await heritable.owner();
});
it('should start off with an owner, but without heir', async function () {
const heir = await heritable.heir();
assert.equal(typeof (owner), 'string');
assert.equal(typeof (heir), 'string');
assert.notStrictEqual(
owner, NULL_ADDRESS,
'Owner shouldn\'t be the null address'
);
assert.isTrue(
heir === NULL_ADDRESS,
'Heir should be the null address'
);
});
it('only owner should set heir', async function () {
const newHeir = accounts[1];
const someRandomAddress = accounts[2];
assert.isTrue(owner !== someRandomAddress);
await heritable.setHeir(newHeir, { from: owner });
await expectThrow(heritable.setHeir(newHeir, { from: someRandomAddress }));
});
it('owner can\'t be heir', async function () {
await assertRevert(heritable.setHeir(owner, { from: owner }));
});
it('owner can remove heir', async function () {
const newHeir = accounts[1];
await heritable.setHeir(newHeir, { from: owner });
let heir = await heritable.heir();
assert.notStrictEqual(heir, NULL_ADDRESS);
await heritable.removeHeir();
heir = await heritable.heir();
assert.isTrue(heir === NULL_ADDRESS);
});
it('heir can claim ownership only if owner is dead and timeout was reached', async function () {
const heir = accounts[1];
await heritable.setHeir(heir, { from: owner });
await expectThrow(heritable.claimHeirOwnership({ from: heir }));
await heritable.proclaimDeath({ from: heir });
await increaseTime(1);
await expectThrow(heritable.claimHeirOwnership({ from: heir }));
await increaseTime(4141);
await heritable.claimHeirOwnership({ from: heir });
assert.isTrue(await heritable.heir() === heir);
});
it('only heir can proclaim death', async function () {
const someRandomAddress = accounts[2];
await assertRevert(heritable.proclaimDeath({ from: owner }));
await assertRevert(heritable.proclaimDeath({ from: someRandomAddress }));
});
it('heir can\'t proclaim death if owner is death', async function () {
const heir = accounts[1];
await heritable.setHeir(heir, { from: owner });
await heritable.proclaimDeath({ from: heir });
await assertRevert(heritable.proclaimDeath({ from: heir }));
});
it('heir can\'t claim ownership if owner heartbeats', async function () {
const heir = accounts[1];
await heritable.setHeir(heir, { from: owner });
await heritable.proclaimDeath({ from: heir });
await heritable.heartbeat({ from: owner });
await expectThrow(heritable.claimHeirOwnership({ from: heir }));
await heritable.proclaimDeath({ from: heir });
await increaseTime(4141);
await heritable.heartbeat({ from: owner });
await expectThrow(heritable.claimHeirOwnership({ from: heir }));
});
it('should log events appropriately', async function () {
const heir = accounts[1];
const setHeirLogs = (await heritable.setHeir(heir, { from: owner })).logs;
const setHeirEvent = setHeirLogs.find(e => e.event === 'HeirChanged');
assert.isTrue(setHeirEvent.args.owner === owner);
assert.isTrue(setHeirEvent.args.newHeir === heir);
const heartbeatLogs = (await heritable.heartbeat({ from: owner })).logs;
const heartbeatEvent = heartbeatLogs.find(e => e.event === 'OwnerHeartbeated');
assert.isTrue(heartbeatEvent.args.owner === owner);
const proclaimDeathLogs = (await heritable.proclaimDeath({ from: heir })).logs;
const ownerDeadEvent = proclaimDeathLogs.find(e => e.event === 'OwnerProclaimedDead');
assert.isTrue(ownerDeadEvent.args.owner === owner);
assert.isTrue(ownerDeadEvent.args.heir === heir);
await increaseTime(4141);
const claimHeirOwnershipLogs = (await heritable.claimHeirOwnership({ from: heir })).logs;
const ownershipTransferredEvent = claimHeirOwnershipLogs.find(e => e.event === 'OwnershipTransferred');
const heirOwnershipClaimedEvent = claimHeirOwnershipLogs.find(e => e.event === 'HeirOwnershipClaimed');
assert.isTrue(ownershipTransferredEvent.args.previousOwner === owner);
assert.isTrue(ownershipTransferredEvent.args.newOwner === heir);
assert.isTrue(heirOwnershipClaimedEvent.args.previousOwner === owner);
assert.isTrue(heirOwnershipClaimedEvent.args.newOwner === heir);
});
it('timeOfDeath can be queried', async function () {
assert.equal(await heritable.timeOfDeath(), 0);
});
it('heartbeatTimeout can be queried', async function () {
assert.equal(await heritable.heartbeatTimeout(), 4141);
});
});
import assertRevert from './helpers/assertRevert';
var LimitBalanceMock = artifacts.require('LimitBalanceMock');
contract('LimitBalance', function (accounts) {
let lb;
beforeEach(async function () {
lb = await LimitBalanceMock.new();
});
let LIMIT = 1000;
it('should expose limit', async function () {
let limit = await lb.limit();
assert.equal(limit, LIMIT);
});
it('should allow sending below limit', async function () {
let amount = 1;
await lb.limitedDeposit({ value: amount });
assert.equal(web3.eth.getBalance(lb.address), amount);
});
it('shouldnt allow sending above limit', async function () {
let amount = 1110;
await assertRevert(lb.limitedDeposit({ value: amount }));
});
it('should allow multiple sends below limit', async function () {
let amount = 500;
await lb.limitedDeposit({ value: amount });
assert.equal(web3.eth.getBalance(lb.address), amount);
await lb.limitedDeposit({ value: amount });
assert.equal(web3.eth.getBalance(lb.address), amount * 2);
});
it('shouldnt allow multiple sends above limit', async function () {
let amount = 500;
await lb.limitedDeposit({ value: amount });
assert.equal(web3.eth.getBalance(lb.address), amount);
await assertRevert(lb.limitedDeposit({ value: amount + 1 }));
});
});
import expectThrow from './helpers/expectThrow';
const ReentrancyMock = artifacts.require('ReentrancyMock');
const ReentrancyAttack = artifacts.require('ReentrancyAttack');
contract('ReentrancyGuard', function (accounts) {
let reentrancyMock;
beforeEach(async function () {
reentrancyMock = await ReentrancyMock.new();
let initialCounter = await reentrancyMock.counter();
assert.equal(initialCounter, 0);
});
it('should not allow remote callback', async function () {
let attacker = await ReentrancyAttack.new();
await expectThrow(reentrancyMock.countAndCall(attacker.address));
});
// The following are more side-effects than intended behaviour:
// I put them here as documentation, and to monitor any changes
// in the side-effects.
it('should not allow local recursion', async function () {
await expectThrow(reentrancyMock.countLocalRecursive(10));
});
it('should not allow indirect local recursion', async function () {
await expectThrow(reentrancyMock.countThisRecursive(10));
});
});
import expectThrow from './helpers/expectThrow';
const SimpleSavingsWallet = artifacts.require('SimpleSavingsWallet');
contract('SimpleSavingsWallet', function (accounts) {
let savingsWallet;
let owner;
const paymentAmount = 4242;
beforeEach(async function () {
savingsWallet = await SimpleSavingsWallet.new(4141);
owner = await savingsWallet.owner();
});
it('should receive funds', async function () {
await web3.eth.sendTransaction({ from: owner, to: savingsWallet.address, value: paymentAmount });
assert.isTrue((new web3.BigNumber(paymentAmount)).equals(web3.eth.getBalance(savingsWallet.address)));
});
it('owner can send funds', async function () {
// Receive payment so we have some money to spend.
await web3.eth.sendTransaction({ from: accounts[9], to: savingsWallet.address, value: 1000000 });
await expectThrow(savingsWallet.sendTo(0, paymentAmount, { from: owner }));
await expectThrow(savingsWallet.sendTo(savingsWallet.address, paymentAmount, { from: owner }));
await expectThrow(savingsWallet.sendTo(accounts[1], 0, { from: owner }));
const balance = web3.eth.getBalance(accounts[1]);
await savingsWallet.sendTo(accounts[1], paymentAmount, { from: owner });
assert.isTrue(balance.plus(paymentAmount).equals(web3.eth.getBalance(accounts[1])));
});
});
import assertRevert from '../helpers/assertRevert';
import { signHex } from '../helpers/sign';
const Bouncer = artifacts.require('SignatureBouncerMock');
require('chai')
.use(require('chai-as-promised'))
.should();
export const getSigner = (contract, signer, data = '') => (addr) => {
// via: https://github.com/OpenZeppelin/zeppelin-solidity/pull/812/files
const message = contract.address.substr(2) + addr.substr(2) + data;
// ^ substr to remove `0x` because in solidity the address is a set of byes, not a string `0xabcd`
return signHex(signer, message);
};
contract('Bouncer', ([_, owner, authorizedUser, anyone, bouncerAddress, newBouncer]) => {
before(async function () {
this.bouncer = await Bouncer.new({ from: owner });
this.roleBouncer = await this.bouncer.ROLE_BOUNCER();
this.genSig = getSigner(this.bouncer, bouncerAddress);
});
it('should have a default owner of self', async function () {
const theOwner = await this.bouncer.owner();
theOwner.should.eq(owner);
});
it('should allow owner to add a bouncer', async function () {
await this.bouncer.addBouncer(bouncerAddress, { from: owner });
const hasRole = await this.bouncer.hasRole(bouncerAddress, this.roleBouncer);
hasRole.should.eq(true);
});
it('should not allow anyone to add a bouncer', async function () {
await assertRevert(
this.bouncer.addBouncer(bouncerAddress, { from: anyone })
);
});
context('modifiers', () => {
it('should allow valid signature for sender', async function () {
await this.bouncer.onlyWithValidSignature(
this.genSig(authorizedUser),
{ from: authorizedUser }
);
});
it('should not allow invalid signature for sender', async function () {
await assertRevert(
this.bouncer.onlyWithValidSignature(
'abcd',
{ from: authorizedUser }
)
);
});
});
context('signatures', () => {
it('should accept valid message for valid user', async function () {
const isValid = await this.bouncer.checkValidSignature(
authorizedUser,
this.genSig(authorizedUser)
);
isValid.should.eq(true);
});
it('should not accept invalid message for valid user', async function () {
const isValid = await this.bouncer.checkValidSignature(
authorizedUser,
this.genSig(anyone)
);
isValid.should.eq(false);
});
it('should not accept invalid message for invalid user', async function () {
const isValid = await this.bouncer.checkValidSignature(
anyone,
'abcd'
);
isValid.should.eq(false);
});
it('should not accept valid message for invalid user', async function () {
const isValid = await this.bouncer.checkValidSignature(
anyone,
this.genSig(authorizedUser)
);
isValid.should.eq(false);
});
});
context('management', () => {
it('should not allow anyone to add bouncers', async function () {
await assertRevert(
this.bouncer.addBouncer(newBouncer, { from: anyone })
);
});
it('should be able to add bouncers', async function () {
await this.bouncer.addBouncer(newBouncer, { from: owner })
.should.be.fulfilled;
});
it('should not allow adding invalid address', async function () {
await assertRevert(
this.bouncer.addBouncer('0x0', { from: owner })
);
});
it('should not allow anyone to remove bouncer', async function () {
await assertRevert(
this.bouncer.removeBouncer(newBouncer, { from: anyone })
);
});
it('should be able to remove bouncers', async function () {
await this.bouncer.removeBouncer(newBouncer, { from: owner })
.should.be.fulfilled;
});
});
});
import ether from '../helpers/ether';
import assertRevert from '../helpers/assertRevert';
const BigNumber = web3.BigNumber;
const should = require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();
const AllowanceCrowdsale = artifacts.require('AllowanceCrowdsaleImpl');
const SimpleToken = artifacts.require('SimpleToken');
contract('AllowanceCrowdsale', function ([_, investor, wallet, purchaser, tokenWallet]) {
const rate = new BigNumber(1);
const value = ether(0.42);
const expectedTokenAmount = rate.mul(value);
const tokenAllowance = new BigNumber('1e22');
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
beforeEach(async function () {
this.token = await SimpleToken.new({ from: tokenWallet });
this.crowdsale = await AllowanceCrowdsale.new(rate, wallet, this.token.address, tokenWallet);
await this.token.approve(this.crowdsale.address, tokenAllowance, { from: tokenWallet });
});
describe('accepting payments', function () {
it('should accept sends', async function () {
await this.crowdsale.send(value).should.be.fulfilled;
});
it('should accept payments', async function () {
await this.crowdsale.buyTokens(investor, { value: value, from: purchaser }).should.be.fulfilled;
});
});
describe('high-level purchase', function () {
it('should log purchase', async function () {
const { logs } = await this.crowdsale.sendTransaction({ value: value, from: investor });
const event = logs.find(e => e.event === 'TokenPurchase');
should.exist(event);
event.args.purchaser.should.equal(investor);
event.args.beneficiary.should.equal(investor);
event.args.value.should.be.bignumber.equal(value);
event.args.amount.should.be.bignumber.equal(expectedTokenAmount);
});
it('should assign tokens to sender', async function () {
await this.crowdsale.sendTransaction({ value: value, from: investor });
let balance = await this.token.balanceOf(investor);
balance.should.be.bignumber.equal(expectedTokenAmount);
});
it('should forward funds to wallet', async function () {
const pre = web3.eth.getBalance(wallet);
await this.crowdsale.sendTransaction({ value, from: investor });
const post = web3.eth.getBalance(wallet);
post.minus(pre).should.be.bignumber.equal(value);
});
});
describe('check remaining allowance', function () {
it('should report correct allowace left', async function () {
let remainingAllowance = tokenAllowance - expectedTokenAmount;
await this.crowdsale.buyTokens(investor, { value: value, from: purchaser });
let tokensRemaining = await this.crowdsale.remainingTokens();
tokensRemaining.should.be.bignumber.equal(remainingAllowance);
});
});
describe('when token wallet is different from token address', function () {
it('creation reverts', async function () {
this.token = await SimpleToken.new({ from: tokenWallet });
await assertRevert(AllowanceCrowdsale.new(rate, wallet, this.token.address, ZERO_ADDRESS));
});
});
});
import ether from '../helpers/ether';
import EVMRevert from '../helpers/EVMRevert';
const BigNumber = web3.BigNumber;
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();
const CappedCrowdsale = artifacts.require('CappedCrowdsaleImpl');
const SimpleToken = artifacts.require('SimpleToken');
contract('CappedCrowdsale', function ([_, wallet]) {
const rate = new BigNumber(1);
const cap = ether(100);
const lessThanCap = ether(60);
const tokenSupply = new BigNumber('1e22');
beforeEach(async function () {
this.token = await SimpleToken.new();
this.crowdsale = await CappedCrowdsale.new(rate, wallet, this.token.address, cap);
await this.token.transfer(this.crowdsale.address, tokenSupply);
});
describe('creating a valid crowdsale', function () {
it('should fail with zero cap', async function () {
await CappedCrowdsale.new(rate, wallet, 0, this.token.address).should.be.rejectedWith(EVMRevert);
});
});
describe('accepting payments', function () {
it('should accept payments within cap', async function () {
await this.crowdsale.send(cap.minus(lessThanCap)).should.be.fulfilled;
await this.crowdsale.send(lessThanCap).should.be.fulfilled;
});
it('should reject payments outside cap', async function () {
await this.crowdsale.send(cap);
await this.crowdsale.send(1).should.be.rejectedWith(EVMRevert);
});
it('should reject payments that exceed cap', async function () {
await this.crowdsale.send(cap.plus(1)).should.be.rejectedWith(EVMRevert);
});
});
describe('ending', function () {
it('should not reach cap if sent under cap', async function () {
let capReached = await this.crowdsale.capReached();
capReached.should.equal(false);
await this.crowdsale.send(lessThanCap);
capReached = await this.crowdsale.capReached();
capReached.should.equal(false);
});
it('should not reach cap if sent just under cap', async function () {
await this.crowdsale.send(cap.minus(1));
let capReached = await this.crowdsale.capReached();
capReached.should.equal(false);
});
it('should reach cap if cap sent', async function () {
await this.crowdsale.send(cap);
let capReached = await this.crowdsale.capReached();
capReached.should.equal(true);
});
});
});
import ether from '../helpers/ether';
const BigNumber = web3.BigNumber;
const should = require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();
const Crowdsale = artifacts.require('Crowdsale');
const SimpleToken = artifacts.require('SimpleToken');
contract('Crowdsale', function ([_, investor, wallet, purchaser]) {
const rate = new BigNumber(1);
const value = ether(42);
const tokenSupply = new BigNumber('1e22');
const expectedTokenAmount = rate.mul(value);
beforeEach(async function () {
this.token = await SimpleToken.new();
this.crowdsale = await Crowdsale.new(rate, wallet, this.token.address);
await this.token.transfer(this.crowdsale.address, tokenSupply);
});
describe('accepting payments', function () {
it('should accept payments', async function () {
await this.crowdsale.send(value).should.be.fulfilled;
await this.crowdsale.buyTokens(investor, { value: value, from: purchaser }).should.be.fulfilled;
});
});
describe('high-level purchase', function () {
it('should log purchase', async function () {
const { logs } = await this.crowdsale.sendTransaction({ value: value, from: investor });
const event = logs.find(e => e.event === 'TokenPurchase');
should.exist(event);
event.args.purchaser.should.equal(investor);
event.args.beneficiary.should.equal(investor);
event.args.value.should.be.bignumber.equal(value);
event.args.amount.should.be.bignumber.equal(expectedTokenAmount);
});
it('should assign tokens to sender', async function () {
await this.crowdsale.sendTransaction({ value: value, from: investor });
let balance = await this.token.balanceOf(investor);
balance.should.be.bignumber.equal(expectedTokenAmount);
});
it('should forward funds to wallet', async function () {
const pre = web3.eth.getBalance(wallet);
await this.crowdsale.sendTransaction({ value, from: investor });
const post = web3.eth.getBalance(wallet);
post.minus(pre).should.be.bignumber.equal(value);
});
});
describe('low-level purchase', function () {
it('should log purchase', async function () {
const { logs } = await this.crowdsale.buyTokens(investor, { value: value, from: purchaser });
const event = logs.find(e => e.event === 'TokenPurchase');
should.exist(event);
event.args.purchaser.should.equal(purchaser);
event.args.beneficiary.should.equal(investor);
event.args.value.should.be.bignumber.equal(value);
event.args.amount.should.be.bignumber.equal(expectedTokenAmount);
});
it('should assign tokens to beneficiary', async function () {
await this.crowdsale.buyTokens(investor, { value, from: purchaser });
const balance = await this.token.balanceOf(investor);
balance.should.be.bignumber.equal(expectedTokenAmount);
});
it('should forward funds to wallet', async function () {
const pre = web3.eth.getBalance(wallet);
await this.crowdsale.buyTokens(investor, { value, from: purchaser });
const post = web3.eth.getBalance(wallet);
post.minus(pre).should.be.bignumber.equal(value);
});
});
});
import { advanceBlock } from '../helpers/advanceToBlock';
import { increaseTimeTo, duration } from '../helpers/increaseTime';
import latestTime from '../helpers/latestTime';
import EVMRevert from '../helpers/EVMRevert';
const BigNumber = web3.BigNumber;
const should = require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();
const FinalizableCrowdsale = artifacts.require('FinalizableCrowdsaleImpl');
const MintableToken = artifacts.require('MintableToken');
contract('FinalizableCrowdsale', function ([_, owner, wallet, thirdparty]) {
const rate = new BigNumber(1000);
before(async function () {
// Advance to the next block to correctly read time in the solidity "now" function interpreted by ganache
await advanceBlock();
});
beforeEach(async function () {
this.openingTime = latestTime() + duration.weeks(1);
this.closingTime = this.openingTime + duration.weeks(1);
this.afterClosingTime = this.closingTime + duration.seconds(1);
this.token = await MintableToken.new();
this.crowdsale = await FinalizableCrowdsale.new(
this.openingTime, this.closingTime, rate, wallet, this.token.address, { from: owner }
);
await this.token.transferOwnership(this.crowdsale.address);
});
it('cannot be finalized before ending', async function () {
await this.crowdsale.finalize({ from: owner }).should.be.rejectedWith(EVMRevert);
});
it('cannot be finalized by third party after ending', async function () {
await increaseTimeTo(this.afterClosingTime);
await this.crowdsale.finalize({ from: thirdparty }).should.be.rejectedWith(EVMRevert);
});
it('can be finalized by owner after ending', async function () {
await increaseTimeTo(this.afterClosingTime);
await this.crowdsale.finalize({ from: owner }).should.be.fulfilled;
});
it('cannot be finalized twice', async function () {
await increaseTimeTo(this.afterClosingTime);
await this.crowdsale.finalize({ from: owner });
await this.crowdsale.finalize({ from: owner }).should.be.rejectedWith(EVMRevert);
});
it('logs finalized', async function () {
await increaseTimeTo(this.afterClosingTime);
const { logs } = await this.crowdsale.finalize({ from: owner });
const event = logs.find(e => e.event === 'Finalized');
should.exist(event);
});
});
import ether from '../helpers/ether';
import { advanceBlock } from '../helpers/advanceToBlock';
import { increaseTimeTo, duration } from '../helpers/increaseTime';
import latestTime from '../helpers/latestTime';
const BigNumber = web3.BigNumber;
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();
const IncreasingPriceCrowdsale = artifacts.require('IncreasingPriceCrowdsaleImpl');
const SimpleToken = artifacts.require('SimpleToken');
contract('IncreasingPriceCrowdsale', function ([_, investor, wallet, purchaser]) {
const value = ether(1);
const tokenSupply = new BigNumber('1e22');
describe('rate during crowdsale should change at a fixed step every block', async function () {
let balance;
const initialRate = new BigNumber(9166);
const finalRate = new BigNumber(5500);
const rateAtTime150 = new BigNumber(9166);
const rateAtTime300 = new BigNumber(9165);
const rateAtTime1500 = new BigNumber(9157);
const rateAtTime30 = new BigNumber(9166);
const rateAtTime150000 = new BigNumber(8257);
const rateAtTime450000 = new BigNumber(6439);
beforeEach(async function () {
await advanceBlock();
this.startTime = latestTime() + duration.weeks(1);
this.closingTime = this.startTime + duration.weeks(1);
this.afterClosingTime = this.closingTime + duration.seconds(1);
this.token = await SimpleToken.new();
this.crowdsale = await IncreasingPriceCrowdsale.new(
this.startTime, this.closingTime, wallet, this.token.address, initialRate, finalRate
);
await this.token.transfer(this.crowdsale.address, tokenSupply);
});
it('at start', async function () {
await increaseTimeTo(this.startTime);
await this.crowdsale.buyTokens(investor, { value, from: purchaser });
balance = await this.token.balanceOf(investor);
balance.should.be.bignumber.equal(value.mul(initialRate));
});
it('at time 150', async function () {
await increaseTimeTo(this.startTime + 150);
await this.crowdsale.buyTokens(investor, { value, from: purchaser });
balance = await this.token.balanceOf(investor);
balance.should.be.bignumber.equal(value.mul(rateAtTime150));
});
it('at time 300', async function () {
await increaseTimeTo(this.startTime + 300);
await this.crowdsale.buyTokens(investor, { value, from: purchaser });
balance = await this.token.balanceOf(investor);
balance.should.be.bignumber.equal(value.mul(rateAtTime300));
});
it('at time 1500', async function () {
await increaseTimeTo(this.startTime + 1500);
await this.crowdsale.buyTokens(investor, { value, from: purchaser });
balance = await this.token.balanceOf(investor);
balance.should.be.bignumber.equal(value.mul(rateAtTime1500));
});
it('at time 30', async function () {
await increaseTimeTo(this.startTime + 30);
await this.crowdsale.buyTokens(investor, { value, from: purchaser });
balance = await this.token.balanceOf(investor);
balance.should.be.bignumber.equal(value.mul(rateAtTime30));
});
it('at time 150000', async function () {
await increaseTimeTo(this.startTime + 150000);
await this.crowdsale.buyTokens(investor, { value, from: purchaser });
balance = await this.token.balanceOf(investor);
balance.should.be.bignumber.equal(value.mul(rateAtTime150000));
});
it('at time 450000', async function () {
await increaseTimeTo(this.startTime + 450000);
await this.crowdsale.buyTokens(investor, { value, from: purchaser });
balance = await this.token.balanceOf(investor);
balance.should.be.bignumber.equal(value.mul(rateAtTime450000));
});
});
});
import ether from '../helpers/ether';
import EVMRevert from '../helpers/EVMRevert';
const BigNumber = web3.BigNumber;
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();
const CappedCrowdsale = artifacts.require('IndividuallyCappedCrowdsaleImpl');
const SimpleToken = artifacts.require('SimpleToken');
contract('IndividuallyCappedCrowdsale', function ([_, wallet, alice, bob, charlie]) {
const rate = new BigNumber(1);
const capAlice = ether(10);
const capBob = ether(2);
const lessThanCapAlice = ether(6);
const lessThanCapBoth = ether(1);
const tokenSupply = new BigNumber('1e22');
describe('individual capping', function () {
beforeEach(async function () {
this.token = await SimpleToken.new();
this.crowdsale = await CappedCrowdsale.new(rate, wallet, this.token.address);
await this.crowdsale.setUserCap(alice, capAlice);
await this.crowdsale.setUserCap(bob, capBob);
await this.token.transfer(this.crowdsale.address, tokenSupply);
});
describe('accepting payments', function () {
it('should accept payments within cap', async function () {
await this.crowdsale.buyTokens(alice, { value: lessThanCapAlice }).should.be.fulfilled;
await this.crowdsale.buyTokens(bob, { value: lessThanCapBoth }).should.be.fulfilled;
});
it('should reject payments outside cap', async function () {
await this.crowdsale.buyTokens(alice, { value: capAlice });
await this.crowdsale.buyTokens(alice, { value: 1 }).should.be.rejectedWith(EVMRevert);
});
it('should reject payments that exceed cap', async function () {
await this.crowdsale.buyTokens(alice, { value: capAlice.plus(1) }).should.be.rejectedWith(EVMRevert);
await this.crowdsale.buyTokens(bob, { value: capBob.plus(1) }).should.be.rejectedWith(EVMRevert);
});
it('should manage independent caps', async function () {
await this.crowdsale.buyTokens(alice, { value: lessThanCapAlice }).should.be.fulfilled;
await this.crowdsale.buyTokens(bob, { value: lessThanCapAlice }).should.be.rejectedWith(EVMRevert);
});
it('should default to a cap of zero', async function () {
await this.crowdsale.buyTokens(charlie, { value: lessThanCapBoth }).should.be.rejectedWith(EVMRevert);
});
});
describe('reporting state', function () {
it('should report correct cap', async function () {
let retrievedCap = await this.crowdsale.getUserCap(alice);
retrievedCap.should.be.bignumber.equal(capAlice);
});
it('should report actual contribution', async function () {
await this.crowdsale.buyTokens(alice, { value: lessThanCapAlice });
let retrievedContribution = await this.crowdsale.getUserContribution(alice);
retrievedContribution.should.be.bignumber.equal(lessThanCapAlice);
});
});
});
describe('group capping', function () {
beforeEach(async function () {
this.token = await SimpleToken.new();
this.crowdsale = await CappedCrowdsale.new(rate, wallet, this.token.address);
await this.crowdsale.setGroupCap([bob, charlie], capBob);
await this.token.transfer(this.crowdsale.address, tokenSupply);
});
describe('accepting payments', function () {
it('should accept payments within cap', async function () {
await this.crowdsale.buyTokens(bob, { value: lessThanCapBoth }).should.be.fulfilled;
await this.crowdsale.buyTokens(charlie, { value: lessThanCapBoth }).should.be.fulfilled;
});
it('should reject payments outside cap', async function () {
await this.crowdsale.buyTokens(bob, { value: capBob });
await this.crowdsale.buyTokens(bob, { value: 1 }).should.be.rejectedWith(EVMRevert);
await this.crowdsale.buyTokens(charlie, { value: capBob });
await this.crowdsale.buyTokens(charlie, { value: 1 }).should.be.rejectedWith(EVMRevert);
});
it('should reject payments that exceed cap', async function () {
await this.crowdsale.buyTokens(bob, { value: capBob.plus(1) }).should.be.rejectedWith(EVMRevert);
await this.crowdsale.buyTokens(charlie, { value: capBob.plus(1) }).should.be.rejectedWith(EVMRevert);
});
});
describe('reporting state', function () {
it('should report correct cap', async function () {
let retrievedCapBob = await this.crowdsale.getUserCap(bob);
retrievedCapBob.should.be.bignumber.equal(capBob);
let retrievedCapCharlie = await this.crowdsale.getUserCap(charlie);
retrievedCapCharlie.should.be.bignumber.equal(capBob);
});
});
});
});
import ether from '../helpers/ether';
const BigNumber = web3.BigNumber;
const should = require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();
const MintedCrowdsale = artifacts.require('MintedCrowdsaleImpl');
const MintableToken = artifacts.require('MintableToken');
contract('MintedCrowdsale', function ([_, investor, wallet, purchaser]) {
const rate = new BigNumber(1000);
const value = ether(42);
const expectedTokenAmount = rate.mul(value);
beforeEach(async function () {
this.token = await MintableToken.new();
this.crowdsale = await MintedCrowdsale.new(rate, wallet, this.token.address);
await this.token.transferOwnership(this.crowdsale.address);
});
describe('accepting payments', function () {
it('should be token owner', async function () {
const owner = await this.token.owner();
owner.should.equal(this.crowdsale.address);
});
it('should accept payments', async function () {
await this.crowdsale.send(value).should.be.fulfilled;
await this.crowdsale.buyTokens(investor, { value: value, from: purchaser }).should.be.fulfilled;
});
});
describe('high-level purchase', function () {
it('should log purchase', async function () {
const { logs } = await this.crowdsale.sendTransaction({ value: value, from: investor });
const event = logs.find(e => e.event === 'TokenPurchase');
should.exist(event);
event.args.purchaser.should.equal(investor);
event.args.beneficiary.should.equal(investor);
event.args.value.should.be.bignumber.equal(value);
event.args.amount.should.be.bignumber.equal(expectedTokenAmount);
});
it('should assign tokens to sender', async function () {
await this.crowdsale.sendTransaction({ value: value, from: investor });
let balance = await this.token.balanceOf(investor);
balance.should.be.bignumber.equal(expectedTokenAmount);
});
it('should forward funds to wallet', async function () {
const pre = web3.eth.getBalance(wallet);
await this.crowdsale.sendTransaction({ value, from: investor });
const post = web3.eth.getBalance(wallet);
post.minus(pre).should.be.bignumber.equal(value);
});
});
});
import { advanceBlock } from '../helpers/advanceToBlock';
import { increaseTimeTo, duration } from '../helpers/increaseTime';
import latestTime from '../helpers/latestTime';
import EVMRevert from '../helpers/EVMRevert';
import ether from '../helpers/ether';
const BigNumber = web3.BigNumber;
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();
const PostDeliveryCrowdsale = artifacts.require('PostDeliveryCrowdsaleImpl');
const SimpleToken = artifacts.require('SimpleToken');
contract('PostDeliveryCrowdsale', function ([_, investor, wallet, purchaser]) {
const rate = new BigNumber(1);
const value = ether(42);
const tokenSupply = new BigNumber('1e22');
before(async function () {
// Advance to the next block to correctly read time in the solidity "now" function interpreted by ganache
await advanceBlock();
});
beforeEach(async function () {
this.openingTime = latestTime() + duration.weeks(1);
this.closingTime = this.openingTime + duration.weeks(1);
this.beforeEndTime = this.closingTime - duration.hours(1);
this.afterClosingTime = this.closingTime + duration.seconds(1);
this.token = await SimpleToken.new();
this.crowdsale = await PostDeliveryCrowdsale.new(
this.openingTime, this.closingTime, rate, wallet, this.token.address
);
await this.token.transfer(this.crowdsale.address, tokenSupply);
});
it('should not immediately assign tokens to beneficiary', async function () {
await increaseTimeTo(this.openingTime);
await this.crowdsale.buyTokens(investor, { value: value, from: purchaser });
const balance = await this.token.balanceOf(investor);
balance.should.be.bignumber.equal(0);
});
it('should not allow beneficiaries to withdraw tokens before crowdsale ends', async function () {
await increaseTimeTo(this.beforeEndTime);
await this.crowdsale.buyTokens(investor, { value: value, from: purchaser });
await this.crowdsale.withdrawTokens({ from: investor }).should.be.rejectedWith(EVMRevert);
});
it('should allow beneficiaries to withdraw tokens after crowdsale ends', async function () {
await increaseTimeTo(this.openingTime);
await this.crowdsale.buyTokens(investor, { value: value, from: purchaser });
await increaseTimeTo(this.afterClosingTime);
await this.crowdsale.withdrawTokens({ from: investor }).should.be.fulfilled;
});
it('should return the amount of tokens bought', async function () {
await increaseTimeTo(this.openingTime);
await this.crowdsale.buyTokens(investor, { value: value, from: purchaser });
await increaseTimeTo(this.afterClosingTime);
await this.crowdsale.withdrawTokens({ from: investor });
const balance = await this.token.balanceOf(investor);
balance.should.be.bignumber.equal(value);
});
});
import ether from '../helpers/ether';
import EVMRevert from '../helpers/EVMRevert';
const BigNumber = web3.BigNumber;
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();
const RefundVault = artifacts.require('RefundVault');
contract('RefundVault', function ([_, owner, wallet, investor]) {
const value = ether(42);
beforeEach(async function () {
this.vault = await RefundVault.new(wallet, { from: owner });
});
it('should accept contributions', async function () {
await this.vault.deposit(investor, { value, from: owner }).should.be.fulfilled;
});
it('should not refund contribution during active state', async function () {
await this.vault.deposit(investor, { value, from: owner });
await this.vault.refund(investor).should.be.rejectedWith(EVMRevert);
});
it('only owner can enter refund mode', async function () {
await this.vault.enableRefunds({ from: _ }).should.be.rejectedWith(EVMRevert);
await this.vault.enableRefunds({ from: owner }).should.be.fulfilled;
});
it('should refund contribution after entering refund mode', async function () {
await this.vault.deposit(investor, { value, from: owner });
await this.vault.enableRefunds({ from: owner });
const pre = web3.eth.getBalance(investor);
await this.vault.refund(investor);
const post = web3.eth.getBalance(investor);
post.minus(pre).should.be.bignumber.equal(value);
});
it('only owner can close', async function () {
await this.vault.close({ from: _ }).should.be.rejectedWith(EVMRevert);
await this.vault.close({ from: owner }).should.be.fulfilled;
});
it('should forward funds to wallet after closing', async function () {
await this.vault.deposit(investor, { value, from: owner });
const pre = web3.eth.getBalance(wallet);
await this.vault.close({ from: owner });
const post = web3.eth.getBalance(wallet);
post.minus(pre).should.be.bignumber.equal(value);
});
});
import ether from '../helpers/ether';
import { advanceBlock } from '../helpers/advanceToBlock';
import { increaseTimeTo, duration } from '../helpers/increaseTime';
import latestTime from '../helpers/latestTime';
import EVMRevert from '../helpers/EVMRevert';
const BigNumber = web3.BigNumber;
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();
const RefundableCrowdsale = artifacts.require('RefundableCrowdsaleImpl');
const SimpleToken = artifacts.require('SimpleToken');
contract('RefundableCrowdsale', function ([_, owner, wallet, investor, purchaser]) {
const rate = new BigNumber(1);
const goal = ether(50);
const lessThanGoal = ether(45);
const tokenSupply = new BigNumber('1e22');
before(async function () {
// Advance to the next block to correctly read time in the solidity "now" function interpreted by ganache
await advanceBlock();
});
beforeEach(async function () {
this.openingTime = latestTime() + duration.weeks(1);
this.closingTime = this.openingTime + duration.weeks(1);
this.afterClosingTime = this.closingTime + duration.seconds(1);
this.token = await SimpleToken.new();
this.crowdsale = await RefundableCrowdsale.new(
this.openingTime, this.closingTime, rate, wallet, this.token.address, goal, { from: owner }
);
await this.token.transfer(this.crowdsale.address, tokenSupply);
});
describe('creating a valid crowdsale', function () {
it('should fail with zero goal', async function () {
await RefundableCrowdsale.new(
this.openingTime, this.closingTime, rate, wallet, this.token.address, 0, { from: owner }
).should.be.rejectedWith(EVMRevert);
});
});
it('should deny refunds before end', async function () {
await this.crowdsale.claimRefund({ from: investor }).should.be.rejectedWith(EVMRevert);
await increaseTimeTo(this.openingTime);
await this.crowdsale.claimRefund({ from: investor }).should.be.rejectedWith(EVMRevert);
});
it('should deny refunds after end if goal was reached', async function () {
await increaseTimeTo(this.openingTime);
await this.crowdsale.sendTransaction({ value: goal, from: investor });
await increaseTimeTo(this.afterClosingTime);
await this.crowdsale.claimRefund({ from: investor }).should.be.rejectedWith(EVMRevert);
});
it('should allow refunds after end if goal was not reached', async function () {
await increaseTimeTo(this.openingTime);
await this.crowdsale.sendTransaction({ value: lessThanGoal, from: investor });
await increaseTimeTo(this.afterClosingTime);
await this.crowdsale.finalize({ from: owner });
const pre = web3.eth.getBalance(investor);
await this.crowdsale.claimRefund({ from: investor, gasPrice: 0 })
.should.be.fulfilled;
const post = web3.eth.getBalance(investor);
post.minus(pre).should.be.bignumber.equal(lessThanGoal);
});
it('should forward funds to wallet after end if goal was reached', async function () {
await increaseTimeTo(this.openingTime);
await this.crowdsale.sendTransaction({ value: goal, from: investor });
await increaseTimeTo(this.afterClosingTime);
const pre = web3.eth.getBalance(wallet);
await this.crowdsale.finalize({ from: owner });
const post = web3.eth.getBalance(wallet);
post.minus(pre).should.be.bignumber.equal(goal);
});
});
import ether from '../helpers/ether';
import { advanceBlock } from '../helpers/advanceToBlock';
import { increaseTimeTo, duration } from '../helpers/increaseTime';
import latestTime from '../helpers/latestTime';
import EVMRevert from '../helpers/EVMRevert';
const BigNumber = web3.BigNumber;
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();
const TimedCrowdsale = artifacts.require('TimedCrowdsaleImpl');
const SimpleToken = artifacts.require('SimpleToken');
contract('TimedCrowdsale', function ([_, investor, wallet, purchaser]) {
const rate = new BigNumber(1);
const value = ether(42);
const tokenSupply = new BigNumber('1e22');
before(async function () {
// Advance to the next block to correctly read time in the solidity "now" function interpreted by ganache
await advanceBlock();
});
beforeEach(async function () {
this.openingTime = latestTime() + duration.weeks(1);
this.closingTime = this.openingTime + duration.weeks(1);
this.afterClosingTime = this.closingTime + duration.seconds(1);
this.token = await SimpleToken.new();
this.crowdsale = await TimedCrowdsale.new(this.openingTime, this.closingTime, rate, wallet, this.token.address);
await this.token.transfer(this.crowdsale.address, tokenSupply);
});
it('should be ended only after end', async function () {
let ended = await this.crowdsale.hasClosed();
ended.should.equal(false);
await increaseTimeTo(this.afterClosingTime);
ended = await this.crowdsale.hasClosed();
ended.should.equal(true);
});
describe('accepting payments', function () {
it('should reject payments before start', async function () {
await this.crowdsale.send(value).should.be.rejectedWith(EVMRevert);
await this.crowdsale.buyTokens(investor, { from: purchaser, value: value }).should.be.rejectedWith(EVMRevert);
});
it('should accept payments after start', async function () {
await increaseTimeTo(this.openingTime);
await this.crowdsale.send(value).should.be.fulfilled;
await this.crowdsale.buyTokens(investor, { value: value, from: purchaser }).should.be.fulfilled;
});
it('should reject payments after end', async function () {
await increaseTimeTo(this.afterClosingTime);
await this.crowdsale.send(value).should.be.rejectedWith(EVMRevert);
await this.crowdsale.buyTokens(investor, { value: value, from: purchaser }).should.be.rejectedWith(EVMRevert);
});
});
});
import ether from '../helpers/ether';
const BigNumber = web3.BigNumber;
require('chai')
.use(require('chai-as-promised'))
.should();
const WhitelistedCrowdsale = artifacts.require('WhitelistedCrowdsaleImpl');
const SimpleToken = artifacts.require('SimpleToken');
contract('WhitelistedCrowdsale', function ([_, wallet, authorized, unauthorized, anotherAuthorized]) {
const rate = 1;
const value = ether(42);
const tokenSupply = new BigNumber('1e22');
describe('single user whitelisting', function () {
beforeEach(async function () {
this.token = await SimpleToken.new();
this.crowdsale = await WhitelistedCrowdsale.new(rate, wallet, this.token.address);
await this.token.transfer(this.crowdsale.address, tokenSupply);
await this.crowdsale.addToWhitelist(authorized);
});
describe('accepting payments', function () {
it('should accept payments to whitelisted (from whichever buyers)', async function () {
await this.crowdsale.buyTokens(authorized, { value: value, from: authorized }).should.be.fulfilled;
await this.crowdsale.buyTokens(authorized, { value: value, from: unauthorized }).should.be.fulfilled;
});
it('should reject payments to not whitelisted (from whichever buyers)', async function () {
await this.crowdsale.send(value).should.be.rejected;
await this.crowdsale.buyTokens(unauthorized, { value: value, from: unauthorized }).should.be.rejected;
await this.crowdsale.buyTokens(unauthorized, { value: value, from: authorized }).should.be.rejected;
});
it('should reject payments to addresses removed from whitelist', async function () {
await this.crowdsale.removeFromWhitelist(authorized);
await this.crowdsale.buyTokens(authorized, { value: value, from: authorized }).should.be.rejected;
});
});
describe('reporting whitelisted', function () {
it('should correctly report whitelisted addresses', async function () {
let isAuthorized = await this.crowdsale.whitelist(authorized);
isAuthorized.should.equal(true);
let isntAuthorized = await this.crowdsale.whitelist(unauthorized);
isntAuthorized.should.equal(false);
});
});
});
describe('many user whitelisting', function () {
beforeEach(async function () {
this.token = await SimpleToken.new();
this.crowdsale = await WhitelistedCrowdsale.new(rate, wallet, this.token.address);
await this.token.transfer(this.crowdsale.address, tokenSupply);
await this.crowdsale.addManyToWhitelist([authorized, anotherAuthorized]);
});
describe('accepting payments', function () {
it('should accept payments to whitelisted (from whichever buyers)', async function () {
await this.crowdsale.buyTokens(authorized, { value: value, from: authorized }).should.be.fulfilled;
await this.crowdsale.buyTokens(authorized, { value: value, from: unauthorized }).should.be.fulfilled;
await this.crowdsale.buyTokens(anotherAuthorized, { value: value, from: authorized }).should.be.fulfilled;
await this.crowdsale.buyTokens(anotherAuthorized, { value: value, from: unauthorized }).should.be.fulfilled;
});
it('should reject payments to not whitelisted (with whichever buyers)', async function () {
await this.crowdsale.send(value).should.be.rejected;
await this.crowdsale.buyTokens(unauthorized, { value: value, from: unauthorized }).should.be.rejected;
await this.crowdsale.buyTokens(unauthorized, { value: value, from: authorized }).should.be.rejected;
});
it('should reject payments to addresses removed from whitelist', async function () {
await this.crowdsale.removeFromWhitelist(anotherAuthorized);
await this.crowdsale.buyTokens(authorized, { value: value, from: authorized }).should.be.fulfilled;
await this.crowdsale.buyTokens(anotherAuthorized, { value: value, from: authorized }).should.be.rejected;
});
});
describe('reporting whitelisted', function () {
it('should correctly report whitelisted addresses', async function () {
let isAuthorized = await this.crowdsale.whitelist(authorized);
isAuthorized.should.equal(true);
let isAnotherAuthorized = await this.crowdsale.whitelist(anotherAuthorized);
isAnotherAuthorized.should.equal(true);
let isntAuthorized = await this.crowdsale.whitelist(unauthorized);
isntAuthorized.should.equal(false);
});
});
});
});
import ether from '../helpers/ether';
import { advanceBlock } from '../helpers/advanceToBlock';
import { increaseTimeTo, duration } from '../helpers/increaseTime';
import latestTime from '../helpers/latestTime';
import EVMRevert from '../helpers/EVMRevert';
import assertRevert from '../helpers/assertRevert';
const BigNumber = web3.BigNumber;
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();
const SampleCrowdsale = artifacts.require('SampleCrowdsale');
const SampleCrowdsaleToken = artifacts.require('SampleCrowdsaleToken');
const RefundVault = artifacts.require('RefundVault');
contract('SampleCrowdsale', function ([owner, wallet, investor]) {
const RATE = new BigNumber(10);
const GOAL = ether(10);
const CAP = ether(20);
before(async function () {
// Advance to the next block to correctly read time in the solidity "now" function interpreted by ganache
await advanceBlock();
});
beforeEach(async function () {
this.openingTime = latestTime() + duration.weeks(1);
this.closingTime = this.openingTime + duration.weeks(1);
this.afterClosingTime = this.closingTime + duration.seconds(1);
this.token = await SampleCrowdsaleToken.new({ from: owner });
this.vault = await RefundVault.new(wallet, { from: owner });
this.crowdsale = await SampleCrowdsale.new(
this.openingTime, this.closingTime, RATE, wallet, CAP, this.token.address, GOAL
);
await this.token.transferOwnership(this.crowdsale.address);
await this.vault.transferOwnership(this.crowdsale.address);
});
it('should create crowdsale with correct parameters', async function () {
this.crowdsale.should.exist;
this.token.should.exist;
const openingTime = await this.crowdsale.openingTime();
const closingTime = await this.crowdsale.closingTime();
const rate = await this.crowdsale.rate();
const walletAddress = await this.crowdsale.wallet();
const goal = await this.crowdsale.goal();
const cap = await this.crowdsale.cap();
openingTime.should.be.bignumber.equal(this.openingTime);
closingTime.should.be.bignumber.equal(this.closingTime);
rate.should.be.bignumber.equal(RATE);
walletAddress.should.be.equal(wallet);
goal.should.be.bignumber.equal(GOAL);
cap.should.be.bignumber.equal(CAP);
});
it('should not accept payments before start', async function () {
await this.crowdsale.send(ether(1)).should.be.rejectedWith(EVMRevert);
await this.crowdsale.buyTokens(investor, { from: investor, value: ether(1) }).should.be.rejectedWith(EVMRevert);
});
it('should accept payments during the sale', async function () {
const investmentAmount = ether(1);
const expectedTokenAmount = RATE.mul(investmentAmount);
await increaseTimeTo(this.openingTime);
await this.crowdsale.buyTokens(investor, { value: investmentAmount, from: investor }).should.be.fulfilled;
(await this.token.balanceOf(investor)).should.be.bignumber.equal(expectedTokenAmount);
(await this.token.totalSupply()).should.be.bignumber.equal(expectedTokenAmount);
});
it('should reject payments after end', async function () {
await increaseTimeTo(this.afterEnd);
await this.crowdsale.send(ether(1)).should.be.rejectedWith(EVMRevert);
await this.crowdsale.buyTokens(investor, { value: ether(1), from: investor }).should.be.rejectedWith(EVMRevert);
});
it('should reject payments over cap', async function () {
await increaseTimeTo(this.openingTime);
await this.crowdsale.send(CAP);
await this.crowdsale.send(1).should.be.rejectedWith(EVMRevert);
});
it('should allow finalization and transfer funds to wallet if the goal is reached', async function () {
await increaseTimeTo(this.openingTime);
await this.crowdsale.send(GOAL);
const beforeFinalization = web3.eth.getBalance(wallet);
await increaseTimeTo(this.afterClosingTime);
await this.crowdsale.finalize({ from: owner });
const afterFinalization = web3.eth.getBalance(wallet);
afterFinalization.minus(beforeFinalization).should.be.bignumber.equal(GOAL);
});
it('should allow refunds if the goal is not reached', async function () {
const balanceBeforeInvestment = web3.eth.getBalance(investor);
await increaseTimeTo(this.openingTime);
await this.crowdsale.sendTransaction({ value: ether(1), from: investor, gasPrice: 0 });
await increaseTimeTo(this.afterClosingTime);
await this.crowdsale.finalize({ from: owner });
await this.crowdsale.claimRefund({ from: investor, gasPrice: 0 }).should.be.fulfilled;
const balanceAfterRefund = web3.eth.getBalance(investor);
balanceBeforeInvestment.should.be.bignumber.equal(balanceAfterRefund);
});
describe('when goal > cap', function () {
// goal > cap
const HIGH_GOAL = ether(30);
it('creation reverts', async function () {
await assertRevert(SampleCrowdsale.new(
this.openingTime, this.closingTime, RATE, wallet, CAP, this.token.address, HIGH_GOAL
));
});
});
});
import decodeLogs from '../helpers/decodeLogs';
const SimpleToken = artifacts.require('SimpleToken');
contract('SimpleToken', accounts => {
let token;
const creator = accounts[0];
beforeEach(async function () {
token = await SimpleToken.new({ from: creator });
});
it('has a name', async function () {
const name = await token.name();
assert.equal(name, 'SimpleToken');
});
it('has a symbol', async function () {
const symbol = await token.symbol();
assert.equal(symbol, 'SIM');
});
it('has 18 decimals', async function () {
const decimals = await token.decimals();
assert(decimals.eq(18));
});
it('assigns the initial total supply to the creator', async function () {
const totalSupply = await token.totalSupply();
const creatorBalance = await token.balanceOf(creator);
assert(creatorBalance.eq(totalSupply));
const receipt = web3.eth.getTransactionReceipt(token.transactionHash);
const logs = decodeLogs(receipt.logs, SimpleToken, token.address);
assert.equal(logs.length, 1);
assert.equal(logs[0].event, 'Transfer');
assert.equal(logs[0].args.from.valueOf(), 0x0);
assert.equal(logs[0].args.to.valueOf(), creator);
assert(logs[0].args.value.eq(totalSupply));
});
});
import {
hashMessage,
signMessage,
} from '../helpers/sign';
const ECRecoveryMock = artifacts.require('ECRecoveryMock');
require('chai')
.use(require('chai-as-promised'))
.should();
contract('ECRecovery', function (accounts) {
let ecrecovery;
const TEST_MESSAGE = 'OpenZeppelin';
before(async function () {
ecrecovery = await ECRecoveryMock.new();
});
it('recover v0', async function () {
// Signature generated outside ganache with method web3.eth.sign(signer, message)
let signer = '0x2cc1166f6212628a0deef2b33befb2187d35b86c';
let message = web3.sha3(TEST_MESSAGE);
// eslint-disable-next-line max-len
let signature = '0x5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be89200';
const addrRecovered = await ecrecovery.recover(message, signature);
addrRecovered.should.eq(signer);
});
it('recover v1', async function () {
// Signature generated outside ganache with method web3.eth.sign(signer, message)
let signer = '0x1e318623ab09fe6de3c9b8672098464aeda9100e';
let message = web3.sha3(TEST_MESSAGE);
// eslint-disable-next-line max-len
let signature = '0x331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e001';
const addrRecovered = await ecrecovery.recover(message, signature);
addrRecovered.should.eq(signer);
});
it('recover using web3.eth.sign()', async function () {
// Create the signature using account[0]
const signature = signMessage(accounts[0], TEST_MESSAGE);
// Recover the signer address from the generated message and signature.
const addrRecovered = await ecrecovery.recover(
hashMessage(TEST_MESSAGE),
signature
);
addrRecovered.should.eq(accounts[0]);
});
it('recover using web3.eth.sign() should return wrong signer', async function () {
// Create the signature using account[0]
const signature = signMessage(accounts[0], TEST_MESSAGE);
// Recover the signer address from the generated message and wrong signature.
const addrRecovered = await ecrecovery.recover(hashMessage('Test'), signature);
assert.notEqual(accounts[0], addrRecovered);
});
it('recover should fail when a wrong hash is sent', async function () {
// Create the signature using account[0]
let signature = signMessage(accounts[0], TEST_MESSAGE);
// Recover the signer address from the generated message and wrong signature.
const addrRecovered = await ecrecovery.recover(hashMessage(TEST_MESSAGE).substring(2), signature);
addrRecovered.should.eq('0x0000000000000000000000000000000000000000');
});
context('toEthSignedMessage', () => {
it('should prefix hashes correctly', async function () {
const hashedMessage = web3.sha3(TEST_MESSAGE);
const ethMessage = await ecrecovery.toEthSignedMessageHash(hashedMessage);
ethMessage.should.eq(hashMessage(TEST_MESSAGE));
});
});
});
var MathMock = artifacts.require('MathMock');
contract('Math', function (accounts) {
let math;
before(async function () {
math = await MathMock.new();
});
it('returns max64 correctly', async function () {
let a = 5678;
let b = 1234;
await math.max64(a, b);
let result = await math.result64();
assert.equal(result, a);
});
it('returns min64 correctly', async function () {
let a = 5678;
let b = 1234;
await math.min64(a, b);
let result = await math.result64();
assert.equal(result, b);
});
it('returns max256 correctly', async function () {
let a = 5678;
let b = 1234;
await math.max256(a, b);
let result = await math.result256();
assert.equal(result, a);
});
it('returns min256 correctly', async function () {
let a = 5678;
let b = 1234;
await math.min256(a, b);
let result = await math.result256();
assert.equal(result, b);
});
});
import MerkleTree from '../helpers/merkleTree.js';
import { sha3, bufferToHex } from 'ethereumjs-util';
var MerkleProofWrapper = artifacts.require('MerkleProofWrapper');
contract('MerkleProof', function (accounts) {
let merkleProof;
before(async function () {
merkleProof = await MerkleProofWrapper.new();
});
describe('verifyProof', function () {
it('should return true for a valid Merkle proof', async function () {
const elements = ['a', 'b', 'c', 'd'];
const merkleTree = new MerkleTree(elements);
const root = merkleTree.getHexRoot();
const proof = merkleTree.getHexProof(elements[0]);
const leaf = bufferToHex(sha3(elements[0]));
const result = await merkleProof.verifyProof(proof, root, leaf);
assert.isOk(result, 'verifyProof did not return true for a valid proof');
});
it('should return false for an invalid Merkle proof', async function () {
const correctElements = ['a', 'b', 'c'];
const correctMerkleTree = new MerkleTree(correctElements);
const correctRoot = correctMerkleTree.getHexRoot();
const correctLeaf = bufferToHex(sha3(correctElements[0]));
const badElements = ['d', 'e', 'f'];
const badMerkleTree = new MerkleTree(badElements);
const badProof = badMerkleTree.getHexProof(badElements[0]);
const result = await merkleProof.verifyProof(badProof, correctRoot, correctLeaf);
assert.isNotOk(result, 'verifyProof did not return false for an invalid proof');
});
it('should return false for a Merkle proof of invalid length', async function () {
const elements = ['a', 'b', 'c'];
const merkleTree = new MerkleTree(elements);
const root = merkleTree.getHexRoot();
const proof = merkleTree.getHexProof(elements[0]);
const badProof = proof.slice(0, proof.length - 5);
const leaf = bufferToHex(sha3(elements[0]));
const result = await merkleProof.verifyProof(badProof, root, leaf);
assert.isNotOk(result, 'verifyProof did not return false for proof of invalid length');
});
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment