Smart contracts - Blockchain with Solidity
New startup has created its own Ethereum-compatible blockchain to help connect financial institutions, and the team wants to build smart contracts to automate some company finances to make everyone’s lives easier, increase transparency, and to make accounting and auditing practically automatic!
Build Smart contracts ProfitSplitter
using Solidity to perfrom several things:
Pay Associate-level employees quickly and easily.
Distribute profits to different tiers of employees.
Distribute company shares for employees in a “deferred equity incentive plan” automatically.
AssociateProfitSplitter.sol
— Level 1 starter code.
TieredProfitSplitter.sol
— Level 2 starter code.
DeferredEquityPlan.sol
— Level 3 starter code.
3 different smart contracts distributing to be created:
Level One is an AssociateProfitSplitter
contract. 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.
Level Two is a TieredProfitSplitter
that 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%.
Level Three is a DeferredEquityPlan
that 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.
localhost:8545
, or replace the port with the one set in the workspace.Deploy
tab in Remix, deploy the contracts to your local Ganache chain by connecting to Injected Web3
and ensuring Metamask is pointed to localhost:8545
employee
addressesdeposit
function by sending various values. Check the employee
balances as and when various amounts of Ether are sent to the contract and ensure the logic is executing properly.AssociateProfitSplitter
Contractpublic
variables:employee_one
— The address
of the first employee. Make sure to set this to payable
.
employee_two
— Another address payable
that represents the second employee.employee_three
— The third address payable
that represents the third employee.address payable _one
address payable _two
address payable _three
balance
— This function should be set to public view returns(uint)
, and must return the contract’s current balance. Since always Ether are to be sent to the beneficiaries, this function should always return 0
. If it does not, the deposit
function is not handling the remainders properly and should be fixed. This will serve as a test function of sorts.
deposit
— This function should set to public payable
check, ensuring that only the owner can call the function.uint amount
to equal msg.value / 3;
in order to calculate the split value of the Ether.amount
to employee_one
.
Repeat the steps for employee_two
and employee_three
.uint
only contains positive whole numbers, and Solidity does not fully support float/decimals, deal with a potential remainder at the end of this function since amount
will discard the remainder during division.
There maybe either 1
or 2
wei leftover, so transfer the msg.value - amount * 3
back to msg.sender
. This will re-multiply the amount
by 3, then subtract it from the msg.value
to account for any leftover wei, and send it back to Human Resources.function() external payable
, and call the deposit
function from within it. This will ensure that the logic in deposit
executes 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 a withdraw
function in this use-case.AssociateProfitSplitter
- Contract Deployment & ConfirmationTieredProfitSplitter
Contractdeposit
function, perform the following:msg.value
by 100
.
This will allow us to multiply the points with a number representing a percentage. For example, points * 60
will output a number that is ~60% of the msg.value
.uint amount
variable will be used to store the amount to send each employee temporarily. For each employee, set the amount
to equal the number of points
multiplied by the percentage (say, 60 for 60%).
After calculating the amount
for the first employee, add the amount
to the total
to keep a running total of how much of the msg.value
we are distributing so far.amount
to employee_one
. Repeat the steps for each employee, setting the amount
to equal the points
multiplied by their given percentage.
For example, each transfer should look something like the following for each employee, until after transferring to the third employee:* For
employee_one, distribute
points 60`.
For employee_two
, distribute points * 25
.employee_three
, distribute `points 15.
* Step 2:
total += amount;* Step 3:
employee_one.transfer(amount);* Send the remainder to the employee with the highest percentage by subtracting
totalfrom
msg.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 the
depositfunction is valid. Since all of the Ether should be transferred to employees, this function should always return
0, since the contract should never store Ether itself.
* Note: The 100 wei threshold is due to the way we calculate the points. If we send less than 100 wei, for example, 80 wei,
pointswould equal
0because
80 / 100equals
0` because the remainder is discarded. We will learn more advanced arbitrary precision division later in the course. In this case, we can disregard the threshold as 100 wei is a significantly smaller value than the Ether or Gwei units that are far more commonly used in the real world (most people aren’t sending less than a penny’s worth of Ether).DeferredEquityPlan
Contractmsg.sender
, since HR will be deploying the contract.
Below the employee
initialization variables at the top (after bool active = true;
), set the total shares and annual distribution:uint
called total_shares
and set this to 1000
.
Create another uint
called annual_distribution
and set this to 250
. This equates to a 4 year vesting period for the total_shares
, as 250
will 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 divide total_shares
by annual_distribution
evenly.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 the unlock_time
to equal now
plus 365 days
. We will increment each distribution period.
The uint public distributed_shares
will track how many vested shares the employee has claimed and was distributed. By default, this is 0
.distribute
function:
Add the following require
statements:unlock_time
is less than or equal to now
.
Require that distributed_shares
is less than the total_shares
the employee was set for.require
statements.
After the require
statements, add 365 days
to the unlock_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.distributed_shares
by calculating how many years have passed since start_time
multiplied by annual_distributions
. For example:
The distributed_shares
is equal to (now - start_time)
divided by 365 days
, multiplied by the annual distribution. If now - start_time
is less than 365 days
, the output will be 0
since the remainder will be discarded. If it is something like 400
days, the output will equal 1
, meaning distributed_shares
would equal 250
.now - start_time
in your calculation to ensure that the order of operations is followed properly.
The final if
statement 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 the total_shares
agreed upon in the contract.uint fakenow = now;
as the first line of the contract, then replace every other instance of now
with fakenow
. Utilize the following fastforward
function to manipulate fakenow
during testing.fakenow
):solidity
function fastforward() public {
fakenow += 100 days;
}
Once satisfied with the contract’s logic, revert the fakenow
testing logic.For some succinct and straightforward code snips, check out Solidity By Example
For a more extensive list of awesome Solidity resources, checkout Awesome Solidity
Another tutorial is available at EthereumDev.io
For building games, an excellent tutorial called CryptoZombies