TruSale¶
The TruSale Smart Contract acts a parent class for the TruPreSale and TruCrowdSale contracts and contains all logic common to both.
Title: | TruSale |
Description: | Parent Smart Contract for all TruReputationToken Token Sales |
Author: | Ian Bray, Tru Ltd |
Solidity Version: | ^0.4.18 |
Relative Path: | ./contracts/TruSale.sol |
License: | Apache 2 License |
Current Version: | 0.1.12 |
1. Imports & Dependencies¶
The following imports and dependencies exist for the TruSale Smart Contract:
Name | Description |
Haltable | Modified Token Market Smart Contract that provides a capability to halt a contract. |
Ownable | Zeppelin Solidity Smart Contract that provides ownership capabilities to a contract. |
SafeMath | Zeppelin Solidity Library to perform mathematics safely inside Solidity |
TruAddress | Library of helper functions surrounding the Solidity Address type |
TruReputationToken | Smart Contract for the Tru Reputation Token |
2. Variables¶
The following variables exist for the TruSale Smart Contract:
Variable | Type | Vis | Details |
truToken | TruReputationToken | public | Variable for the token being sold in Sale |
saleStartTime | uint256 | public | Start timestamp of the Sale |
saleEndTime | uint256 | public | End timestamp of the Sale |
purchaserCount | uint | public | Number of sale purchasers so far Default: 0 |
multiSigWallet | address | public | Sale wallet address |
BASE_RATE | uint256 | public | Constant variable of post sale TRU to ETH rate Default: 1000 |
PRESALE_RATE | uint256 | public | Constant variable of Pre-Sale TRU to ETH rate Default: 1250 - 25% Bonus |
SALE_RATE | uint256 | public | Constant variable of CrowdSale TRU to ETH rate Default: 1125 - 12.5% Bonus |
MIN_AMOUNT | uint256 | public | Minimum Amount of ETH for an address to participate in Sale Default: 1 * 10^18 |
MAX_AMOUNT | uint256 | public | Maximum ETH buy Amount for a non-Whitelist address Default: 20 * 10^18 |
weiRaised | uint256 | public | Amount raised during Sale in Wei |
cap | uint256 | public | Cap of the Sale- value set during construction |
isCompleted | bool | public | Whether the Sale is complete |
isPreSale | bool | public | Whether the Sale is a Pre-Sale |
isCrowdSale | bool | public | Whether the Sale is a CrowdSale |
soldTokens | uint256 | public | Amount of TRU during Sale |
4. Events¶
The following events exist for the TruSale Smart Contract:
Name | Description |
TokenPurchased | Event to notify when a token purchase occurs |
WhiteListUpdated | Event to notify when the purchaseWhiteList is updated |
EndChanged | Event to notify when the saleEndTime changes |
Completed | Event to notify when the Sale completes |
TokenPurchased¶
Event Name: | TokenPurchased |
Description: | EEvent to notify when a token purchase occurs |
Usage¶
The TokenPurchased event has the following usage syntax and arguments:
Argument | Type | Indexed? | Details | |
1 | purchaser | address | Yes | Address being updated on the Whitelist |
2 | recipient | address | No | Status of the address on the Whitelist |
3 | weiValue | uint256 | No | Amount of ETH spent (in Wei) |
4 | tokenAmount | uint256 | No | Amount of tokens purchased (in smallest decimal) |
TokenPurchased(0x123456789abcdefghijklmnopqrstuvwxyz98765,
0x123456789abcdefghijklmnopqrstuvwxyz98765,
1000000000000000000,
1250000000000000000000);
WhiteListUpdated¶
Event Name: | WhiteListUpdated |
Description: | Event to notify when the purchaseWhiteList is updated |
Usage¶
The WhiteListUpdated event has the following usage syntax and arguments:
Argument | Type | Indexed? | Details | |
1 | purchaserAddress | address | Yes | Address being updated on the Whitelist |
2 | whitelistStatus | address | No | Status of the address on the Whitelist |
3 | executor | address | Yes | Address that executed the WhiteListUpdated event |
WhiteListUpdated(0x123456789abcdefghijklmnopqrstuvwxyz98765,
true,
0x12acd9ef9abcdefghijklmnopqrstuvwxyzghy74);
EndChanged¶
Event Name: | EndChanged |
Description: | Event to notify when the purchaseWhiteList is updated |
Usage¶
The EndChanged event has the following usage syntax and arguments:
Argument | Type | Indexed? | Details | |
1 | oldEnd | uint256 | No | Previous saleEndTime timestamp |
2 | newEnd | uint256 | No | Updated saleEndTime timestamp |
3 | executor | address | Yes | Address that executed the EndChanged event |
EndChanged(1511930475,
1512016874,
0x123456789abcdefghijklmnopqrstuvwxyz98765);
5. Mappings¶
The following mappings exist for the TruSale Smart Contract:
Name | Mapping Type | Description |
purchasedAmount | address => uint256 | Mapping of purchased amount in ETH to buying address |
tokenAmount | address => uint256 | Mapping of purchased amount of TRU to buying address |
purchaserWhiteList | address => bool | Mapping of Whitelisted address to their Whitelist status |
6. Modifiers¶
The following modifiers exist for the TruSale Smart Contract:
Name | Description |
onlyTokenOwner | Modifier to check if transaction sender is the owner of the Token contract |
onlyTokenOwner¶
Modifier Name: | onlyTokenOwner |
Description: | Modifier to check if transaction sender is the owner of the Token contract |
Code¶
The code for the onlyTokenOwner modifier is as follows:
modifier onlyTokenOwner(address _tokenOwner) {
require(msg.sender == _tokenOwner);
_;
}
The onlyTokenOwner function performs the following:
- Checks that the msg.sender matches the supplied _tokenOwner variable. If not, it will throw.
7. Functions¶
The following functions exist for the TruSale Smart Contract:
Name | Description |
TruSale Constructor | Constructor for the TruSale Smart Contract |
buy | Function for buying tokens from the Sale |
updateWhitelist | Function to add or disable a purchaser from AML Whitelist |
changeEndTime | Function to change the end time of the Sale |
hasEnded | Function to check whether the Sale has ended |
checkSaleValid | Internal function to validate that the Sale is valid |
validatePurchase | Internal function to validate the purchase of TRU Tokens |
forwardFunds | Internal function to forward all raised funds to the Sale Wallet |
createSale | Internal function used to encapsulate more complex constructor logic |
buyTokens | Private function execute purchase of TRU Tokens |
TruSale Constructor¶
Function Name: | TruSale |
Description: | Constructor for the TruSale Smart Contract |
Function Type: | Constructor |
Function Visibility: | Public |
Function Modifiers: | N/A |
Return Type: | None |
Return Details: | N/A |
Code¶
The code for the TruSale Constructor function is as follows:
function TruSale(uint256 _startTime,
uint256 _endTime,
address _token,
address _saleWallet) public {
require(TruAddress.isValid(_token) == true);
TruReputationToken tToken = TruReputationToken(_token);
address tokenOwner = tToken.owner();
createSale(_startTime, _endTime, _token, _saleWallet, tokenOwner);
}
The TruSale Constructor function performs the following:
- Checks the _token argument is a valid Ethereum address.
- Gets the owner of the _token TruReputationToken object
- Executes the createSale function with the tokenOwner variable as an argument.
Usage¶
The TruSale Constructor function has the following usage syntax and arguments:
Argument | Type | Details | |
1 | _startTime | uint256 | Sale start timestamp |
2 | _endTime | uint256 | Sale end timestamp |
3 | _token | address | Address of TruReputationToken Contract |
4 | _saleWallet | address | Address of sale wallet |
TruSale(1511930475,
1512016874,
0x123456789abcdefghijklmnopqrstuvwxyz98765,
0x987654321abcdefghijklmnopqrstuvwxyz12345);
buy¶
Function Name: | buy |
Description: | Function for buying tokens from the Sale |
Function Type: | N/A |
Function Visibility: | Public payable |
Function Modifiers: | stopInEmergency |
Return Type: | N/A |
Return Details: | N/A |
Code¶
The code for the buy function is as follows:
function buy() public payable stopInEmergency {
// Check that the Sale is still open and the Cap has not been reached
require(checkSaleValid());
validatePurchase(msg.sender);
}
Note
the buy function is a Solidity payable functino- as such, ETH is sent to the function to allow the purchase of tokens during a sale. This function can be halted via the stop-in-emergency modifier as part of the Haltable characteristics of this Contract.
The buy function performs the following:
- The modifier stopInEmergency checks that the Sale has not been halted. If it has, it will throw.
- Checks the checkSaleValid function returns true. If not, it will throw.
- executes the validatePurchase function.
updateWhitelist¶
Function Name: | updateWhitelist |
Description: | Function to add or disable a purchaser from AML Whitelist |
Function Type: | N/A |
Function Visibility: | Public |
Function Modifiers: | onlyOwner |
Return Type: | None |
Return Details: | N/A |
Code¶
The code for the updateWhitelist function is as follows:
function updateWhitelist(address _purchaser, uint _status) public onlyOwner {
require(TruAddress.isValid(_purchaser) == true);
bool boolStatus = false;
if (_status == 0) {
boolStatus = false;
} else if (_status == 1) {
boolStatus = true;
} else {
revert();
}
WhiteListUpdated(_purchaser, boolStatus);
purchaserWhiteList[_purchaser] = boolStatus;
}
Note
The updateWhitelist function uses uint for the status argument because fuzz testing found that bool arguments on public functions in Solidity could be interpreted as true when supplied with a random string.
In the interest of type safety and defensive development this was set to uint with 0 being false and 1 being true, all other values are ignored.
Be very careful using bool on public functions in Solidity.
The updateWhitelist function performs the following:
- Validates the _purchaser argument is a valid Ethereum address.
- Checks the _status argument is either 0 or 1. If 0, sets boolStatus to false, if 1, sets boolStatus to true. If else, it will throw.
- Fires the WhiteListUpdated event
- Sets the _purchaser to the boolStatus on the purchaseWhiteList
Usage¶
The updateWhitelist function has the following usage syntax and arguments:
Argument | Type | Details | |
1 | _purchaser | uint256 | Address of the purchaser to add or update on the Whitelist |
2 | _status | uint | Status on the Whitelist- 0 for disabled, 1 for enabled |
updateWhitelist(0x987654321abcdefghijklmnopqrstuvwxyz12345, 1);
changeEndTime¶
Function Name: | changeEndTime |
Description: | Function to change the end time of the Sale |
Function Type: | N/A |
Function Visibility: | Public |
Function Modifiers: | onlyOwner |
Return Type: | None |
Return Details: | N/A |
Code¶
The code for the changeEndTime function is as follows:
function changeEndTime(uint256 _endTime) public onlyOwner {
// _endTime must be greater than or equal to saleStartTime
require(_endTime >= saleStartTime);
// Fire Event for time Change
EndChanged(saleEndTime, _endTime);
// Change the Sale End Time
saleEndTime = _endTime;
}
Note
The changeEndTime function has been included to allow a Sale’s end time to be altered after the start. This is addressed in SALREQ 012 and behaves in the following way:
1. If the End Time is moved before the current block timestamp, it will automatically close the Sale fully and finally.
2. If the End Time is moved beyond the current end time, it will extend the time remaining in the Sale. This is useful if issues with the network are encountered and should only be used will full communication to purchasers prior to the change.
The changeEndTime function performs the following:
- Checks the _endTime argument is equal to or greater than the saleStartTime variable. If not, it will throw.
- Fire the EndChanged event.
- Set the saleEndTime variable to the _endTime argument.
Usage¶
The changeEndTime function has the following usage syntax and arguments:
Argument | Type | Details | |
1 | _endTime | uint256 | New end timestamp for Sale |
changeEndTime(1511930475);
hasEnded¶
Function Name: | hasEnded |
Description: | Function to check whether the Sale has ended |
Function Type: | Constant |
Function Visibility: | Public |
Function Modifiers: | N/A |
Return Type: | bool |
Return Details: | Returns true if the Sale has ended; false if it has not |
Code¶
The code for the hasEnded function is as follows:
function hasEnded() public constant returns (bool) {
bool isCapHit = weiRaised >= cap;
bool isExpired = now > saleEndTime;
return isExpired || isCapHit;
}
The hasEnded function performs the following:
- Checks that the weiRaised variable is less than the cap variable.
- Checks that the current block timestamp is less than the saleEndTime timestamp
- If either of the previous checks are true, the Sale has ended. Otherwise the Sale has not ended.
checkSaleValid¶
Function Name: | checkSaleValid |
Description: | Internal function to validate that the Sale is valid |
Function Type: | Constant |
Function Visibility: | Internal |
Function Modifiers: | N/A |
Return Type: | bool |
Return Details: | Returns true if the Sale is still open; false if it is not |
Code¶
The code for the checkSaleValid function is as follows:
function checkSaleValid() internal constant returns (bool) {
bool afterStart = now >= saleStartTime;
bool beforeEnd = now <= saleEndTime;
bool capNotHit = weiRaised.add(msg.value) <= cap;
return afterStart && beforeEnd && capNotHit;
}
The checkSaleValid function performs the following:
- Checks the Sale has started. If it has not, will return false.
- Checks the Sale has not ended. If it has, will return false.
- Checks the cap has not been hit, if it has, will return false.
validatePurchase¶
Function Name: | validatePurchase |
Description: | Internal function to validate the purchase of TRU Tokens |
Function Type: | N/A |
Function Visibility: | Internal |
Function Modifiers: | stopInEmergency |
Return Type: | N/A |
Return Details: | N/A |
Code¶
The code for the validatePurchase function is as follows:
function validatePurchase(address _purchaser) internal stopInEmergency {
// _purchaser must be valid
require(TruAddress.isValid(_purchaser) == true);
// Value must be greater than 0
require(msg.value > 0);
buyTokens(_purchaser);
}
Note
The validatePurchase function acts as the both a pre-validation step for a purchase, and a point at which the Sale can be halted as per the Haltable Smart Contract.
The validatePurchase function performs the following:
- Validates that the _purchaser argument is a valid Ethereum Address.
- Validates that the msg.value is greater than 0
- Executes the buyTokens function.
Usage¶
The validatePurchase function has the following usage syntax:
validatePurchase(0x987654321abcdefghijklmnopqrstuvwxyz12345);
forwardFunds¶
Function Name: | forwardFunds |
Description: | Internal function to forward all raised funds to the Sale Wallet |
Function Type: | N/A |
Function Visibility: | Internal |
Function Modifiers: | N/A |
Return Type: | N/A |
Return Details: | N/A |
Code¶
The code for the forwardFunds function is as follows:
function forwardFunds() internal {
multiSigWallet.transfer(msg.value);
}
The forwardFunds function performs the following:
- Transfers any new funds away from the TruSale Smart Contract, to the Sale Wallet reflected in the multiSigWallet variable.
createSale¶
Function Name: | createSale |
Description: | Internal function used to encapsulate more complex constructor logic |
Function Type: | N/A |
Function Visibility: | Internal |
Function Modifiers: | onlyTokenOwner |
Return Type: | N/A |
Return Details: | N/A |
Code¶
The code for the createSale function is as follows:
function createSale(
uint256 _startTime,
uint256 _endTime,
address _token,
address _saleWallet,
address _tokenOwner)
internal onlyTokenOwner(_tokenOwner) {
// _startTime must be greater than or equal to now
require(now <= _startTime);
// _endTime must be greater than or equal to _startTime
require(_endTime >= _startTime);
// _salletWallet must be valid
require(TruAddress.isValid(_saleWallet) == true);
truToken = TruReputationToken(_token);
multiSigWallet = _saleWallet;
saleStartTime = _startTime;
saleEndTime = _endTime;
}
Note
The createSale argument uses the onlyTokenOwner modifier to ensure that no instance of the TruSale can be created for TruReputationToken unless they are the owner of that contract. If that modifier is passed, the rest of the logic is processed to construct the TruSale instance.
The createSale function performs the following:
- Ensures the _startTime timestamp argument is greater than the latest block timestamp.
- Ensures the _endTime timestamp argument is greater than the _startTime timestamp argument.
- Ensures the _saleWallet argument is a valid Ethereum Address.
- Sets the truToken variable to the instance of TruReputationToken from the _token argument.
- Sets the multiSigWallet variable to the _saleWallet argument.
- Sets the saleStartTime variable to the _startTime argument.
- Sets the saleEndTime variable to the _endTime argument.
Usage¶
The createSale function has the following usage syntax:
createSale(1511930475,
1512016874,
0x123456789abcdefghijklmnopqrstuvwxyz98765,,
0x465328375xyzacefgijklmnopqrstuvwxyz66712,
0xa57htuju9abcdefghijehtitthtjiohjtoi02447);
buyTokens¶
Function Name: | buyTokens |
Description: | Private function execute purchase of TRU Tokens |
Function Type: | N/A |
Function Visibility: | Private |
Function Modifiers: | N/A |
Return Type: | N/A |
Return Details: | N/A |
Code¶
The code for the buyTokens function is as follows:
function buyTokens(address _purchaser) private {
uint256 weiTotal = msg.value;
// If the Total wei is less than the minimum purchase, reject
require(weiTotal >= MIN_AMOUNT);
// If the Total wei is greater than the maximum stake, purchasers must be on the whitelist
if (weiTotal > MAX_AMOUNT) {
require(purchaserWhiteList[msg.sender]);
}
// Prevention to stop circumvention of Maximum Amount without being on the Whitelist
if (purchasedAmount[msg.sender] != 0 && !purchaserWhiteList[msg.sender]) {
uint256 totalPurchased = purchasedAmount[msg.sender];
totalPurchased = totalPurchased.add(weiTotal);
require(totalPurchased < MAX_AMOUNT);
}
uint256 tokenRate = BASE_RATE;
if (isPreSale) {
tokenRate = PRESALE_RATE;
}
if (isCrowdSale) {
tokenRate = SALE_RATE;
}
// Multiply Wei x Rate to get Number of Tokens to create (as a 10^18 subunit)
uint256 noOfTokens = weiTotal.mul(tokenRate);
// Add the wei to the running total
weiRaised = weiRaised.add(weiTotal);
// If the purchaser address has not purchased already, add them to the list
if (purchasedAmount[msg.sender] == 0) {
purchaserCount++;
}
soldTokens = soldTokens.add(noOfTokens);
purchasedAmount[msg.sender] = purchasedAmount[msg.sender].add(msg.value);
tokenAmount[msg.sender] = tokenAmount[msg.sender].add(noOfTokens);
// Mint the Tokens to the Purchaser
truToken.mint(_purchaser, noOfTokens);
TokenPurchased(msg.sender,
_purchaser,
weiTotal,
noOfTokens);
forwardFunds();
}
The buyTokens function performs the following:
- Checks that the sent amount (msg.value) is equal to or greater than the MIN_AMOUNT variable. If it is not, it will throw.
- Checks if the sent amount (msg.value) is greater than the MAX_AMOUNT variable. If it is, it will perform a further check to see if the sender is on the Whitelist- if they are, it will proceed, if not it will throw. If the amount is less than or equal to the MAX_AMOUNT variable, it will proceed.
- Checks that the cumulative total of this purchase, and any prior purchases do not exceed the MAX_AMOUNT variable if the purchaser is not on the Whitelist. If it is, it will throw.
- Sets the Sale Rate to the default of the BASE_RATE variable.
- If the isPreSale variable is true sets the Sale Rate to PRESALE_RATE variable.
- If the isCrowdSale variable is true sets the Sale Rate to SALE_RATE variable.
- Calculates the number of tokens purchased.
- Increments the purchaserCount variable if this is the first purchase from this address.
- Adds the calculated token count to the soldTokens variable.
- Adds the msg.value to the purchasedAmount mapping for the purchaser.
- Adds the token amount to the tokenAmount mapping for the purchaser.
- Mints the token amount to the purchaser’s address.
- Fires the TokenPurchased event.
- Executes the forwardFunds function.