A smart contract is an agreement between two sides in the form of computer code. They run on the blockchain, so they are stored on a public database and cannot be changed. Smart contracts have varying levels of difficulty and can become quite complex. Here we will show three examples built on Solidity for Ethereum.
Navigate to the Remix IDE and create a new contract called AssociateProfitSplitter.sol using the starter code for level one above.
While developing and testing your contract, use the Ganache development chain, and point MetaMask to localhost:8545, or replace the port with what you have set in your workspace.
- Level One is an
AssociateProfitSplittercontract. This will accept Ether into the contract and divide the Ether evenly among the associate level employees. This will allow the Human Resources department to pay employees quickly and efficiently.
At the top of your contract, you will need to define the following public variables:
-
employee_one-- Theaddressof the first employee. Make sure to set this topayable. -
employee_two-- Anotheraddress payablethat represents the second employee. -
employee_three-- The thirdaddress payablethat represents the third employee.
Create a constructor function that accepts:
-
address payable _one -
address payable _two -
address payable _three
Within the constructor, set the employee addresses to equal the parameter values. This will allow you to avoid hardcoding the employee addresses.
Next, create the following functions:
-
balance-- This function should be set topublic view returns(uint), and must return the contract's current balance. Since we should always be sending Ether to the beneficiaries, this function should always return0. If it does not, thedepositfunction is not handling the remainders properly and should be fixed. This will serve as a test function of sorts. -
deposit-- This function should set topublic payablecheck, ensuring that only the owner can call the function.-
In this function, perform the following steps:
-
Set a
uint amountto equalmsg.value / 3;in order to calculate the split value of the Ether. -
Transfer the
amounttoemployee_one. -
Repeat the steps for
employee_twoandemployee_three. -
Since
uintonly contains positive whole numbers, and Solidity does not fully support float/decimals, we must deal with a potential remainder at the end of this function sinceamountwill discard the remainder during division. -
We may either have
1or2wei leftover, so transfer themsg.value - amount * 3back tomsg.sender. This will re-multiply theamountby 3, then subtract it from themsg.valueto account for any leftover wei, and send it back to Human Resources.
-
-
-
Create a fallback function using
function() external payable, and call thedepositfunction from within it. This will ensure that the logic indepositexecutes if Ether is sent directly to the contract. This is important to prevent Ether from being locked in the contract since we don't have awithdrawfunction in this use-case.
In the Deploy tab in Remix, deploy the contract to your local Ganache chain by connecting to Injected Web3 and ensuring MetaMask is pointed to localhost:8545.
You will need to fill in the constructor parameters with your designated employee addresses.
Test the deposit function by sending various values. Keep an eye on the employee balances as you send different amounts of Ether to the contract and ensure the logic is executing properly.
AssociateProfitSplitter - Contract Deployment
AssociateProfitSplitter - Contract Success
- Level Two is a
TieredProfitSplitterthat will distribute different percentages of incoming Ether to employees at different tiers/levels. For example, the CEO gets paid 60%, CTO 25%, and Bob gets 15%.
Using the starter code, within the deposit function, perform the following:
-
Calculate the number of points/units by dividing
msg.valueby100.- This will allow us to multiply the points with a number representing a percentage. For example,
points * 60will output a number that is ~60% of themsg.value.
- This will allow us to multiply the points with a number representing a percentage. For example,
-
The
uint amountvariable will be used to store the amount to send each employee temporarily. For each employee, set theamountto equal the number ofpointsmultiplied by the percentage (say, 60 for 60%). -
After calculating the
amountfor the first employee, add theamountto thetotalto keep a running total of how much of themsg.valuewe are distributing so far. -
Then, transfer the
amounttoemployee_one. Repeat the steps for each employee, setting theamountto equal thepointsmultiplied by their given percentage. -
For example, each transfer should look something like the following for each employee, until after transferring to the third employee:
-
Step 1:
amount = points * 60;-
For
employee_one, distributepoints * 60. -
For
employee_two, distributepoints * 25. -
For
employee_three, distributepoints * 15.
-
-
Step 2:
total += amount; -
Step 3:
employee_one.transfer(amount);
-
-
Send the remainder to the employee with the highest percentage by subtracting
totalfrommsg.value, and sending that to an employee. -
Deploy and test the contract functionality by depositing various Ether values (greater than 100 wei).
- The provided
balancefunction can be used as a test to see if the logic you have in thedepositfunction is valid. Since all of the Ether should be transferred to employees, this function should always return0, since the contract should never store Ether itself.
- The provided
TieredProfitSplitter - Contract Deployment
TieredProfitSplitter - Deposit Success
-
Level Three is a
DeferredEquityPlanthat models traditional company stock plans. This contract will automatically manage 1000 shares with an annual distribution of 250 over 4 years for a single employee. -
Human Resources will be set in the constructor as the
msg.sender, since HR will be deploying the contract. -
Below the
employeeinitialization variables at the top (afterbool active = true;), set the total shares and annual distribution:-
Create a
uintcalledtotal_sharesand set this to1000. -
Create another
uintcalledannual_distributionand set this to250. This equates to a 4 year vesting period for thetotal_shares, as250will be distributed per year. Since it is expensive to calculate this in Solidity, we can simply set these values manually. You can tweak them as you see fit, as long as you can dividetotal_sharesbyannual_distributionevenly.
-
-
The
uint start_time = now;line permanently stores the contract's start date. We'll use this to calculate the vested shares later. Below this variable, set theunlock_timeto equalnowplus365 days. We will increment each distribution period. -
The
uint public distributed_shareswill track how many vested shares the employee has claimed and was distributed. By default, this is0. -
In the
distributefunction:-
Add the following
requirestatements:-
Require that
unlock_timeis less than or equal tonow. -
Require that
distributed_sharesis less than thetotal_sharesthe employee was set for. -
Ensure to provide error messages in your
requirestatements.
-
-
After the
requirestatements, add365 daysto theunlock_time. This will calculate next year's unlock time before distributing this year's shares. We want to perform all of our calculations like this before distributing the shares. -
Next, set the new value for
distributed_sharesby calculating how many years have passed sincestart_timemultiplied byannual_distributions. For example:-
The
distributed_sharesis equal to(now - start_time)divided by365 days, multiplied by the annual distribution. Ifnow - start_timeis less than365 days, the output will be0since the remainder will be discarded. If it is something like400days, the output will equal1, meaningdistributed_shareswould equal250. -
Make sure to include the parenthesis around
now - start_timein your calculation to ensure that the order of operations is followed properly.
-
-
The final
ifstatement provided checks that in case the employee does not cash out until 5+ years after the contract start, the contract does not reward more than thetotal_sharesagreed upon in the contract.
-
-
Deploy and test your contract locally.
- For this contract, test the timelock functionality by adding a new variable called
uint fakenow = now;as the first line of the contract, then replace every other instance ofnowwithfakenow. Utilize the followingfastforwardfunction to manipulatefakenowduring testing.
- Congratulations on building three smart contracts on the Ethereum platform using solidity!












