As explained in my previous post my first smart contract is based on the “Withdrawal from Contracts” example from the Solidity Common Patterns documentation. In this post I review some of the Solidity code I learned.
Smart Contract Intro
The example in the documentation was inspired by the King of the Ether game and the rules of the contract are:
- To play a user submits a value of X Ether paid from his address
- If X > historicHighest:
- user is king
- old king can collect previous payments
- historicHighest = X
The contract itself is:
pragma solidity ^0.4.2; contract WithdrawalContract { address public richest; uint public mostSent; mapping (address => uint) pendingWithdrawals; function WithdrawalContract() payable { richest = msg.sender; mostSent = msg.value; } event BecameRichestEvent(address sender, uint amount); event FailedRichestEvent(address sender, uint amount); event Collected(uint amount); function BecomeRichest() payable returns (bool) { if (msg.value > mostSent) { pendingWithdrawals[richest] += msg.value; richest = msg.sender; mostSent = msg.value; BecameRichestEvent(msg.sender, msg.value); return true; } else { FailedRichestEvent(msg.sender, msg.value); return false; } } function withdraw() { uint amount = pendingWithdrawals[msg.sender]; // Remember to zero the pending refund before // sending to prevent re-entrancy attacks pendingWithdrawals[msg.sender] = 0; msg.sender.transfer(amount); Collected(amount); } }
Solidity Specifics
I found this Introduction to Smart Contracts documentation a good source of information.
Addresses – address public richest;
Type address — holds a 20byte Ethereum address. Here it’s used to save the address of the user who is currently the richest.
public
allows other contracts to access the variable.
An address has various member functions as detailed here. One of these is address.transfer(uint256 amount)
which sends the given amount of Wei to the address. In this case transfer is used in the Withdraw function to safely send Ether to a valid users address.
Mappings - mapping (address => uint) pendingWithdrawals;
The next line, mapping (address => uint) public balances;
also creates a public state variable, but it is a more complex datatype.
Mappings can be seen as hash tables. pendinWithdrawals maps addresses to unsigned integers. For example:
pendingWithdrawals[0xc7975434577b0d57248e1de03fcc6f27cd6cc742] = 7;
Maps the value 7 to address 0xc7975434577b0d57248e1de03fcc6f27cd6cc742 and can be called using:
x = pendingWithdrawals[0xc7975434577b0d57248e1de03fcc6f27cd6cc742];
x = 7
Transactions & Constructors – function WithdrawalContract() payable
This is a constructor – a function that is run when the contract is first created.
The payable modifier means the function is a transaction. A transaction is the transfer of data between two accounts (good explanation here). A transaction contains some standard data e.g. sender address or the amount of wei being transferred. We can access the transaction data using the global msg
variable.
In this case msg is used to set the initial richest address to msg.sender which is the address of the person deploying the contract. It also saves the mostSent as the msg.value which is the number of wei sent during the deployment.
Events – event BecameRichestEvent(address sender, uint amount);
The front end of the application, in my case Web3.js, listens for Solidity events being fired so it can update the interface. For example the BecameRichest event fires when a new account becomes the richest.
The event is fired by the line:
BecameRichestEvent(msg.sender, msg.value);
The listener will have access to the sender and value data passed to the call.
In the next post I plan to detail some of the front end functionality, including how to listen for events.