0

I'm trying to write an extension of OpenZeppelin's CappedCrowdsale to allow for a hard cap in USD, as I think this is more human-readable than having everything in wei.

However, I'm having issues updating some of the inherited state variables (Crowdsale's rate and CappedCrowdsale's cap). I try to do this in the updateEthPrice function, which I know is getting called because the ethPriceInDollars variable gets updated correctly, but cap and rate always return 1... (which is what they were initialized with)

At first I thought that maybe the inherited constructors were firing after the base constructor (and overriding whatever I set in the base constructor), but even if I call updateEthPrice once the contract has been deployed, I'm still not seeing any changes to cap and rate

Been banging my head on this all day, any help is appreciated!

pragma solidity ^0.4.19;

import "zeppelin-solidity/contracts/crowdsale/CappedCrowdsale.sol";


/**
 * @title DollarCappedCrowdsale
 * @author Gianni Settino
 * @dev Extension of CappedCrowdsale with a cap in USD instead of wei.
 * This means that Crowdsale's rate and CappedCrowdsale's cap now depend
 * on ETH's USD price, which can be changed up until the start of the token sale.
 */
contract DollarCappedCrowdsale is CappedCrowdsale(1), Ownable {
  uint256 public ethPriceInDollars;
  uint256 public hardCapInDollars;
  uint256 public tokenPriceInCents;

  // @dev We temporarily init Crowdsale's rate and CappedCrowdsale's cap to 1.
  // Reason: their "real" values are subsequently calculated in updateEthPrice()
  function DollarCappedCrowdsale(
    uint256 _startTime,
    uint256 _endTime,
    address _wallet,
    uint256 _ethPriceInDollars,
    uint256 _hardCapInDollars,
    uint256 _tokenPriceInCents
    )
    public
    Crowdsale(_startTime, _endTime, 1, _wallet)
  {
    require(_hardCapInDollars > 0);
    require(_tokenPriceInCents > 0);
    hardCapInDollars = _hardCapInDollars;
    tokenPriceInCents = _tokenPriceInCents;
    updateEthPrice(_ethPriceInDollars);
  }

  function updateEthPrice(uint256 _ethPriceInDollars) public onlyOwner {
    require(now < startTime);
    require(_ethPriceInDollars > 0);
    ethPriceInDollars = _ethPriceInDollars;
    cap = (hardCapInDollars / ethPriceInDollars).mul(1 ether);
    rate = (ethPriceInDollars.mul(100)) / tokenPriceInCents;
  }
}
2
  • What values are you using for the constructor parameters? The only suspicious code is cap = (hardCapInDollars / ethPriceInDollars).mul(1 ether), depending on the values the division will truncate and loss presicion, a alternative is to first multiply cap = hardCapInDollars.mul(1 ether) / ethPriceInDollars.
    – Ismael
    Commented Feb 7, 2018 at 6:32
  • DollarCappedCrowdsale(START_TIME, START_TIME.add(LENGTH_IN_SEC), msg.sender, _ethPriceInDollars, HARD_CAP_IN_DOLLARS, TOKEN_PRICE_IN_CENTS) where START_TIME = 1519603200, LENGTH_IN_SEC = 7 days, _ethPriceInDollars = 870 (passed when we deploy the contract), HARD_CAP_IN_DOLLARS = 40000000, and TOKEN_PRICE_IN_CENTS = 10
    – giaset
    Commented Feb 10, 2018 at 14:52

2 Answers 2

1

I've tried your contractin remix and it works as expected. The modified version to be used with latest version of OpenZeppelin contracts.

pragma solidity ^0.4.19;

import "github.com/OpenZeppelin/zeppelin-solidity/contracts/crowdsale/CappedCrowdsale.sol";

contract DollarCappedCrowdsale is CappedCrowdsale(1), Ownable {
  uint256 public ethPriceInDollars;
  uint256 public hardCapInDollars;
  uint256 public tokenPriceInCents;

  function DollarCappedCrowdsale(
    uint256 _startTime,
    uint256 _endTime,
    address _wallet,
    uint256 _ethPriceInDollars,
    uint256 _hardCapInDollars,
    uint256 _tokenPriceInCents
    )
    public
    Crowdsale(_startTime, _endTime, 1, _wallet, MintableToken(1))
  {
    require(_hardCapInDollars > 0);
    require(_tokenPriceInCents > 0);
    hardCapInDollars = _hardCapInDollars;
    tokenPriceInCents = _tokenPriceInCents;
    updateEthPrice(_ethPriceInDollars);
  }

  function updateEthPrice(uint256 _ethPriceInDollars) public onlyOwner {
    require(now < startTime);
    require(_ethPriceInDollars > 0);
    ethPriceInDollars = _ethPriceInDollars;
    cap = (hardCapInDollars / ethPriceInDollars).mul(1 ether);
    rate = (ethPriceInDollars.mul(100)) / tokenPriceInCents;
  }
}

Creation parameters

1518412209,1518413209,"0x01","870","40000000","10"

Resulting parameters:

  • rate: 8700
  • tokenPriceInCents: 10
  • hardCapInDollars: 40000000
  • ethPriceInDollars: 870
  • cap: 45977000000000000000000
1
  • Amazing, this works, thank you so much! I had to start using the new version of OpenZeppelin's Crowdsale contract, I'll do a deep dive and see what changed to make it work all of a sudden...
    – giaset
    Commented Feb 13, 2018 at 4:49
0

As I suspected, you're inheriting the CappedCrowdsale Contract incorrectly. See the answer below and let me know how that works out.

contract SampleCrowdsale is CappedCrowdsale, RefundableCrowdsale {

    function SampleCrowdsale(uint256 _startTime, uint256 _endTime, uint256 _rate, uint256 _goal, uint256 _cap, address _wallet, MintableToken _token) public
        CappedCrowdsale(_cap)
        FinalizableCrowdsale()
        RefundableCrowdsale(_goal)
        Crowdsale(_startTime, _endTime, _rate, _wallet, _token)
    {
    //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);
    }
}
9
  • Also, I would suggest using SafeMath for ALL of your math functions.
    – Dorian Lee
    Commented Feb 5, 2018 at 17:27
  • Hi Dorian, thanks for your response. I’m actually trying to update the inherited cap and rate state variables, not the ones from DollarCappedCrowdsale. As for SafeMath, I noticed that for division, it doesn’t make a difference (ie SafeMath isn’t doing any assertions), and the syntax is cleaner using the / operator. Is this against best practices? Cheers!
    – giaset
    Commented Feb 5, 2018 at 18:23
  • Is the inheritance of CappedCrowdsale(1) the correct way? I only use 'is BasicToken', for instance... Maybe that has something to do with it?
    – Dorian Lee
    Commented Feb 5, 2018 at 18:33
  • Oh yeah, ok I see. Just don't use CappedCrowdsale(1) and use CappedCrowdsale. Then in the CappedCrowdsale set the variable to 1. uint256 cap = 1; then just update as necessary. I have found it difficult to construct other contracts using variables so I just initialize them with the hardcoded variable, then use functions to change them
    – Dorian Lee
    Commented Feb 5, 2018 at 18:36
  • That seems... bad. Don’t think it’s good practice to go into an OpenZeppelin contract and start modifying the code... And ya, CappedCrowdsale(1) is the proper way of inheriting from CappedCrowdsale with a constructor parameter of 1 (you can also do this in the DollarCappedCrowdsale constructor like you would for a modifier)
    – giaset
    Commented Feb 5, 2018 at 20:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.