“Proof of Workout” — How I use blockchain to commit to my workouts

Amr Saleh
The Elk Team
Published in
3 min readMay 29, 2019

I like MMA, I like working out, but I find it hard to commit to going to the gym with my busy schedule. I got a punching bag at home and I still struggle with committing to my workout. Knowing the rule of loss aversion, and that losing money hurts more than gaining muscle feels good I thought, why not add a financial motive to help me stick to my exercise plan!

The idea first came to me at Ethereum camp, an Ethereum-IoT hackathon in Berlin that my team and I went to last year. We demoed a punching bag using the Elk board and an accelerometer sensor.

Once we got back to Cairo, I thought to implement the concept on my punching bag at home, and to create a challenge: workout or lose money!

I wanted to challenge myself so I locked a $100 worth of Ether in a smart contract and committed to 500 punches a day for 10 days. If I finish my workout on schedule, I get my money back,and if I don’t, the money goes to charity.

I used an early prototype of Elk, our blockchain development board. It runs the Go Ethereum client in its light mode, and allows you to log sensors data to smart contracts easily with its SDK.

By connecting Elk to an accelerometer, I was able to count the number of punches/kicks, and I put a light bulb on top of the punching bag that blinks if the punch counts. And to know when I was done for the day, once I reached 500 punches, the board would blink five times for me.

With Elk, it’s incredibly easy to write code that interacts with smart contracts. Here’s the 64-line smart contract we used:

pragma solidity ^0.4.0;contract ProofOfWorkout {

address public charityAddress;
uint public charityAddressValue;
mapping (address => uint) public committedNumberOfPunches;
mapping (address => uint) public remainingNumberOfPunches;
mapping (address => uint) public expiryTime;
mapping (address => bool) public isCampaignRunning;
mapping (address => uint) public committedValue;
mapping (address => address) public boxingMachineAddress;

event CampaignBegan(address trainee, uint value, uint numberOfPunches, uint expiryTime, address boxingMachineAddress);
event ProgressChanged(address trainee, uint remainingPunches);
event CampaignEnded(address trainee, uint remainingPunches);

function ProofOfWorkout(address _charityAddress) public {
charityAddress = _charityAddress;
charityAddressValue = 0;
}

function beginCampaign(uint _numberOfPunches, uint _expiryTime, address _boxingMachineAddress) public payable {
require(_numberOfPunches>0 && !isCampaignRunning[msg.sender] && committedValue[msg.sender]==0 && msg.value>0);
isCampaignRunning[msg.sender] = true;
committedValue[msg.sender] = msg.value;
committedNumberOfPunches[msg.sender] = _numberOfPunches;
remainingNumberOfPunches[msg.sender] = _numberOfPunches;
expiryTime[msg.sender] = now + _expiryTime;
boxingMachineAddress[msg.sender] = _boxingMachineAddress;
emit CampaignBegan(msg.sender, msg.value, _numberOfPunches, _expiryTime, _boxingMachineAddress);
}

function reportProgress(address _traineeAddress, uint _reportedPunches) public {
require(msg.sender == boxingMachineAddress[_traineeAddress] && isCampaignRunning[_traineeAddress]);
if(now <= expiryTime[_traineeAddress]){
remainingNumberOfPunches[_traineeAddress] = remainingNumberOfPunches[_traineeAddress] >= _reportedPunches ? remainingNumberOfPunches[_traineeAddress] - _reportedPunches : 0;
emit ProgressChanged(_traineeAddress, remainingNumberOfPunches[_traineeAddress]);
if(remainingNumberOfPunches[_traineeAddress]==0){
isCampaignRunning[_traineeAddress] = false;
emit CampaignEnded(_traineeAddress, 0);
}
}
else {
isCampaignRunning[_traineeAddress] = false;
emit CampaignEnded(_traineeAddress, remainingNumberOfPunches[_traineeAddress]);
}
}

function withdrawTrainee() public {
require(!isCampaignRunning[msg.sender] && committedValue[msg.sender]>0);
uint traineeAmount = (committedNumberOfPunches[msg.sender]-remainingNumberOfPunches[msg.sender])*committedValue[msg.sender]/committedNumberOfPunches[msg.sender];
charityAddressValue = charityAddressValue + (committedValue[msg.sender] - traineeAmount);
committedValue[msg.sender] = 0;
msg.sender.transfer(traineeAmount);
}

function withdrawCharity() public {
require(msg.sender == charityAddress && charityAddressValue>0);
uint amount = charityAddressValue;
charityAddressValue = 0;
charityAddress.transfer(amount);
}
}

Elk makes it possible to connect the amazing properties of programmable money to the real world. My team and I will be launching Elk on Kickstarter next month. You can learn more about it and sign up for the early bird at elk.cc.

--

--

Amr Saleh
The Elk Team

Maker, Breaker, Sometimes Fixer, Husband, Co-founder & CEO of Elk.cc.