Initial update to latest solidity

This commit is contained in:
Nemil Dalal 2017-12-28 18:39:48 -08:00
parent 70a36c9bd9
commit 84881b3cda

View File

@ -37,8 +37,8 @@ features are typically marked, and subject to change. Pull requests welcome.
// simple_bank.sol (note .sol extension)
/* **** START EXAMPLE **** */
// Declare the source file compiler version.
pragma solidity ^0.4.2;
// Declare the source file compiler version
pragma solidity ^0.4.19;
// Start with Natspec comment (the three slashes)
// used for documentation - and as descriptive data for UI elements/actions
@ -65,7 +65,7 @@ contract SimpleBank { // CapWords
event LogDepositMade(address accountAddress, uint amount);
// Constructor, can receive one or many variables here; only one allowed
function SimpleBank() {
function SimpleBank() public {
// msg provides details about the message that's sent to the contract
// msg.sender is contract caller (address of contract creator)
owner = msg.sender;
@ -73,7 +73,11 @@ contract SimpleBank { // CapWords
/// @notice Deposit ether into bank
/// @return The balance of the user after the deposit is made
function deposit() public returns (uint) {
function deposit() public payable returns (uint) {
// Use 'require' to test user inputs, 'assert' for internal invariants
// Here we are making sure that there isn't an overflow issue
require((balances[msg.sender] + msg.value) >= balances[msg.sender]);
balances[msg.sender] += msg.value;
// no "this." or "self." required with state variable
// all values set to data type's initial value by default
@ -88,18 +92,17 @@ contract SimpleBank { // CapWords
/// @param withdrawAmount amount you want to withdraw
/// @return The balance remaining for the user
function withdraw(uint withdrawAmount) public returns (uint remainingBal) {
if(balances[msg.sender] >= withdrawAmount) {
// Note the way we deduct the balance right away, before sending - due to
// the risk of a recursive call that allows the caller to request an amount greater
// than their balance
balances[msg.sender] -= withdrawAmount;
require(withdrawAmount <= balances[msg.sender]);
if (!msg.sender.send(withdrawAmount)) {
// increment back only on fail, as may be sending to contract that
// has overridden 'send' on the receipt end
balances[msg.sender] += withdrawAmount;
}
}
// Note the way we deduct the balance right away, before sending
// Every .transfer/.send from this contract can call an external function
// This may allow the caller to request an amount greater
// than their balance using a recursive call
// Aim to commit state before calling external functions, including .transfer/.send
balances[msg.sender] -= withdrawAmount;
// this automatically throws on a failure, which means the updated balance is reverted
msg.sender.transfer(withdrawAmount);
return balances[msg.sender];
}
@ -108,18 +111,9 @@ contract SimpleBank { // CapWords
/// @return The balance of the user
// 'constant' prevents function from editing state variables;
// allows function to run locally/off blockchain
function balance() constant returns (uint) {
function balance() constant public returns (uint) {
return balances[msg.sender];
}
// Fallback function - Called if other functions don't match call or
// sent ether without data
// Typically, called when invalid data is sent
// Added so ether sent to this contract is reverted if the contract fails
// otherwise, the sender's money is transferred to contract
function () {
throw; // throw reverts state to before call
}
}
// ** END EXAMPLE **
@ -137,6 +131,11 @@ int256 constant a = 8; // same effect as line above, here the 256 is explicit
uint constant VERSION_ID = 0x123A1; // A hex constant
// with 'constant', compiler replaces each occurrence with actual value
// All state variables (those outside a function)
// are by default 'internal' and accessible inside contract
// and in all contracts that inherit ONLY
// Need to explicitly set to 'public' to allow external contracts to access
int256 public a = 8;
// For int and uint, can explicitly set space in steps of 8 up to 256
// e.g., int8, int16, int24
@ -145,6 +144,12 @@ int64 c;
uint248 e;
// Be careful that you don't overflow, and protect against attacks that do
// For example, for an addition, you'd do:
uint256 c = a + b;
assert(c >= a); // assert tests for internal invariants; require is used for user inputs
// For more examples of common arithmetic issues, see Zeppelin's SafeMath library
// https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/math/SafeMath.sol
// No random functions built in, use other contracts for randomness
@ -165,14 +170,14 @@ address public owner;
// a getter is automatically created, but NOT a setter
// All addresses can be sent ether
owner.send(SOME_BALANCE); // returns false on failure
if (owner.send) {} // REMEMBER: wrap in 'if', as contract addresses have
owner.transfer(SOME_BALANCE); // fails and reverts on failure
// Can also do a lower level .send call, which returns a false if it failed
if (owner.send) {} // REMEMBER: wrap send in 'if', as contract addresses have
// functions executed on send and these can fail
// Also, make sure to deduct balances BEFORE attempting a send, as there is a risk of a recursive
// call that can drain the contract
// can override send by defining your own
// Can check balance
owner.balance; // the balance of the owner (user or contract)
@ -213,7 +218,7 @@ uint x = 5;
// Destructuring/Tuples
(x, y) = (2, 7); // assign/swap multiple value
(x, y) = (2, 7); // assign/swap multiple values
// 2. DATA STRUCTURES
@ -250,7 +255,7 @@ delete balances; // sets all elements to 0
// mapping, without knowing source keys - can build data structure
// on top to do this
// Structs and enums
// Structs
struct Bank {
address owner;
uint balance;
@ -273,7 +278,7 @@ state = State.Created;
// enums can be explicitly converted to ints
uint createdState = uint(State.Created); // 0
// Data locations: Memory vs. storage vs. stack - all complex types (arrays,
// Data locations: Memory vs. storage vs. calldata - all complex types (arrays,
// structs) have a data location
// 'memory' does not persist, 'storage' does
// Default is 'storage' for local and state variables; 'memory' for func params
@ -292,13 +297,13 @@ uint createdState = uint(State.Created); // 0
// 4. Global Variables of note
// ** this **
this; // address of contract
// often used at end of contract life to send remaining balance to party
// often used at end of contract life to transfer remaining balance to party
this.balance;
this.someFunction(); // calls func externally via call, not via internal jump
// ** msg - Current message received by the contract ** **
msg.sender; // address of sender
msg.value; // amount of ether provided to this contract in wei
msg.value; // amount of ether provided to this contract in wei, the function should be marked "payable"
msg.data; // bytes, complete call data
msg.gas; // remaining gas
@ -308,6 +313,8 @@ tx.gasprice; // gas price of the transaction
// ** block - Information about current block **
now; // current time (approximately), alias for block.timestamp (uses Unix time)
// Note that this can be manipulated by miners, so use carefully
block.number; // current block number
block.difficulty; // current block difficulty
block.blockhash(1); // returns bytes32, only works for most recent 256 blocks
@ -334,9 +341,10 @@ function increment(uint x, uint y) returns (uint x, uint y) {
// Call previous functon
uint (a,b) = increment(1,1);
// 'constant' indicates that function does not/cannot change persistent vars
// 'constant' (alias for 'view')
// indicates that function does not/cannot change persistent vars
// Constant function execute locally, not on blockchain
uint y;
uint y = 1;
function increment(uint x) constant returns (uint x) {
x += 1;
@ -344,13 +352,21 @@ function increment(uint x) constant returns (uint x) {
// y is a state variable, and can't be changed in a constant function
}
// 'pure' is more strict than 'constant', and does not
// even allow reading of state vars
// The exact rules are more complicated, so see more about
// constant/pure:
// http://solidity.readthedocs.io/en/develop/contracts.html#view-functions
// 'Function Visibility specifiers'
// These can be placed where 'constant' is, including:
// public - visible externally and internally (default)
// external
// public - visible externally and internally (default for function)
// external - only visible externally (including a call made with this.)
// private - only visible in the current contract
// internal - only visible in current contract, and those deriving from it
// Generally, a good idea to mark each function explicitly
// Functions hoisted - and can assign a function to a variable
function a() {
var z = b;
@ -361,8 +377,15 @@ function b() {
}
// All functions that receive ether must be marked 'payable'
function depositEther() public payable {
balances[msg.sender] += msg.value;
}
// Prefer loops to recursion (max call stack depth is 1024)
// Also, don't setup loops that you haven't bounded,
// as this can hit the gas limit
// B. Events
// Events are notify external parties; easy to search and
@ -378,7 +401,8 @@ event LogSent(address indexed from, address indexed to, uint amount); // note ca
// Call
Sent(from, to, amount);
// For an external party (a contract or external entity), to watch:
// For an external party (a contract or external entity), to watch using
// the Web3 Javascript library:
Coin.Sent().watch({}, '', function(error, result) {
if (!error) {
console.log("Coin transfer: " + result.args.amount +
@ -398,10 +422,10 @@ Coin.Sent().watch({}, '', function(error, result) {
// '_' (underscore) often included as last line in body, and indicates
// function being called should be placed there
modifier onlyAfter(uint _time) { if (now <= _time) throw; _ }
modifier onlyOwner { if (msg.sender == owner) _ }
modifier onlyAfter(uint _time) { require (now >= _time); _; }
modifier onlyOwner { require(msg.sender == owner) _; }
// commonly used with state machines
modifier onlyIfState (State currState) { if (currState != State.A) _ }
modifier onlyIfStateA (State currState) { require(currState == State.A) _; }
// Append right after function declaration
function changeOwner(newOwner)
@ -415,12 +439,10 @@ onlyIfState(State.A)
// underscore can be included before end of body,
// but explicitly returning will skip, so use carefully
modifier checkValue(uint amount) {
_
_;
if (msg.value > amount) {
uint amountToRefund = amount - msg.value;
if (!msg.sender.send(amountToRefund)) {
throw;
}
msg.sender.transfer(amountToRefund);
}
}
@ -437,22 +459,21 @@ modifier checkValue(uint amount) {
// amount of gas for a block of code - and will fail if that is exceeded
// For example:
for(uint x = 0; x < refundAddressList.length; x++) {
if (!refundAddressList[x].send(SOME_AMOUNT)) {
throw;
}
refundAddressList[x].transfer(SOME_AMOUNT);
}
// Two errors above:
// 1. A failure on send stops the loop from completing, tying up money
// 1. A failure on transfer stops the loop from completing, tying up money
// 2. This loop could be arbitrarily long (based on the amount of users who need refunds), and
// therefore may always fail as it exceeds the max gas for a block
// Instead, you should let people withdraw individually from their subaccount, and mark withdrawn
// e.g., favor pull payments over push payments
// 7. OBJECTS/CONTRACTS
// A. Calling external contract
contract infoFeed {
contract InfoFeed {
function info() returns (uint ret) { return 42; }
}
@ -502,23 +523,10 @@ function someAbstractFunction(uint x);
import "filename";
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol";
// Importing under active development
// Cannot currently be done at command line
// 8. OTHER KEYWORDS
// A. Throwing
// Throwing
throw; // reverts unused money to sender, state is reverted
// Can't currently catch
// Common design pattern is:
if (!addr.send(123)) {
throw;
}
// B. Selfdestruct
// A. Selfdestruct
// selfdestruct current contract, sending funds to address (often creator)
selfdestruct(SOME_ADDRESS);
@ -543,7 +551,7 @@ function remove() {
// that is private needs to be obfuscated (e.g., hashed w/secret)
// Steps: 1. Commit to something, 2. Reveal commitment
sha3("some_bid_amount", "some secret"); // commit
keccak256("some_bid_amount", "some secret"); // commit
// call contract's reveal function in the future
// showing bid plus secret that hashes to SHA3
@ -617,6 +625,7 @@ contract SomeOracle {
// ** START EXAMPLE **
// CrowdFunder.sol
pragma solidity ^0.4.19;
/// @title CrowdFunder
/// @author nemild
@ -650,22 +659,20 @@ contract CrowdFunder {
event LogWinnerPaid(address winnerAddress);
modifier inState(State _state) {
if (state != _state) throw;
_
require(state == _state);
_;
}
modifier isCreator() {
if (msg.sender != creator) throw;
_
require(msg.sender == creator);
_;
}
// Wait 6 months after final contract state before allowing contract destruction
// Wait 24 weeks after final contract state before allowing contract destruction
modifier atEndOfLifecycle() {
if(!((state == State.ExpiredRefund || state == State.Successful) &&
completeAt + 6 months < now)) {
throw;
}
_
require(((state == State.ExpiredRefund || state == State.Successful) &&
completeAt + 24 weeks < now));
_;
}
function CrowdFunder(
@ -673,6 +680,7 @@ contract CrowdFunder {
string _campaignUrl,
address _fundRecipient,
uint _minimumToRaise)
public
{
creator = msg.sender;
fundRecipient = _fundRecipient;
@ -683,7 +691,9 @@ contract CrowdFunder {
function contribute()
public
payable
inState(State.Fundraising)
returns(uint256 id)
{
contributions.push(
Contribution({
@ -699,7 +709,9 @@ contract CrowdFunder {
return contributions.length - 1; // return id
}
function checkIfFundingCompleteOrExpired() {
function checkIfFundingCompleteOrExpired()
public
{
if (totalRaised > minimumToRaise) {
state = State.Successful;
payOut();
@ -715,31 +727,23 @@ contract CrowdFunder {
public
inState(State.Successful)
{
if(!fundRecipient.send(this.balance)) {
throw;
}
fundRecipient.transfer(this.balance);
LogWinnerPaid(fundRecipient);
}
function getRefund(id)
public
function getRefund(uint256 id)
inState(State.ExpiredRefund)
public
returns(bool)
{
if (contributions.length <= id || id < 0 || contributions[id].amount == 0 ) {
throw;
}
require(contributions.length > id && id >= 0 && contributions[id].amount != 0 );
uint amountToRefund = contributions[id].amount;
uint256 amountToRefund = contributions[id].amount;
contributions[id].amount = 0;
if(!contributions[id].contributor.send(amountToSend)) {
contributions[id].amount = amountToSend;
return false;
}
contributions[id].contributor.transfer(amountToRefund);
return true;
return true;
}
function removeContract()
@ -750,8 +754,6 @@ contract CrowdFunder {
selfdestruct(msg.sender);
// creator gets all money that hasn't be claimed
}
function () { throw; }
}
// ** END EXAMPLE **
@ -798,6 +800,7 @@ someContractAddress.callcode('function_name');
// 13. STYLE NOTES
// Based on Python's PEP8 style guide
// Full Style guide: http://solidity.readthedocs.io/en/develop/style-guide.html
// Quick summary:
// 4 spaces for indentation
@ -825,11 +828,15 @@ someContractAddress.callcode('function_name');
## Additional resources
- [Solidity Docs](https://solidity.readthedocs.org/en/latest/)
- [Smart Contract Best Practices](https://github.com/ConsenSys/smart-contract-best-practices)
- [Solidity Style Guide](https://ethereum.github.io/solidity//docs/style-guide/): Ethereum's style guide is heavily derived from Python's [pep8](https://www.python.org/dev/peps/pep-0008/) style guide.
- [Browser-based Solidity Editor](http://chriseth.github.io/browser-solidity/)
- [Browser-based Solidity Editor](https://remix.ethereum.org/)
- [Gitter Solidity Chat room](https://gitter.im/ethereum/solidity)
- [Modular design strategies for Ethereum Contracts](https://docs.erisindustries.com/tutorials/solidity/)
## Important libraries
- [Zeppelin](https://github.com/OpenZeppelin/zeppelin-solidity/): Libraries that provide common contract patterns (crowdfuding, safemath, etc)
## Sample contracts
- [Dapp Bin](https://github.com/ethereum/dapp-bin)
- [Solidity Baby Step Contracts](https://github.com/fivedogit/solidity-baby-steps/tree/master/contracts)
@ -841,9 +848,6 @@ someContractAddress.callcode('function_name');
- [Smart Contract Security](https://blog.ethereum.org/2016/06/10/smart-contract-security/)
- [Hacking Distributed Blog](http://hackingdistributed.com/)
## Information purposefully excluded
- Libraries
## Style
- Python's [PEP8](https://www.python.org/dev/peps/pep-0008/) is used as the baseline style guide, including its general philosophy