Traditionally when we create crowdsale smart contract we provide four parameters: initial rate, final rate, opening time, and closing time, which determines current rate when one buys tokens using Ether. For example OpenZeppelin contracts.
Why can't we simply define a function, say, setRate(uint) onlyAdjuster and adjust the rate manually? Isn't it more flexible?
The matter is that I have created my own ERC20 token and just want to sell it for Ether. I have no time restrictions or final rate, only finite amount of tokens I want to distribute. What is the best practice in my case?
OpenZeppelin provides a highly configurable Crowdsale base contract that can be combined with various other functionalities to construct a bespoke crowdsale.
You could include a manual rate adjustment. I would think of this as the equivalent of a vending machine for laundry or arcade tokens, where the owner could change the price of tokens.
I would recommend clearly advising participants under what circumstances the rate could be changed and who could change it, including by how much and how frequently.
You may want to code these types of restrictions on the rate change into your crowdsale. Such as limiting the frequency of a change (e.g. can only be changed after X time since the last change) and/or the amount (e.g. can only be adjusted up or down by Y%).
Your solution should also have appropriate testing and auditing:
@abcoathup
Thanks for reply.
Yeah, I am using Crowdsale smart-contract.
As you recommend we do have our rules on under what circumstances the rate could be changed and who could change it. But I’d like simply to create a rate setter with restricted access and change it manually according to rules.
You can have a manual rate setter with no restrictions (other than who can set it).
Though this means that your users will have to trust what you say that you are going to do, rather than knowing the restrictions are enforced in code.
I would recommend being very clear with your users under what circumstances you would change the rate, both with how frequently, with what notice, and by how much (along with any other conditions). I would also include why these conditions are not implemented in code.
As always, I recommend that smart contracts are appropriately tested and audited.
Also get appropriate advice on any regulatory requirements.
@abcoathup
I wonder why are members of Crowdsale.sol such as _wallet, _rate, _token, and _weiRaised declared private? While they are readable, they are not accessible when we inherit from Crowdsale contract. This does not allow me to inherit from Crowdsale and for example define a setter function for _rate.
I made _rateinternal in Crowdsale.sol and my derived contract worked fine.
pragma solidity >=0.4.21 <0.6.0;
import "../node_modules/@openzeppelin/contracts/access/Roles.sol";
import "../node_modules/@openzeppelin/contracts/crowdsale/Crowdsale.sol";
contract MyCrowdsale is Crowdsale{
using Roles for Roles.Role;
Roles.Role private _owners;
constructor(uint256 initialRate, address payable wallet, IERC20 tokenAddr, address ownerAddr)
Crowdsale(initialRate, wallet, tokenAddr)
public
{
_owners.add(ownerAddr);
}
function setRate(uint256 newRate) public
{
require(_owners.has(msg.sender), "DOES_NOT_HAVE_RATE_SETTER_ROLE");
_rate = newRate;
}
}
What is the reason to make those variables private?
We can override Crowdsale functionality to give a changeable/settable rate so could do something like the following code.
Please note, I haven't tested this smart contract at all.
As always, I recommend that smart contracts are appropriately tested and audited.
pragma solidity ^0.5.0;
import "@openzeppelin/contracts/access/Roles.sol";
import "@openzeppelin/contracts/crowdsale/Crowdsale.sol";
contract MyCrowdsale is Crowdsale{
using Roles for Roles.Role;
Roles.Role private _owners;
uint256 private _changeableRate;
constructor(uint256 initialRate, address payable wallet, IERC20 tokenAddr, address ownerAddr)
Crowdsale(initialRate, wallet, tokenAddr)
public
{
_owners.add(ownerAddr);
_changeableRate = initialRate;
}
function setRate(uint256 newRate) public
{
require(_owners.has(msg.sender), "DOES_NOT_HAVE_RATE_SETTER_ROLE");
_changeableRate = newRate;
}
function rate() public view returns (uint256) {
return _changeableRate;
}
function _getTokenAmount(uint256 weiAmount) internal view returns (uint256) {
return weiAmount.mul(_changeableRate);
}
}
@abcoathup
Yeah, I fixed import "../node_modules/@openzeppelin/... to import "@openzeppelin/...
I agree with you, I always respect other’s code, never change private member. In my example I just wanted to show that if _rate was declared as internal then we could derive from Crowdsale without need to override ALL methods where _rate appears.
In fact, my contract looks exactly like you suggest.
I’ve also written tests for this contract, and I am going to test it on Ropsten before I deploy it on mainnet. You provide security audit as far as I know, so probably I will submit my code for the audit.
Whilst having internal state variables would make things easier, it would break encapsulation. I believe the code overriding functions using rate make it clearer what is being changed.
Good to hear that you have tests for your contract. A thorough test suite is very important. The following article on testing is worth a read: Test smart contracts like a rockstar
I recommend reading the following checklist for before an audit: