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');
});
});
});
var Destructible = artifacts.require('Destructible');
require('../helpers/transactionMined.js');
contract('Destructible', function (accounts) {
it('should send balance to owner after destruction', async function () {
let destructible = await Destructible.new({ from: accounts[0], value: web3.toWei('10', 'ether') });
let owner = await destructible.owner();
let initBalance = web3.eth.getBalance(owner);
await destructible.destroy({ from: owner });
let newBalance = web3.eth.getBalance(owner);
assert.isTrue(newBalance > initBalance);
});
it('should send balance to recepient after destruction', async function () {
let destructible = await Destructible.new({ from: accounts[0], value: web3.toWei('10', 'ether') });
let owner = await destructible.owner();
let initBalance = web3.eth.getBalance(accounts[1]);
await destructible.destroyAndSend(accounts[1], { from: owner });
let newBalance = web3.eth.getBalance(accounts[1]);
assert.isTrue(newBalance.greaterThan(initBalance));
});
});
var TokenDestructible = artifacts.require('TokenDestructible');
var StandardTokenMock = artifacts.require('StandardTokenMock');
require('../helpers/transactionMined.js');
contract('TokenDestructible', function (accounts) {
let destructible;
beforeEach(async function () {
destructible = await TokenDestructible.new({
from: accounts[0],
value: web3.toWei('10', 'ether'),
});
});
it('should send balance to owner after destruction', async function () {
let owner = await destructible.owner();
let initBalance = web3.eth.getBalance(owner);
await destructible.destroy([], { from: owner });
let newBalance = web3.eth.getBalance(owner);
assert.isTrue(newBalance > initBalance);
});
it('should send tokens to owner after destruction', async function () {
let owner = await destructible.owner();
let token = await StandardTokenMock.new(destructible.address, 100);
let initContractBalance = await token.balanceOf(destructible.address);
let initOwnerBalance = await token.balanceOf(owner);
assert.equal(initContractBalance, 100);
assert.equal(initOwnerBalance, 0);
await destructible.destroy([token.address], { from: owner });
let newContractBalance = await token.balanceOf(destructible.address);
let newOwnerBalance = await token.balanceOf(owner);
assert.equal(newContractBalance, 0);
assert.equal(newOwnerBalance, 100);
});
});
pragma solidity ^0.4.21;
import "../../contracts/math/Math.sol";
contract MathMock {
uint64 public result64;
uint256 public result256;
function max64(uint64 a, uint64 b) public {
result64 = Math.max64(a, b);
}
function min64(uint64 a, uint64 b) public {
result64 = Math.min64(a, b);
}
function max256(uint256 a, uint256 b) public {
result256 = Math.max256(a, b);
}
function min256(uint256 a, uint256 b) public {
result256 = Math.min256(a, b);
}
}
import expectThrow from '../helpers/expectThrow';
const CanReclaimToken = artifacts.require('CanReclaimToken');
const BasicTokenMock = artifacts.require('BasicTokenMock');
contract('CanReclaimToken', function (accounts) {
let token = null;
let canReclaimToken = null;
beforeEach(async function () {
// Create contract and token
token = await BasicTokenMock.new(accounts[0], 100);
canReclaimToken = await CanReclaimToken.new();
// Force token into contract
await token.transfer(canReclaimToken.address, 10);
const startBalance = await token.balanceOf(canReclaimToken.address);
assert.equal(startBalance, 10);
});
it('should allow owner to reclaim tokens', async function () {
const ownerStartBalance = await token.balanceOf(accounts[0]);
await canReclaimToken.reclaimToken(token.address);
const ownerFinalBalance = await token.balanceOf(accounts[0]);
const finalBalance = await token.balanceOf(canReclaimToken.address);
assert.equal(finalBalance, 0);
assert.equal(ownerFinalBalance - ownerStartBalance, 10);
});
it('should allow only owner to reclaim tokens', async function () {
await expectThrow(
canReclaimToken.reclaimToken(token.address, { from: accounts[1] }),
);
});
});
import assertRevert from '../helpers/assertRevert';
var Claimable = artifacts.require('Claimable');
contract('Claimable', function (accounts) {
let claimable;
beforeEach(async function () {
claimable = await Claimable.new();
});
it('should have an owner', async function () {
let owner = await claimable.owner();
assert.isTrue(owner !== 0);
});
it('changes pendingOwner after transfer', async function () {
let newOwner = accounts[1];
await claimable.transferOwnership(newOwner);
let pendingOwner = await claimable.pendingOwner();
assert.isTrue(pendingOwner === newOwner);
});
it('should prevent to claimOwnership from no pendingOwner', async function () {
await assertRevert(claimable.claimOwnership({ from: accounts[2] }));
});
it('should prevent non-owners from transfering', async function () {
const other = accounts[2];
const owner = await claimable.owner.call();
assert.isTrue(owner !== other);
await assertRevert(claimable.transferOwnership(other, { from: other }));
});
describe('after initiating a transfer', function () {
let newOwner;
beforeEach(async function () {
newOwner = accounts[1];
await claimable.transferOwnership(newOwner);
});
it('changes allow pending owner to claim ownership', async function () {
await claimable.claimOwnership({ from: newOwner });
let owner = await claimable.owner();
assert.isTrue(owner === newOwner);
});
});
});
var Contactable = artifacts.require('Contactable');
contract('Contactable', function (accounts) {
let contactable;
beforeEach(async function () {
contactable = await Contactable.new();
});
it('should have an empty contact info', async function () {
let info = await contactable.contactInformation();
assert.isTrue(info === '');
});
describe('after setting the contact information', function () {
let contactInfo = 'contact information';
beforeEach(async function () {
await contactable.setContactInformation(contactInfo);
});
it('should return the setted contact information', async function () {
let info = await contactable.contactInformation();
assert.isTrue(info === contactInfo);
});
});
});
import assertRevert from '../helpers/assertRevert';
var DelayedClaimable = artifacts.require('DelayedClaimable');
contract('DelayedClaimable', function (accounts) {
var delayedClaimable;
beforeEach(function () {
return DelayedClaimable.new().then(function (deployed) {
delayedClaimable = deployed;
});
});
it('can set claim blocks', async function () {
await delayedClaimable.transferOwnership(accounts[2]);
await delayedClaimable.setLimits(0, 1000);
let end = await delayedClaimable.end();
assert.equal(end, 1000);
let start = await delayedClaimable.start();
assert.equal(start, 0);
});
it('changes pendingOwner after transfer successful', async function () {
await delayedClaimable.transferOwnership(accounts[2]);
await delayedClaimable.setLimits(0, 1000);
let end = await delayedClaimable.end();
assert.equal(end, 1000);
let start = await delayedClaimable.start();
assert.equal(start, 0);
let pendingOwner = await delayedClaimable.pendingOwner();
assert.equal(pendingOwner, accounts[2]);
await delayedClaimable.claimOwnership({ from: accounts[2] });
let owner = await delayedClaimable.owner();
assert.equal(owner, accounts[2]);
});
it('changes pendingOwner after transfer fails', async function () {
await delayedClaimable.transferOwnership(accounts[1]);
await delayedClaimable.setLimits(100, 110);
let end = await delayedClaimable.end();
assert.equal(end, 110);
let start = await delayedClaimable.start();
assert.equal(start, 100);
let pendingOwner = await delayedClaimable.pendingOwner();
assert.equal(pendingOwner, accounts[1]);
await assertRevert(delayedClaimable.claimOwnership({ from: accounts[1] }));
let owner = await delayedClaimable.owner();
assert.isTrue(owner !== accounts[1]);
});
it('set end and start invalid values fail', async function () {
await delayedClaimable.transferOwnership(accounts[1]);
await assertRevert(delayedClaimable.setLimits(1001, 1000));
});
});
import expectThrow from '../helpers/expectThrow';
const Ownable = artifacts.require('Ownable');
const HasNoContracts = artifacts.require('HasNoContracts');
contract('HasNoContracts', function (accounts) {
let hasNoContracts = null;
let ownable = null;
beforeEach(async () => {
// Create contract and token
hasNoContracts = await HasNoContracts.new();
ownable = await Ownable.new();
// Force ownership into contract
await ownable.transferOwnership(hasNoContracts.address);
const owner = await ownable.owner();
assert.equal(owner, hasNoContracts.address);
});
it('should allow owner to reclaim contracts', async function () {
await hasNoContracts.reclaimContract(ownable.address);
const owner = await ownable.owner();
assert.equal(owner, accounts[0]);
});
it('should allow only owner to reclaim contracts', async function () {
await expectThrow(
hasNoContracts.reclaimContract(ownable.address, { from: accounts[1] }),
);
});
});
import expectThrow from '../helpers/expectThrow';
import toPromise from '../helpers/toPromise';
const HasNoEtherTest = artifacts.require('HasNoEtherTest');
const ForceEther = artifacts.require('ForceEther');
contract('HasNoEther', function (accounts) {
const amount = web3.toWei('1', 'ether');
it('should be constructorable', async function () {
await HasNoEtherTest.new();
});
it('should not accept ether in constructor', async function () {
await expectThrow(HasNoEtherTest.new({ value: amount }));
});
it('should not accept ether', async function () {
let hasNoEther = await HasNoEtherTest.new();
await expectThrow(
toPromise(web3.eth.sendTransaction)({
from: accounts[1],
to: hasNoEther.address,
value: amount,
}),
);
});
it('should allow owner to reclaim ether', async function () {
// Create contract
let hasNoEther = await HasNoEtherTest.new();
const startBalance = await web3.eth.getBalance(hasNoEther.address);
assert.equal(startBalance, 0);
// Force ether into it
let forceEther = await ForceEther.new({ value: amount });
await forceEther.destroyAndSend(hasNoEther.address);
const forcedBalance = await web3.eth.getBalance(hasNoEther.address);
assert.equal(forcedBalance, amount);
// Reclaim
const ownerStartBalance = await web3.eth.getBalance(accounts[0]);
await hasNoEther.reclaimEther();
const ownerFinalBalance = await web3.eth.getBalance(accounts[0]);
const finalBalance = await web3.eth.getBalance(hasNoEther.address);
assert.equal(finalBalance, 0);
assert.isAbove(ownerFinalBalance, ownerStartBalance);
});
it('should allow only owner to reclaim ether', async function () {
// Create contract
let hasNoEther = await HasNoEtherTest.new({ from: accounts[0] });
// Force ether into it
let forceEther = await ForceEther.new({ value: amount });
await forceEther.destroyAndSend(hasNoEther.address);
const forcedBalance = await web3.eth.getBalance(hasNoEther.address);
assert.equal(forcedBalance, amount);
// Reclaim
await expectThrow(hasNoEther.reclaimEther({ from: accounts[1] }));
});
});
import expectThrow from '../helpers/expectThrow';
const HasNoTokens = artifacts.require('HasNoTokens');
const ERC223TokenMock = artifacts.require('ERC223TokenMock');
contract('HasNoTokens', function (accounts) {
let hasNoTokens = null;
let token = null;
beforeEach(async () => {
// Create contract and token
hasNoTokens = await HasNoTokens.new();
token = await ERC223TokenMock.new(accounts[0], 100);
// Force token into contract
await token.transfer(hasNoTokens.address, 10);
const startBalance = await token.balanceOf(hasNoTokens.address);
assert.equal(startBalance, 10);
});
it('should not accept ERC223 tokens', async function () {
await expectThrow(token.transferERC223(hasNoTokens.address, 10, ''));
});
it('should allow owner to reclaim tokens', async function () {
const ownerStartBalance = await token.balanceOf(accounts[0]);
await hasNoTokens.reclaimToken(token.address);
const ownerFinalBalance = await token.balanceOf(accounts[0]);
const finalBalance = await token.balanceOf(hasNoTokens.address);
assert.equal(finalBalance, 0);
assert.equal(ownerFinalBalance - ownerStartBalance, 10);
});
it('should allow only owner to reclaim tokens', async function () {
await expectThrow(
hasNoTokens.reclaimToken(token.address, { from: accounts[1] }),
);
});
});
import expectThrow from '../helpers/expectThrow';
import expectEvent from '../helpers/expectEvent';
const WhitelistMock = artifacts.require('WhitelistMock');
require('chai')
.use(require('chai-as-promised'))
.should();
contract('Whitelist', function (accounts) {
let mock;
const [
owner,
whitelistedAddress1,
whitelistedAddress2,
anyone,
] = accounts;
const whitelistedAddresses = [whitelistedAddress1, whitelistedAddress2];
before(async function () {
mock = await WhitelistMock.new();
});
context('in normal conditions', () => {
it('should add address to the whitelist', async function () {
await expectEvent.inTransaction(
mock.addAddressToWhitelist(whitelistedAddress1, { from: owner }),
'WhitelistedAddressAdded'
);
const isWhitelisted = await mock.whitelist(whitelistedAddress1);
isWhitelisted.should.be.equal(true);
});
it('should add addresses to the whitelist', async function () {
await expectEvent.inTransaction(
mock.addAddressesToWhitelist(whitelistedAddresses, { from: owner }),
'WhitelistedAddressAdded'
);
for (let addr of whitelistedAddresses) {
const isWhitelisted = await mock.whitelist(addr);
isWhitelisted.should.be.equal(true);
}
});
it('should not announce WhitelistedAddressAdded event if address is already in the whitelist', async function () {
const { logs } = await mock.addAddressToWhitelist(whitelistedAddress1, { from: owner });
logs.should.be.empty;
});
it('should remove address from the whitelist', async function () {
await expectEvent.inTransaction(
mock.removeAddressFromWhitelist(whitelistedAddress1, { from: owner }),
'WhitelistedAddressRemoved'
);
let isWhitelisted = await mock.whitelist(whitelistedAddress1);
isWhitelisted.should.be.equal(false);
});
it('should remove addresses from the the whitelist', async function () {
await expectEvent.inTransaction(
mock.removeAddressesFromWhitelist(whitelistedAddresses, { from: owner }),
'WhitelistedAddressRemoved'
);
for (let addr of whitelistedAddresses) {
const isWhitelisted = await mock.whitelist(addr);
isWhitelisted.should.be.equal(false);
}
});
it('should not announce WhitelistedAddressRemoved event if address is not in the whitelist', async function () {
const { logs } = await mock.removeAddressFromWhitelist(whitelistedAddress1, { from: owner });
logs.should.be.empty;
});
it('should allow whitelisted address to call #onlyWhitelistedCanDoThis', async () => {
await mock.addAddressToWhitelist(whitelistedAddress1, { from: owner });
await mock.onlyWhitelistedCanDoThis({ from: whitelistedAddress1 })
.should.be.fulfilled;
});
});
context('in adversarial conditions', () => {
it('should not allow "anyone" to add to the whitelist', async () => {
await expectThrow(
mock.addAddressToWhitelist(whitelistedAddress1, { from: anyone })
);
});
it('should not allow "anyone" to remove from the whitelist', async () => {
await expectThrow(
mock.removeAddressFromWhitelist(whitelistedAddress1, { from: anyone })
);
});
it('should not allow "anyone" to call #onlyWhitelistedCanDoThis', async () => {
await expectThrow(
mock.onlyWhitelistedCanDoThis({ from: anyone })
);
});
});
});
import expectThrow from '../../helpers/expectThrow';
import expectEvent from '../../helpers/expectEvent';
const RBACMock = artifacts.require('RBACMock');
require('chai')
.use(require('chai-as-promised'))
.should();
const ROLE_ADVISOR = 'advisor';
contract('RBAC', function (accounts) {
let mock;
const [
admin,
anyone,
futureAdvisor,
...advisors
] = accounts;
before(async () => {
mock = await RBACMock.new(advisors, { from: admin });
});
context('in normal conditions', () => {
it('allows admin to call #onlyAdminsCanDoThis', async () => {
await mock.onlyAdminsCanDoThis({ from: admin })
.should.be.fulfilled;
});
it('allows admin to call #onlyAdvisorsCanDoThis', async () => {
await mock.onlyAdvisorsCanDoThis({ from: admin })
.should.be.fulfilled;
});
it('allows advisors to call #onlyAdvisorsCanDoThis', async () => {
await mock.onlyAdvisorsCanDoThis({ from: advisors[0] })
.should.be.fulfilled;
});
it('allows admin to call #eitherAdminOrAdvisorCanDoThis', async () => {
await mock.eitherAdminOrAdvisorCanDoThis({ from: admin })
.should.be.fulfilled;
});
it('allows advisors to call #eitherAdminOrAdvisorCanDoThis', async () => {
await mock.eitherAdminOrAdvisorCanDoThis({ from: advisors[0] })
.should.be.fulfilled;
});
it('does not allow admins to call #nobodyCanDoThis', async () => {
await expectThrow(
mock.nobodyCanDoThis({ from: admin })
);
});
it('does not allow advisors to call #nobodyCanDoThis', async () => {
await expectThrow(
mock.nobodyCanDoThis({ from: advisors[0] })
);
});
it('does not allow anyone to call #nobodyCanDoThis', async () => {
await expectThrow(
mock.nobodyCanDoThis({ from: anyone })
);
});
it('allows an admin to remove an advisor\'s role', async () => {
await mock.removeAdvisor(advisors[0], { from: admin })
.should.be.fulfilled;
});
it('allows admins to #adminRemoveRole', async () => {
await mock.adminRemoveRole(advisors[3], ROLE_ADVISOR, { from: admin })
.should.be.fulfilled;
});
it('announces a RoleAdded event on addRole', async () => {
await expectEvent.inTransaction(
mock.adminAddRole(futureAdvisor, ROLE_ADVISOR, { from: admin }),
'RoleAdded'
);
});
it('announces a RoleRemoved event on removeRole', async () => {
await expectEvent.inTransaction(
mock.adminRemoveRole(futureAdvisor, ROLE_ADVISOR, { from: admin }),
'RoleRemoved'
);
});
});
context('in adversarial conditions', () => {
it('does not allow an advisor to remove another advisor', async () => {
await expectThrow(
mock.removeAdvisor(advisors[1], { from: advisors[0] })
);
});
it('does not allow "anyone" to remove an advisor', async () => {
await expectThrow(
mock.removeAdvisor(advisors[0], { from: anyone })
);
});
});
});
var PullPaymentMock = artifacts.require('PullPaymentMock');
contract('PullPayment', function (accounts) {
let ppce;
let amount = 17 * 1e18;
beforeEach(async function () {
ppce = await PullPaymentMock.new({ value: amount });
});
it('can\'t call asyncSend externally', async function () {
assert.isUndefined(ppce.asyncSend);
});
it('can record an async payment correctly', async function () {
let AMOUNT = 100;
await ppce.callSend(accounts[0], AMOUNT);
let paymentsToAccount0 = await ppce.payments(accounts[0]);
let totalPayments = await ppce.totalPayments();
assert.equal(totalPayments, AMOUNT);
assert.equal(paymentsToAccount0, AMOUNT);
});
it('can add multiple balances on one account', async function () {
await ppce.callSend(accounts[0], 200);
await ppce.callSend(accounts[0], 300);
let paymentsToAccount0 = await ppce.payments(accounts[0]);
let totalPayments = await ppce.totalPayments();
assert.equal(totalPayments, 500);
assert.equal(paymentsToAccount0, 500);
});
it('can add balances on multiple accounts', async function () {
await ppce.callSend(accounts[0], 200);
await ppce.callSend(accounts[1], 300);
let paymentsToAccount0 = await ppce.payments(accounts[0]);
assert.equal(paymentsToAccount0, 200);
let paymentsToAccount1 = await ppce.payments(accounts[1]);
assert.equal(paymentsToAccount1, 300);
let totalPayments = await ppce.totalPayments();
assert.equal(totalPayments, 500);
});
it('can withdraw payment', async function () {
let payee = accounts[1];
let initialBalance = web3.eth.getBalance(payee);
await ppce.callSend(payee, amount);
let payment1 = await ppce.payments(payee);
assert.equal(payment1, amount);
let totalPayments = await ppce.totalPayments();
assert.equal(totalPayments, amount);
await ppce.withdrawPayments({ from: payee });
let payment2 = await ppce.payments(payee);
assert.equal(payment2, 0);
totalPayments = await ppce.totalPayments();
assert.equal(totalPayments, 0);
let balance = web3.eth.getBalance(payee);
assert(Math.abs(balance - initialBalance - amount) < 1e16);
});
});
const BigNumber = web3.BigNumber;
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();
const EVMThrow = require('../helpers/EVMThrow.js');
const SplitPayment = artifacts.require('SplitPayment');
contract('SplitPayment', function ([owner, payee1, payee2, payee3, nonpayee1, payer1]) {
const amount = web3.toWei(1.0, 'ether');
beforeEach(async function () {
this.payees = [payee1, payee2, payee3];
this.shares = [20, 10, 70];
this.contract = await SplitPayment.new(this.payees, this.shares);
});
it('should accept payments', async function () {
await web3.eth.sendTransaction({ from: owner, to: this.contract.address, value: amount });
const balance = web3.eth.getBalance(this.contract.address);
balance.should.be.bignumber.equal(amount);
});
it('should store shares if address is payee', async function () {
const shares = await this.contract.shares.call(payee1);
shares.should.be.bignumber.not.equal(0);
});
it('should not store shares if address is not payee', async function () {
const shares = await this.contract.shares.call(nonpayee1);
shares.should.be.bignumber.equal(0);
});
it('should throw if no funds to claim', async function () {
await this.contract.claim({ from: payee1 }).should.be.rejectedWith(EVMThrow);
});
it('should throw if non-payee want to claim', async function () {
await web3.eth.sendTransaction({ from: payer1, to: this.contract.address, value: amount });
await this.contract.claim({ from: nonpayee1 }).should.be.rejectedWith(EVMThrow);
});
it('should distribute funds to payees', async function () {
await web3.eth.sendTransaction({ from: payer1, to: this.contract.address, value: amount });
// receive funds
const initBalance = web3.eth.getBalance(this.contract.address);
initBalance.should.be.bignumber.equal(amount);
// distribute to payees
const initAmount1 = web3.eth.getBalance(payee1);
await this.contract.claim({ from: payee1 });
const profit1 = web3.eth.getBalance(payee1) - initAmount1;
assert(Math.abs(profit1 - web3.toWei(0.20, 'ether')) < 1e16);
const initAmount2 = web3.eth.getBalance(payee2);
await this.contract.claim({ from: payee2 });
const profit2 = web3.eth.getBalance(payee2) - initAmount2;
assert(Math.abs(profit2 - web3.toWei(0.10, 'ether')) < 1e16);
const initAmount3 = web3.eth.getBalance(payee3);
await this.contract.claim({ from: payee3 });
const profit3 = web3.eth.getBalance(payee3) - initAmount3;
assert(Math.abs(profit3 - web3.toWei(0.70, 'ether')) < 1e16);
// end balance should be zero
const endBalance = web3.eth.getBalance(this.contract.address);
endBalance.should.be.bignumber.equal(0);
// check correct funds released accounting
const totalReleased = await this.contract.totalReleased.call();
totalReleased.should.be.bignumber.equal(initBalance);
});
});
import expectThrow from '../../helpers/expectThrow';
import ether from '../../helpers/ether';
var CappedToken = artifacts.require('CappedToken');
contract('Capped', function (accounts) {
const cap = ether(1000);
let token;
beforeEach(async function () {
token = await CappedToken.new(cap);
});
it('should start with the correct cap', async function () {
let _cap = await token.cap();
assert(cap.eq(_cap));
});
it('should mint when amount is less than cap', async function () {
const result = await token.mint(accounts[0], 100);
assert.equal(result.logs[0].event, 'Mint');
});
it('should fail to mint if the ammount exceeds the cap', async function () {
await token.mint(accounts[0], cap.sub(1));
await expectThrow(token.mint(accounts[0], 100));
});
it('should fail to mint after cap is reached', async function () {
await token.mint(accounts[0], cap);
await expectThrow(token.mint(accounts[0], 1));
});
});
import EVMRevert from '../../helpers/EVMRevert';
var Message = artifacts.require('MessageHelper');
var ERC827TokenMock = artifacts.require('ERC827TokenMock');
var BigNumber = web3.BigNumber;
var _ = require('lodash');
var ethjsABI = require('ethjs-abi');
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();
contract('ERC827 Token', function (accounts) {
let token;
function findMethod (abi, name, args) {
for (var i = 0; i < abi.length; i++) {
const methodArgs = _.map(abi[i].inputs, 'type').join(',');
if ((abi[i].name === name) && (methodArgs === args)) {
return abi[i];
}
}
}
beforeEach(async function () {
token = await ERC827TokenMock.new(accounts[0], 100);
});
it('should return the correct totalSupply after construction', async function () {
let totalSupply = await token.totalSupply();
assert.equal(totalSupply, 100);
});
it('should return the correct allowance amount after approval', async function () {
let token = await ERC827TokenMock.new(accounts[0], 100);
await token.approve(accounts[1], 100);
let allowance = await token.allowance(accounts[0], accounts[1]);
assert.equal(allowance, 100);
});
it('should return correct balances after transfer', async function () {
await token.transfer(accounts[1], 100);
let balance0 = await token.balanceOf(accounts[0]);
assert.equal(balance0, 0);
let balance1 = await token.balanceOf(accounts[1]);
assert.equal(balance1, 100);
});
it('should throw an error when trying to transfer more than balance', async function () {
await token.transfer(accounts[1], 101).should.be.rejectedWith(EVMRevert);
});
it('should return correct balances after transfering from another account', async function () {
await token.approve(accounts[1], 100);
await token.transferFrom(accounts[0], accounts[2], 100, { from: accounts[1] });
let balance0 = await token.balanceOf(accounts[0]);
assert.equal(balance0, 0);
let balance1 = await token.balanceOf(accounts[2]);
assert.equal(balance1, 100);
let balance2 = await token.balanceOf(accounts[1]);
assert.equal(balance2, 0);
});
it('should throw an error when trying to transfer more than allowed', async function () {
await token.approve(accounts[1], 99);
await token.transferFrom(
accounts[0], accounts[2], 100,
{ from: accounts[1] }
).should.be.rejectedWith(EVMRevert);
});
it('should throw an error when trying to transferFrom more than _from has', async function () {
let balance0 = await token.balanceOf(accounts[0]);
await token.approve(accounts[1], 99);
await token.transferFrom(
accounts[0], accounts[2], balance0 + 1,
{ from: accounts[1] }
).should.be.rejectedWith(EVMRevert);
});
describe('validating allowance updates to spender', function () {
let preApproved;
it('should start with zero', async function () {
preApproved = await token.allowance(accounts[0], accounts[1]);
assert.equal(preApproved, 0);
});
it('should increase by 50 then decrease by 10', async function () {
const abiMethod = findMethod(token.abi, 'increaseApproval', 'address,uint256');
const increaseApprovalData = ethjsABI.encodeMethod(abiMethod,
[accounts[1], 50]
);
await token.sendTransaction(
{ from: accounts[0], data: increaseApprovalData }
);
let postIncrease = await token.allowance(accounts[0], accounts[1]);
preApproved.plus(50).should.be.bignumber.equal(postIncrease);
await token.decreaseApproval(accounts[1], 10);
let postDecrease = await token.allowance(accounts[0], accounts[1]);
postIncrease.minus(10).should.be.bignumber.equal(postDecrease);
});
});
it('should increase by 50 then set to 0 when decreasing by more than 50', async function () {
await token.approve(accounts[1], 50);
await token.decreaseApproval(accounts[1], 60);
let postDecrease = await token.allowance(accounts[0], accounts[1]);
postDecrease.should.be.bignumber.equal(0);
});
it('should throw an error when trying to transfer to 0x0', async function () {
await token.transfer(0x0, 100).should.be.rejectedWith(EVMRevert);
});
it('should throw an error when trying to transferFrom to 0x0', async function () {
await token.approve(accounts[1], 100);
await token.transferFrom(accounts[0], 0x0, 100, { from: accounts[1] })
.should.be.rejectedWith(EVMRevert);
});
describe('Test ERC827 methods', function () {
it(
'should return correct balances after transfer (with data) and show the event on receiver contract'
, async function () {
const message = await Message.new();
const extraData = message.contract.showMessage.getData(
web3.toHex(123456), 666, 'Transfer Done'
);
const abiMethod = findMethod(token.abi, 'transfer', 'address,uint256,bytes');
const transferData = ethjsABI.encodeMethod(abiMethod,
[message.contract.address, 100, extraData]
);
const transaction = await token.sendTransaction(
{ from: accounts[0], data: transferData }
);
assert.equal(2, transaction.receipt.logs.length);
new BigNumber(100).should.be.bignumber.equal(
await token.balanceOf(message.contract.address)
);
});
it(
'should return correct allowance after approve (with data) and show the event on receiver contract'
, async function () {
const message = await Message.new();
const extraData = message.contract.showMessage.getData(
web3.toHex(123456), 666, 'Transfer Done'
);
const abiMethod = findMethod(token.abi, 'approve', 'address,uint256,bytes');
const approveData = ethjsABI.encodeMethod(abiMethod,
[message.contract.address, 100, extraData]
);
const transaction = await token.sendTransaction(
{ from: accounts[0], data: approveData }
);
assert.equal(2, transaction.receipt.logs.length);
new BigNumber(100).should.be.bignumber.equal(
await token.allowance(accounts[0], message.contract.address)
);
});
it(
'should return correct allowance after increaseApproval (with data) and show the event on receiver contract'
, async function () {
const message = await Message.new();
const extraData = message.contract.showMessage.getData(
web3.toHex(123456), 666, 'Transfer Done'
);
await token.approve(message.contract.address, 10);
new BigNumber(10).should.be.bignumber.equal(
await token.allowance(accounts[0], message.contract.address)
);
const abiMethod = findMethod(token.abi, 'increaseApproval', 'address,uint256,bytes');
const increaseApprovalData = ethjsABI.encodeMethod(abiMethod,
[message.contract.address, 50, extraData]
);
const transaction = await token.sendTransaction(
{ from: accounts[0], data: increaseApprovalData }
);
assert.equal(2, transaction.receipt.logs.length);
new BigNumber(60).should.be.bignumber.equal(
await token.allowance(accounts[0], message.contract.address)
);
});
it(
'should return correct allowance after decreaseApproval (with data) and show the event on receiver contract'
, async function () {
const message = await Message.new();
await token.approve(message.contract.address, 100);
new BigNumber(100).should.be.bignumber.equal(
await token.allowance(accounts[0], message.contract.address)
);
const extraData = message.contract.showMessage.getData(
web3.toHex(123456), 666, 'Transfer Done'
);
const abiMethod = findMethod(token.abi, 'decreaseApproval', 'address,uint256,bytes');
const decreaseApprovalData = ethjsABI.encodeMethod(abiMethod,
[message.contract.address, 60, extraData]
);
const transaction = await token.sendTransaction(
{ from: accounts[0], data: decreaseApprovalData }
);
assert.equal(2, transaction.receipt.logs.length);
new BigNumber(40).should.be.bignumber.equal(
await token.allowance(accounts[0], message.contract.address)
);
});
it(
'should return correct balances after transferFrom (with data) and show the event on receiver contract'
, async function () {
const message = await Message.new();
const extraData = message.contract.showMessage.getData(
web3.toHex(123456), 666, 'Transfer Done'
);
await token.approve(accounts[1], 100, { from: accounts[0] });
new BigNumber(100).should.be.bignumber.equal(
await token.allowance(accounts[0], accounts[1])
);
const abiMethod = findMethod(token.abi, 'transferFrom', 'address,address,uint256,bytes');
const transferFromData = ethjsABI.encodeMethod(abiMethod,
[accounts[0], message.contract.address, 100, extraData]
);
const transaction = await token.sendTransaction(
{ from: accounts[1], data: transferFromData }
);
assert.equal(2, transaction.receipt.logs.length);
new BigNumber(100).should.be.bignumber.equal(
await token.balanceOf(message.contract.address)
);
});
it('should fail inside approve (with data)', async function () {
const message = await Message.new();
const extraData = message.contract.fail.getData();
const abiMethod = findMethod(token.abi, 'approve', 'address,uint256,bytes');
const approveData = ethjsABI.encodeMethod(abiMethod,
[message.contract.address, 10, extraData]
);
await token.sendTransaction(
{ from: accounts[0], data: approveData }
).should.be.rejectedWith(EVMRevert);
// approval should not have gone through so allowance is still 0
new BigNumber(0).should.be.bignumber
.equal(await token.allowance(accounts[1], message.contract.address));
});
it('should fail inside transfer (with data)', async function () {
const message = await Message.new();
const extraData = message.contract.fail.getData();
const abiMethod = findMethod(token.abi, 'transfer', 'address,uint256,bytes');
const transferData = ethjsABI.encodeMethod(abiMethod,
[message.contract.address, 10, extraData]
);
await token.sendTransaction(
{ from: accounts[0], data: transferData }
).should.be.rejectedWith(EVMRevert);
// transfer should not have gone through, so balance is still 0
new BigNumber(0).should.be.bignumber
.equal(await token.balanceOf(message.contract.address));
});
it('should fail inside transferFrom (with data)', async function () {
const message = await Message.new();
const extraData = message.contract.fail.getData();
await token.approve(accounts[1], 10, { from: accounts[2] });
const abiMethod = findMethod(token.abi, 'transferFrom', 'address,address,uint256,bytes');
const transferFromData = ethjsABI.encodeMethod(abiMethod,
[accounts[2], message.contract.address, 10, extraData]
);
await token.sendTransaction(
{ from: accounts[1], data: transferFromData }
).should.be.rejectedWith(EVMRevert);
// transferFrom should have failed so balance is still 0 but allowance is 10
new BigNumber(10).should.be.bignumber
.equal(await token.allowance(accounts[2], accounts[1]));
new BigNumber(0).should.be.bignumber
.equal(await token.balanceOf(message.contract.address));
});
it('should fail approve (with data) when using token contract address as receiver', async function () {
const message = await Message.new();
const extraData = message.contract.showMessage.getData(
web3.toHex(123456), 666, 'Transfer Done'
);
const abiMethod = findMethod(token.abi, 'approve', 'address,uint256,bytes');
const approveData = ethjsABI.encodeMethod(abiMethod,
[token.contract.address, 100, extraData]
);
await token.sendTransaction(
{ from: accounts[0], data: approveData }
).should.be.rejectedWith(EVMRevert);
});
it('should fail transfer (with data) when using token contract address as receiver', async function () {
const message = await Message.new();
const extraData = message.contract.showMessage.getData(
web3.toHex(123456), 666, 'Transfer Done'
);
const abiMethod = findMethod(token.abi, 'transfer', 'address,uint256,bytes');
const transferData = ethjsABI.encodeMethod(abiMethod,
[token.contract.address, 100, extraData]
);
await token.sendTransaction(
{ from: accounts[0], data: transferData }
).should.be.rejectedWith(EVMRevert);
});
it('should fail transferFrom (with data) when using token contract address as receiver', async function () {
const message = await Message.new();
const extraData = message.contract.showMessage.getData(
web3.toHex(123456), 666, 'Transfer Done'
);
await token.approve(accounts[1], 1, { from: accounts[0] });
const abiMethod = findMethod(token.abi, 'transferFrom', 'address,address,uint256,bytes');
const transferFromData = ethjsABI.encodeMethod(abiMethod,
[accounts[0], token.contract.address, 1, extraData]
);
await token.sendTransaction(
{ from: accounts[1], data: transferFromData }
).should.be.rejectedWith(EVMRevert);
});
});
});
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