The ESR Token Pre-Sale (16th April) — For advanced users!

Jeff P
15 min readApr 14, 2021

Ok so if you just want to get on with purchasing ESR tokens, and don’t care how the smart contract that you’re sending HPB to actually works, then you can just head straight over to the main guide:

https://waxlyrical.medium.com/the-esr-token-pre-sale-16th-april-ff0147d198d0

If however, you happen to be a tad curious about how the actual smart contract works, I’ll try to break it down for you, and even allow you to interact directly with it if you want to :)

here is the link to the code:

If you open the link, you’ll see the smart contract source code. Let’s copy it into the HPB Integrated Development Environment (IDE) for developing smart contracts and DApps.

head over to https://remix.hpb.io

You’ll see a page that looks a bit like this:

Before we do anything, lets get rid of any default “template” smart contracts stored in here. Click on the “browser” drop down link on the left-hand column, and if you see any solidity smart contract files in there (solidity files end in .sol) then right-click on them and delete them until this is empty.

Next, click on the tiny “+” symbol above the browser, and a box will open asking you to name a new smart contract file. Name this “TokenSale.sol”

You should now have a blank code window ready to add code. Go back to the Github tokensale link I posted further up the page, and copy the entire section of code, and now paste that into your empty remix code window.

Now on the right-hand side of the page, ensure that compiler version 0.5.7 is selected, and tick the “Auto-Compile” tick box.

If all goes well, the remix IDE should start to compile the contract without any RED error boxes on the right-hand side. (Blue and Yellow boxes are just warnings, but won’t stop the code compiling)

Ok now before we more ahead, lets look at this code in a bit more detail to find out what’s actually going on.

Line 12 should say:

pragma solidity ^0.5.6;

You will find this in all Ethereum-compatible smart contracts. HPB is “Ethereum Virtual Machine” compatible, so it adopts the exact same solidity coding syntax. This simply tells the compiler what compilation checks to use depending on the version.

The code between lines 17 & 61 are a very well known “library” of mathematical calculations known as “SafeMath”, an industry standard in solidity, often imported into many smart contracts. You can do a google search for this library if you want to know more.

We next have a small section of code pointing to the functions used in our “ESR_Token” contract. This was the actual smart contract issued when the ESR tokens were originally generated.

contract ESR_Token {
function thisContractAddress() public pure returns (address) {}
function balanceOf(address) public pure returns (uint256) {}
function transfer(address, uint) public {}
}

We are referencing three functions from that original ESR token smart contract so that this “token pre-sale” smart contract is aware of them.

We need to be able to know what that ESR token address is, what the balance of ESR tokens are for wallet addresses, and also be able to transfer ESR tokens from this “pre-sale” smart contract, to individual HPB wallets participating in the pre-sale.

We then move to the actual main contract starting on line 80:

contract TokenSale {
using SafeMath for uint256;

So our contract is called “TokenSale” and we will be using the “SafeMath” library when dealing with unsigned integers (numbers) in the contract that can go up to 256 bits in size (big numbers!)

Next we declare that the functions we will borrow from the “ESR_Token” contract will be referenced with the word “token”

ESR_Token public token;

Next we declare a whole bunch of addresses:

address public admin;

that’s me! The address of my wallet when I create the “pre-sale smart contract will be assigned as admin.

address public thisContractAddress;

This is fairly self-explanatory. This will be the address of the smart contract.

address public tokenContractAddress = 0xa7Be5e053cb523585A63F8F78b7DbcA68647442F;

This is the original “custom token” address of the ESR token. If you search for this address in HPB Scan you’ll see the ESR token.

address public tokenBurnAddress = 0x30171d518E3627E2006C9645d63e2a0A60F50f99;

When the pre-sale ends, any remaining ESR tokens stored in the smart contract will be sent to this address. The “owner” of this address is 0x0, which is basically a null and void address, therefore nobody can ever access it, and the tokens will be locked there forever….basically “burned”!

address payable public teamFundAddress = 0xF3E8352bacB923FA385Bf34C61B92cC94515a57a;

This address is another smart contract. It’s the address to which 10% of HPB generated by this pre-sale will go. This smart-contract is “time-locked” with a special withdrawal function whereby I cannot withdraw any funds from this contract until the date passes 19th July 2021. If I try to withdraw before this date, the transaction will fail.

address payable public liquidityFundAddress = 0xFF3c2E48dE1C8801337a16fE7944b4b5Df20A2cC;

This is a wallet address where 50% of HPB will automatically go, and is reserved to create liquidity pools on CEX/DEX exchanges for trading ESR.

address payable public investmentFundAddress = 0x4053284F7bA7Ac7DF52DBBc12b42e6Fa2956Bba1;

This is another wallet address. 40% of the HPB from the pre-sale will go to this wallet, to be used for marketing, advertising, promotion and other elements of making ESR a success!

This next section dictates the opening and closing times of the pre-sale.

// starting time and closing time of ESR token sale
// scheduled start on Friday, April 16th 2021 at 09:00am GMT
// (1618563600) - https://www.epochconverter.com/
uint public openingTime = 1618563600;
uint public closingTime = openingTime.add(5 days);

This Solidity contracts use something known as “epoch time” and you need to convert real times (e.g. 04/05/2012 17:52:07) into epoch time to pass it to solidity. You can visit https://www.epochconverter.com/ for more details, but basically the value of 1618563600 in epoch time, represents Friday, April 16th 2021 at 09:00am GMT

The closing time is simple….add exactly 5 days to the opening time to get the closing time :)

We then move onto a section of code that lets me track how the token sale is going:

uint public preIcoPhaseCountdown;       // used for website tokensale tracking
uint public icoPhaseCountdown; // used for website tokensale tracking
uint public postIcoPhaseCountdown; // used for website tokensale tracking

The next section allows for any emergency situations where the token pre-sale would need to be paused (Hopefully I won’t need to use this!)

// pause token sale in an emergency [true/false]
bool public tokenSaleIsPaused;

// note the pause time to allow special function to extend closingTime
uint public tokenSalePausedTime;

// note the resume time
uint public tokenSaleResumedTime;

// The time (in seconds) that needs to be added on to the closing time
// in the event of an emergency pause of the token sale
uint public tokenSalePausedDuration;

The next section references HPB in values of wei — Just like Ethereum, HPB is referenced to 18 decimal places, so you could send someone 1.000000000000000001 HPB if you really wanted to!

It also references the number of tokens for sale, and the maximum amount of Wei that could be raised. (1,000,000,000,000,000,000 wei in 1 HPB)

// Amount of wei raised
uint256 public weiRaised;

// 10 ESR tokens per HPB - 20,000,000 ESR tokens for sale
uint public maxHPBRaised = 2000000;

// Maximum amount of Wei that can be raised
// e.g. 20,000,000 tokens for sale with 10 tokens per 1 hpb
// means maximum Wei raised would be maxHPBRaised * 1000000000000000000
uint public maxWeiRaised = maxHPBRaised.mul(1000000000000000000);
// used as a divider so that 1 HPB will buy 10 ESR tokens
// set rate to 100,000,000,000,000,000
uint public rate = 100000000000000000;

The next section declares what the minimum and maximum spend (in HPB, expressed as wei) can be when making an ESR token purchase

// minimum and maximum spend of HPB per transaction
uint public minSpend = 1000000000000000000; // 1 HPB
uint public maxSpend = 10000000000000000000000; // 10,000 HPB

As you can see, you need to send a minimum of 1 HPB, and a maximum of 10,000 HPB in a single transaction or else the transaction will fail.

Next we have what’s known as a “modifier” -

// MODIFIERS
modifier onlyAdmin {
require(msg.sender == admin
);
_;
}

To break this down, it’s basically saying that any function that’s called in the smart contract, which happens to be “tagged” with the onlyAdmin keyword, must be the admin (me!) — so basically the caller of the function (msg.sender) must be “admin”

Next we decalre some events. Events are basically data expressed when functions are called. They are very useful for DApps and websites, which will uses these events to display information on the webpage or app, containing the event information.

// EVENTS
event Deployed(string, uint);
event SalePaused(string, uint);
event SaleResumed(string, uint);
event TokensBurned(string, uint);

When the contract is first “deployed” onto the HPB blockchain, an even will be written. If the pre-sale is paused in an emergency, an event will be written. If the sale then resumes, an event will be written, and when the remaining tokens in the smart contract get burned after the pre-sale, an event will be written.

Now we switch to a very special smart contract function known as the “constructor”

constructor() public {

admin = msg.sender;
thisContractAddress = address(this);
token = ESR_Token(tokenContractAddress);require(liquidityFundAddress != address(0));
require(investmentFundAddress != address(0));
require(teamFundAddress != address(0));
require(tokenContractAddress != address(0));
require(tokenBurnAddress != address(0));
preIcoPhaseCountdown = openingTime;
icoPhaseCountdown = closingTime;
emit Deployed("ESR Token Sale contract deployed", now);
}

The constructor is special because it can only be called once, when the smart contract is actually being deployed onto the blockchain. It’s once and once only.

Let’s break down what’s actually happening in this constructor function. Firstly we are saying that the person deploying this contract (msg.sender) should be assigned the role of admin. Seeing as I am the one deploying the contract from my HPB wallet, I will be assigned as admin.

Next we are saying that the keyword “token” should be assigned the ESR token contract address from the original ESR smart contract.

We now have some “require” statements. All of these require statements must be adhered to for the constructor function to execute successfully.

So we’re saying that the “liquidityFundAddress” CANNOT (cannot is expressed as != in solidity) have a zero address, i.e. there must be a valid address assigned to this.

The exact same applies to the “investmentFundAddress”, the “teamFundAddress”, the “tokenContractAddress” and the “tokenBurnAddress”.

Next, we’re telling the smart contract that the value for “openingTime” should also be assigned to “preIcoPhaseCountdown” and the value for “closingTime” should be assigned to “icoPhaseCountdown”. Again, this is just for pre-sale tracking.

The final section is to write the event. Remember the event for “Deployed” I mention, well this emits that even detail, and the “now” keyword, writes the time at which the smart contract was written.

emit Deployed("ESR Token Sale contract deployed", now);

The next section of the smart contract are four public functions that anyone can call to “view” information on the smart contract:

// check balance of THIS smart contract in wei (18 decimals)
function tokenSaleTokenBalanceinWei() public view returns(uint) {
return token.balanceOf(thisContractAddress);
}

// check balance of this smart contract
function tokenSaleTokenBalance() public view returns(uint) {
return tokenSaleTokenBalanceinWei().div(1000000000000000000);
}

// check the token balance of any HPB Wallet address
function getAnyAddressTokenBalance(address _address) public view returns(uint) {
return token.balanceOf(_address);
}

// confirm if The Token Sale has finished
function tokenSaleHasFinished() public view returns (bool) {
return now > closingTime;
}

“view” (a.k.a. READ) functions are represented as blue buttons in remix when contracts are deployed or loaded, and anyone can click on them.

Next we have an important function…token burn!

// this function will send any unsold tokens to the null TokenBurn contract address
// once the token sale is finished, ANYONE can publicly call this function!
function burnUnsoldTokens() public {
require(tokenSaleIsPaused == false);
// can only be called after the close time
require(tokenSaleHasFinished() == true);
token.transfer(tokenBurnAddress, tokenSaleTokenBalanceinWei());
emit TokensBurned("tokens sent to TokenBurn contract", now);
}

This is a public function that anyone can call, as long as the require statements are met. So you can see that the function can be called, providing that the “tokenSaleIsPaused” value is set to false (i.e. the pre-sale hasn’t been paused in an emergency!) and that the “tokenSaleHasFinished” value is set to true (i.e. you cannot call this function until the pre-sale has ended)

Once you call this function (assuming the require statements are adhered to) then it initiates a token transfer to the tokenBurnAddress, declared as one of the address variables.

Also another event is written using the emit keyword.

The next section are functions specifically related to being able to pause/resume the token sale in the event of an emergency:

// function to temporarily pause the token sale if needed
function pauseTokenSale() onlyAdmin public {
// confirm the token sale hasn't already completed
require(tokenSaleHasFinished() == false);

// confirm the token sale isn't already paused
require(tokenSaleIsPaused == false);

// pause the sale and note the time of the pause
tokenSaleIsPaused = true;
tokenSalePausedTime = now;
emit SalePaused("token sale has been paused", now);
}

// function to resume token sale
function resumeTokenSale() onlyAdmin public {

// confirm the token sale is currently paused
require(tokenSaleIsPaused == true);

tokenSaleResumedTime = now;

// now calculate the difference in time between the pause time
// and the resume time, to establish how long the sale was
// paused for. This time now needs to be added to the closingTime.

// Note: if the token sale was paused whilst the sale was live and was
// paused before the sale ended, then the value of tokenSalePausedTime
// will always be less than the value of tokenSaleResumedTime

tokenSalePausedDuration = tokenSaleResumedTime.sub(tokenSalePausedTime);

// add the total pause time to the closing time.

closingTime = closingTime.add(tokenSalePausedDuration);

// now resume the token sale
tokenSaleIsPaused = false;
emit SaleResumed("token sale has now resumed", now);
}

You’ll see that each of these functions includes the “modifier” keyword of onlyAdmin which means only I can call them.

Next we have event logging. Its fairly self-explanatory what it’s tracking:

event TokenPurchase(
address indexed purchaser,
address indexed beneficiary,
uint256 value,
uint256 amount
);

Next we have what is known as a fall-back function. This is a special function that allow you to simply send HPB to the smart contract without declaring anything else. Any function contained within this fall-back function will be executed each and every time HPB is sent to the wallet.

function () external payable {
buyTokens(msg.sender);
}

The function contained within this “catch-all” fallback function is the function “buyTokens” — So basically every time the sender (msg.sender) send HPB to this smart contract, the “buyTokens” function is called.

Next we have the actual “buyTokens” function:

function buyTokens(address buyer) public payable {

// check Tokensale is open (can disable for testing)
require(openingTime <= block.timestamp);
require(block.timestamp < closingTime);

// minimum purchase of 10 ESR tokens (1 HPB)
require(msg.value >= minSpend);

// maximum purchase per transaction to allow broader
// token distribution during tokensale
require(msg.value <= maxSpend);

// stop sales of tokens if token balance is les than 10 ESR tokens
require(tokenSaleTokenBalanceinWei() > 10000000000000000000);

// stop sales of tokens if Token sale is paused
require(tokenSaleIsPaused == false);

// log the amount being sent
uint256 weiAmount = msg.value;
preValidatePurchase(buyer, weiAmount);
// calculate token amount to be sold
uint256 tokens = weiAmount.mul(10);
// tokens = tokens.mul(100000000000000000);

// check that the amount of eth being sent by the buyer
// does not exceed the equivalent number of tokens remaining
require(tokens <= tokenSaleTokenBalanceinWei());
// update state
weiRaised = weiRaised.add(weiAmount);
processPurchase(buyer, tokens);
emit TokenPurchase(
msg.sender,
buyer,
weiAmount,
tokens
);
updatePurchasingState(buyer, weiAmount);// send to wallet for project distribution
forwardFunds();
postValidatePurchase(buyer, weiAmount);
}

Lets chop this long function up into sections:

    
// check Tokensale is open (can disable for testing)
require(openingTime <= block.timestamp);
require(block.timestamp < closingTime);

There are two require statements here. The first is basically saying that the current time (block.timestamp) is greater than the opening time. Basically if the time now has passed the declared “opening time” of the pre-sale, then the require statement is true.

The same goes for the closing time. The current time (block-timestamp) must be before the closing time for you to be able to send HPB to the contract.

We then have two more require statements. We require that the amount of HPB sent (msg.value) meets the minimum and maximum requirements:

// minimum purchase of 10 ESR tokens (1 HPB)
require(msg.value >= minSpend);

// maximum purchase per transaction to allow broader
// token distribution during tokensale
require(msg.value <= maxSpend);

We then have a couple more require statements:

// stop sales of tokens if token balance is les than 10 ESR tokens
require(tokenSaleTokenBalanceinWei() > 10000000000000000000);

// stop sales of tokens if Token sale is paused
require(tokenSaleIsPaused == false);

Basically if the balance of ESR tokens in the smart contract drops below 10, then no more can be sold, because the minimum number of HPB must be 1 HPB (i.e. 10 ESR tokens) to if there were 9 ESR tokens in the smart contract, you wouldn’t be able to buy them as this would mean sending 0.9 HPB which is not allowed.

It also checks that the token sale is not paused.

Next we log the amount being sent in wei (msg.value) , calculate how many tokens will need to be sent (HPB value multiplied by 10) and check that there are enough tokens left in the contract to fufill the sale.

We then update the value for weiRaised, and emit the event details of the transaction.

// log the amount being sent
uint256 weiAmount = msg.value;
preValidatePurchase(buyer, weiAmount);
// calculate token amount to be sold
uint256 tokens = weiAmount.mul(10);
// tokens = tokens.mul(100000000000000000);

// check that the amount of hpb being sent by the buyer
// does not exceed the equivalent number of tokens remaining
require(tokens <= tokenSaleTokenBalanceinWei());
// update state
weiRaised = weiRaised.add(weiAmount);
processPurchase(buyer, tokens);
emit TokenPurchase(
msg.sender,
buyer,
weiAmount,
tokens
);

You’ll see that the “buyTokens” function also contains an embedded function called “ forwardFunds” so whenever this “buyTokens” function is called, it will also execute the “forwardFunds” function.

Lets look at this more closely….

function forwardFunds() internal {
liquidityFundAddress.transfer((msg.value.div(100)).mul(50));
investmentFundAddress.transfer((msg.value.div(100)).mul(40));
teamFundAddress.transfer((msg.value.div(100)).mul(10));
}

This function is performing three sequential crypto transfers.

It is automatically sending 50% of the incoming HPB to the “liquidityFundAddress”, 40% of the HPB to the “investmentFundAddress”, and finally 10% of the HPB to the “teamFundAddress”

There are a few more functions remaining, but hopefully you now have a feel for deciphering a smart contract, and if you want to delve deeper, there are a ton of free resoureces out there on the internet!

Ok so the last thing is to actually interact with the smart contract directly from the remix IDE!

If you switch to the “Run” tab in Remix, you can actually load up the ESR token pre-sale contract and interact with its functions.

You’ll need Metamask to do this. Assuming you have metamask installed, and it’s setup to point to the HPB network then you can interact with the contract. If you haven’t done so already, you’ll need to add the following “custom network” values into Metamask to point to the HPB network.

If you’re all setup and Metamask is pointing to the HPB network, then in Remix, you’ll want to switch from the “Javascript VM” environment, to the “Injected Web3” environment. This will connect Metamask to Remix. If it works correctly, you’ll see details of your Metamask HPB wallet address in the “Account” field.

Select “TokenSale” from the contract drop-down if it isn’t already pre-selected.

Next you’ll want to “load” the ESR token pre-sale smart contract address into the “At Address” box. This will load up the functions to READ/WRITE with the ESR presale smart contract. The address you need to add in is the same address that people will be sending HPB to in order to obtain their ESR tokens. The address is:

0xebcd446644647ec8a9c8eff1c79b7a8629bcbfdf

Once you do this, click on the “At Address” button, and it will load up the TokenSale smart contract below:

you can now click on the little arrow drop-down to reveal all of the functions within the smart contract. Red buttons represent “Write” functions, and cost gas to call. Most will be restricted to admin, apart from the “burnUnsoldTokens” function which can be called by anyone, once the tokensale is finished.

You also have blue buttons. These represent “READ” functions, and do not require gas. You can click on any of them to view information sotred within the smart contract on the blockchain.

If you want to actually purchase ESR tokens from within Remix you can!

Simply type in the amount of HPB you wish to send in the Value box (set to HPB) and then click on the “fallback” function, which will call the “buyTokens” function.

This will prompt Metamask to appear, asking you to confirm you wish to send your HPB to this smart contract address, and voila, you have made a purchase via the Remix IDE!

You can also view the smart contracts for the TeamFunds contract (time-locked) the TokenBurn smart contract, and a list of all the associated wallet address for the ESR project on the github:

https://github.com/esrdapp/tokensale

--

--

Jeff P

I tend to write about anything I find interesting. There’s not much more to it than that really :-)