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.9

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

3. Enums

There are no enums for the TruSale Smart Contract.

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 Usage Example
 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 Usage Example
 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 Usage Example
 EndChanged(1511930475,
            1512016874,
            0x123456789abcdefghijklmnopqrstuvwxyz98765);

Completed

Event Name: Completed
Description: Event to notify when the Sale completes

Usage

The Completed event has the following usage syntax:

  Argument Type Indexed? Details
1 executor address Yes Address that executed the Completed event
Completed Usage Example
 Completed(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:

onlyTokenOwner Code
 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:

TruSale Constructor Code
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 Constructor Usage Example
 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:

buy Code
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:

Usage

The buy function has the following usage syntax:

buy Usage Example
 buy({value: 1000000000000000000});

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:

updateWhitelist Code
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 Usage Example
 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:

changeEndTime Code
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 Usage Example
 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:

hasEnded Code
 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.

Usage

The hasEnded function has the following usage syntax:

hasEnded Usage Example
 hasEnded();

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:

checkSaleValid Code
 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.

Usage

The checkSaleValid function has the following usage syntax:

checkSaleValid Usage Example
 checkSaleValid();

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 Usage Example
 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.

Usage

The forwardFunds function has the following usage syntax:

forwardFunds Usage Example
 forwardFunds();

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:

createSale Code
 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 Usage Example
 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:

buyTokens Code
 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.

Usage

The buyTokens function has the following usage syntax:

buyTokens Usage Example
 buyTokens(0xa57htuju9abcdefghijehtitthtjiohjtoi02447);