learnxinyminutes-docs/solidity.html.markdown

860 lines
27 KiB
Markdown
Raw Normal View History

2015-11-23 21:02:00 +00:00
---
language: Solidity
filename: learnSolidity.sol
contributors:
2015-12-29 02:58:31 +00:00
- ["Nemil Dalal", "https://www.nemil.com"]
- ["Joseph Chow", ""]
2015-11-23 21:02:00 +00:00
---
2015-12-28 22:11:00 +00:00
Solidity lets you program on [Ethereum](https://www.ethereum.org/), a
blockchain-based virtual machine that allows the creation and
execution of smart contracts, without requiring centralized or trusted parties.
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
Solidity is a statically typed, contract programming language that has
similarities to Javascript and C. Like objects in OOP, each contract contains
state variables, functions, and common data types. Contract-specific features
include modifier (guard) clauses, event notifiers for listeners, and custom
global variables.
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
Some Ethereum contract examples include crowdfunding, voting, and blind auctions.
There is a high risk and high cost of errors in Solidity code, so you must be very careful to test
and slowly rollout. WITH THE RAPID CHANGES IN ETHEREUM, THIS DOCUMENT IS UNLIKELY TO STAY UP TO
DATE, SO YOU SHOULD FOLLOW THE SOLIDITY CHAT ROOM AND ETHEREUM BLOG FOR THE LATEST. ALL CODE HERE IS
PROVIDED AS IS, WITH SUBSTANTIAL RISK OF ERRORS OR DEPRECATED CODE PATTERNS.
Unlike other code, you may also need to add in design patterns like pausing, deprecation, and
throttling usage to reduce risk. This document primarily discusses syntax, and so excludes many
popular design patterns.
2015-12-28 22:11:00 +00:00
As Solidity and Ethereum are under active development, experimental or beta
features are typically marked, and subject to change. Pull requests welcome.
2015-11-23 21:02:00 +00:00
```javascript
2015-12-28 22:11:00 +00:00
// First, a simple Bank contract
// Allows deposits, withdrawals, and balance checks
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// simple_bank.sol (note .sol extension)
/* **** START EXAMPLE **** */
2017-07-25 10:32:29 +00:00
// Declare the source file compiler version.
pragma solidity ^0.4.2;
2015-12-28 22:11:00 +00:00
// Start with Natspec comment (the three slashes)
// used for documentation - and as descriptive data for UI elements/actions
/// @title SimpleBank
/// @author nemild
/* 'contract' has similarities to 'class' in other languages (class variables,
inheritance, etc.) */
contract SimpleBank { // CapWords
2015-12-28 22:11:00 +00:00
// Declare state variables outside function, persist through life of contract
// dictionary that maps addresses to balances
// always be careful about overflow attacks with numbers
2015-11-30 22:23:41 +00:00
mapping (address => uint) private balances;
2015-11-30 20:45:03 +00:00
2015-12-28 22:11:00 +00:00
// "private" means that other contracts can't directly query balances
// but data is still viewable to other parties on blockchain
2015-11-30 20:45:03 +00:00
address public owner;
2015-12-28 22:11:00 +00:00
// 'public' makes externally readable (not writeable) by users or contracts
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// Events - publicize actions to external listeners
event LogDepositMade(address accountAddress, uint amount);
2015-12-28 22:11:00 +00:00
// Constructor, can receive one or many variables here; only one allowed
function SimpleBank() {
2015-12-29 00:36:48 +00:00
// msg provides details about the message that's sent to the contract
2015-12-28 22:11:00 +00:00
// msg.sender is contract caller (address of contract creator)
owner = msg.sender;
2015-11-23 21:02:00 +00:00
}
2015-11-30 20:45:03 +00:00
2015-12-28 22:11:00 +00:00
/// @notice Deposit ether into bank
/// @return The balance of the user after the deposit is made
function deposit() public returns (uint) {
balances[msg.sender] += msg.value;
// no "this." or "self." required with state variable
2015-12-29 18:05:45 +00:00
// all values set to data type's initial value by default
2015-12-28 22:11:00 +00:00
LogDepositMade(msg.sender, msg.value); // fire event
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
return balances[msg.sender];
2015-11-23 21:02:00 +00:00
}
2015-12-28 22:11:00 +00:00
/// @notice Withdraw ether from bank
/// @dev This does not return any excess ether sent to it
/// @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
2015-12-28 22:11:00 +00:00
balances[msg.sender] -= withdrawAmount;
2015-11-23 21:07:17 +00:00
2015-12-29 00:36:48 +00:00
if (!msg.sender.send(withdrawAmount)) {
// increment back only on fail, as may be sending to contract that
// has overridden 'send' on the receipt end
2015-12-29 03:29:19 +00:00
balances[msg.sender] += withdrawAmount;
2015-12-29 00:36:48 +00:00
}
2015-11-23 21:07:17 +00:00
}
2015-12-29 02:58:31 +00:00
2015-12-29 00:36:48 +00:00
return balances[msg.sender];
2015-11-23 21:02:00 +00:00
}
2015-12-28 22:11:00 +00:00
/// @notice Get balance
/// @return The balance of the user
// 'constant' prevents function from editing state variables;
// allows function to run locally/off blockchain
2015-12-11 18:07:01 +00:00
function balance() constant returns (uint) {
2015-12-28 22:11:00 +00:00
return balances[msg.sender];
2015-11-23 21:02:00 +00:00
}
2015-12-28 22:11:00 +00:00
// 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
}
2015-11-23 21:02:00 +00:00
}
2015-11-30 20:45:03 +00:00
// ** END EXAMPLE **
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// Now, the basics of Solidity
2015-11-30 20:48:47 +00:00
// 1. DATA TYPES AND ASSOCIATED METHODS
2015-12-28 22:11:00 +00:00
// uint used for currency amount (there are no doubles
// or floats) and for dates (in unix time)
2015-11-30 20:45:03 +00:00
uint x;
// int of 256 bits, cannot be changed after instantiation
int constant a = 8;
2015-11-30 21:01:51 +00:00
int256 constant a = 8; // same effect as line above, here the 256 is explicit
2015-12-11 18:07:01 +00:00
uint constant VERSION_ID = 0x123A1; // A hex constant
2015-12-29 15:59:01 +00:00
// with 'constant', compiler replaces each occurrence with actual value
2015-11-30 20:45:03 +00:00
2015-12-28 22:11:00 +00:00
// For int and uint, can explicitly set space in steps of 8 up to 256
// e.g., int8, int16, int24
2015-11-23 21:02:00 +00:00
uint8 b;
int64 c;
uint248 e;
2015-12-29 15:59:01 +00:00
// Be careful that you don't overflow, and protect against attacks that do
2015-12-28 22:11:00 +00:00
// No random functions built in, use other contracts for randomness
2015-11-23 21:02:00 +00:00
// Type casting
2015-11-30 20:45:03 +00:00
int x = int(b);
2015-11-23 21:02:00 +00:00
bool b = true; // or do 'var b = true;' for inferred typing
2015-12-28 22:11:00 +00:00
// Addresses - holds 20 byte/160 bit Ethereum addresses
// No arithmetic allowed
address public owner;
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// Types of accounts:
// Contract account: address set on create (func of creator address, num transactions sent)
// External Account: (person/external entity): address created from public key
// Add 'public' field to indicate publicly/externally accessible
// a getter is automatically created, but NOT a setter
// All addresses can be sent ether
2015-11-23 21:02:00 +00:00
owner.send(SOME_BALANCE); // returns false on failure
if (owner.send) {} // REMEMBER: wrap 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
2015-12-28 22:11:00 +00:00
// can override send by defining your own
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// Can check balance
owner.balance; // the balance of the owner (user or contract)
// Bytes available from 1 to 32
2015-11-23 21:02:00 +00:00
byte a; // byte is same as bytes1
2015-12-28 22:11:00 +00:00
bytes2 b;
bytes32 c;
// Dynamically sized bytes
bytes m; // A special array, same as byte[] array (but packed tightly)
// More expensive than byte1-byte32, so use those when possible
2015-11-23 21:02:00 +00:00
// same as bytes, but does not allow length or index access (for now)
2015-12-28 22:11:00 +00:00
string n = "hello"; // stored in UTF8, note double quotes, not single
// string utility functions to be added in future
// prefer bytes32/bytes, as UTF8 uses more storage
2015-11-23 21:02:00 +00:00
2017-08-23 08:14:39 +00:00
// Type inference
2015-11-30 20:45:03 +00:00
// var does inferred typing based on first assignment,
2015-11-23 21:02:00 +00:00
// can't be used in functions parameters
var a = true;
2015-12-28 22:11:00 +00:00
// use carefully, inference may provide wrong type
// e.g., an int8, when a counter needs to be int16
// var can be used to assign function to variable
function a(uint x) returns (uint) {
return x * 2;
}
var f = a;
f(22); // call
2015-11-23 21:02:00 +00:00
// by default, all values are set to 0 on instantiation
2015-11-30 22:23:41 +00:00
// Delete can be called on most types
2015-12-28 22:11:00 +00:00
// (does NOT destroy value, but sets value to 0, the initial value)
2015-11-23 21:02:00 +00:00
uint x = 5;
2015-12-28 22:11:00 +00:00
// Destructuring/Tuples
(x, y) = (2, 7); // assign/swap multiple value
2015-11-23 21:02:00 +00:00
2015-11-30 20:48:47 +00:00
2015-11-23 21:02:00 +00:00
// 2. DATA STRUCTURES
// Arrays
2015-11-30 20:45:03 +00:00
bytes32[5] nicknames; // static array
bytes32[] names; // dynamic array
2015-11-23 21:02:00 +00:00
uint newLength = names.push("John"); // adding returns new length of the array
// Length
names.length; // get length
2015-12-28 22:11:00 +00:00
names.length = 1; // lengths can be set (for dynamic arrays in storage only)
// multidimensional array
uint x[][5]; // arr with 5 dynamic array elements (opp order of most languages)
2015-11-23 21:02:00 +00:00
// Dictionaries (any type to any other type)
2015-11-24 05:09:10 +00:00
mapping (string => uint) public balances;
2015-12-28 22:11:00 +00:00
balances["charles"] = 1;
console.log(balances["ada"]); // is 0, all non-set key values return zeroes
// 'public' allows following from another contract
contractName.balances("charles"); // returns 1
2015-12-28 22:11:00 +00:00
// 'public' created a getter (but not setter) like the following:
function balances(string _account) returns (uint balance) {
2015-12-28 22:11:00 +00:00
return balances[_account];
2015-11-23 21:02:00 +00:00
}
2015-12-28 22:11:00 +00:00
// Nested mappings
2015-12-29 02:53:37 +00:00
mapping (address => mapping (address => uint)) public custodians;
2015-12-28 22:11:00 +00:00
2015-11-23 21:02:00 +00:00
// To delete
2015-11-30 22:23:41 +00:00
delete balances["John"];
2015-12-28 22:11:00 +00:00
delete balances; // sets all elements to 0
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// Unlike other languages, CANNOT iterate through all elements in
// mapping, without knowing source keys - can build data structure
// on top to do this
2015-11-23 21:02:00 +00:00
2015-11-30 20:45:03 +00:00
// Structs and enums
2015-12-28 22:11:00 +00:00
struct Bank {
address owner;
uint balance;
}
2015-11-23 21:02:00 +00:00
Bank b = Bank({
2015-12-28 22:11:00 +00:00
owner: msg.sender,
balance: 5
2015-11-23 21:02:00 +00:00
});
2015-12-28 22:11:00 +00:00
// or
Bank c = Bank(msg.sender, 5);
c.amount = 5; // set to new value
delete b;
// sets to initial value, set all variables in struct to 0, except mappings
2015-11-23 21:02:00 +00:00
// Enums
2015-12-28 22:11:00 +00:00
enum State { Created, Locked, Inactive }; // often used for state machine
2015-11-23 21:02:00 +00:00
State public state; // Declare variable from enum
state = State.Created;
2015-11-30 20:45:03 +00:00
// enums can be explicitly converted to ints
2015-12-28 22:11:00 +00:00
uint createdState = uint(State.Created); // 0
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// Data locations: Memory vs. storage vs. stack - all complex types (arrays,
// structs) have a data location
2015-11-30 20:45:03 +00:00
// 'memory' does not persist, 'storage' does
2015-12-28 22:11:00 +00:00
// Default is 'storage' for local and state variables; 'memory' for func params
// stack holds small local variables
// for most types, can explicitly set which data location to use
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// 3. Simple operators
// Comparisons, bit operators and arithmetic operators are provided
// exponentiation: **
// exclusive or: ^
// bitwise negation: ~
2015-11-30 20:48:47 +00:00
2015-12-28 22:11:00 +00:00
// 4. Global Variables of note
2015-11-30 20:45:03 +00:00
// ** this **
2015-12-28 22:11:00 +00:00
this; // address of contract
// often used at end of contract life to send remaining balance to party
2015-11-30 20:45:03 +00:00
this.balance;
2015-12-28 22:11:00 +00:00
this.someFunction(); // calls func externally via call, not via internal jump
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// ** msg - Current message received by the contract ** **
msg.sender; // address of sender
2015-12-29 00:36:48 +00:00
msg.value; // amount of ether provided to this contract in wei
2015-11-30 20:45:03 +00:00
msg.data; // bytes, complete call data
msg.gas; // remaining gas
2015-11-23 21:02:00 +00:00
2015-11-30 20:45:03 +00:00
// ** tx - This transaction **
2015-12-28 22:11:00 +00:00
tx.origin; // address of sender of the transaction
tx.gasprice; // gas price of the transaction
// ** block - Information about current block **
2015-12-29 18:05:45 +00:00
now; // current time (approximately), alias for block.timestamp (uses Unix time)
2015-12-28 22:11:00 +00:00
block.number; // current block number
block.difficulty; // current block difficulty
block.blockhash(1); // returns bytes32, only works for most recent 256 blocks
2015-11-30 20:45:03 +00:00
block.gasLimit();
2015-12-28 22:11:00 +00:00
// ** storage - Persistent storage hash **
2015-11-30 20:45:03 +00:00
storage['abc'] = 'def'; // maps 256 bit words to 256 bit words
2015-11-23 21:02:00 +00:00
2015-11-30 20:48:47 +00:00
2015-11-23 21:02:00 +00:00
// 4. FUNCTIONS AND MORE
// A. Functions
// Simple function
function increment(uint x) returns (uint) {
2015-12-28 22:11:00 +00:00
x += 1;
return x;
2015-11-23 21:02:00 +00:00
}
2015-12-28 22:11:00 +00:00
// Functions can return many arguments, and by specifying returned arguments
// name don't need to explicitly return
2015-11-23 21:02:00 +00:00
function increment(uint x, uint y) returns (uint x, uint y) {
2015-12-28 22:11:00 +00:00
x += 1;
y += 1;
2015-11-23 21:02:00 +00:00
}
2015-12-28 22:11:00 +00:00
// Call previous functon
2015-11-23 21:02:00 +00:00
uint (a,b) = increment(1,1);
2015-12-28 22:11:00 +00:00
// 'constant' indicates that function does not/cannot change persistent vars
// Constant function execute locally, not on blockchain
2015-11-23 21:02:00 +00:00
uint y;
function increment(uint x) constant returns (uint x) {
2015-12-28 22:11:00 +00:00
x += 1;
y += 1; // this line would fail
// y is a state variable, and can't be changed in a constant function
2015-11-23 21:02:00 +00:00
}
2015-12-28 22:11:00 +00:00
// 'Function Visibility specifiers'
// These can be placed where 'constant' is, including:
// public - visible externally and internally (default)
// external
2015-11-23 21:02:00 +00:00
// private - only visible in the current contract
2015-12-28 22:11:00 +00:00
// internal - only visible in current contract, and those deriving from it
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// Functions hoisted - and can assign a function to a variable
2015-11-23 21:02:00 +00:00
function a() {
2015-12-28 22:11:00 +00:00
var z = b;
b();
2015-11-23 21:02:00 +00:00
}
function b() {
}
2015-12-28 22:11:00 +00:00
// Prefer loops to recursion (max call stack depth is 1024)
2015-11-23 21:02:00 +00:00
// B. Events
2015-12-28 22:11:00 +00:00
// Events are notify external parties; easy to search and
// access events from outside blockchain (with lightweight clients)
// typically declare after contract parameters
2015-11-23 21:02:00 +00:00
// Typically, capitalized - and add Log in front to be explicit and prevent confusion
// with a function call
2015-12-28 22:11:00 +00:00
// Declare
event LogSent(address indexed from, address indexed to, uint amount); // note capital first letter
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// Call
Sent(from, to, amount);
// For an external party (a contract or external entity), to watch:
2015-11-23 21:02:00 +00:00
Coin.Sent().watch({}, '', function(error, result) {
if (!error) {
console.log("Coin transfer: " + result.args.amount +
" coins were sent from " + result.args.from +
" to " + result.args.to + ".");
console.log("Balances now:\n" +
"Sender: " + Coin.balances.call(result.args.from) +
"Receiver: " + Coin.balances.call(result.args.to));
}
}
2015-12-28 22:11:00 +00:00
// Common paradigm for one contract to depend on another (e.g., a
// contract that depends on current exchange rate provided by another)
2015-11-23 21:02:00 +00:00
// C. Modifiers
2015-12-28 22:11:00 +00:00
// Modifiers validate inputs to functions such as minimal balance or user auth;
// similar to guard clause in other languages
2015-11-30 20:45:03 +00:00
2015-12-28 22:11:00 +00:00
// '_' (underscore) often included as last line in body, and indicates
2015-11-23 21:02:00 +00:00
// function being called should be placed there
2015-12-11 18:07:01 +00:00
modifier onlyAfter(uint _time) { if (now <= _time) throw; _ }
modifier onlyOwner { if (msg.sender == owner) _ }
2015-12-28 22:11:00 +00:00
// commonly used with state machines
modifier onlyIfState (State currState) { if (currState != State.A) _ }
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// Append right after function declaration
2015-11-30 20:45:03 +00:00
function changeOwner(newOwner)
2015-12-11 18:07:01 +00:00
onlyAfter(someTime)
onlyOwner()
2015-12-28 22:11:00 +00:00
onlyIfState(State.A)
2015-12-11 18:07:01 +00:00
{
2015-12-28 22:11:00 +00:00
owner = newOwner;
2015-11-23 21:02:00 +00:00
}
2015-12-28 22:11:00 +00:00
// 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;
}
2015-12-28 22:11:00 +00:00
}
}
2015-11-30 20:48:47 +00:00
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// 6. BRANCHING AND LOOPS
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// All basic logic blocks work - including if/else, for, while, break, continue
// return - but no switch
2015-11-30 20:45:03 +00:00
2015-12-28 22:11:00 +00:00
// Syntax same as javascript, but no type conversion from non-boolean
// to boolean (comparison operators must be used to get the boolean val)
2015-11-30 20:48:47 +00:00
// For loops that are determined by user behavior, be careful - as contracts have a maximal
// 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;
}
}
// Two errors above:
// 1. A failure on send 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
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// 7. OBJECTS/CONTRACTS
// A. Calling external contract
2015-11-23 21:02:00 +00:00
contract infoFeed {
2015-12-28 22:11:00 +00:00
function info() returns (uint ret) { return 42; }
2015-11-23 21:02:00 +00:00
}
contract Consumer {
2015-12-28 22:11:00 +00:00
InfoFeed feed; // points to contract on blockchain
// Set feed to existing contract instance
function setFeed(address addr) {
// automatically cast, be careful; constructor is not called
feed = InfoFeed(addr);
}
// Set feed to new instance of contract
function createNewFeed() {
2015-12-29 18:05:45 +00:00
feed = new InfoFeed(); // new instance created; constructor called
2015-12-28 22:11:00 +00:00
}
function callFeed() {
// final parentheses call contract, can optionally add
// custom ether value or gas
feed.info.value(10).gas(800)();
}
2015-11-23 21:02:00 +00:00
}
2015-11-30 20:45:03 +00:00
// B. Inheritance
2015-12-28 22:11:00 +00:00
// Order matters, last inherited contract (i.e., 'def') can override parts of
// previously inherited contracts
contract MyContract is abc, def("a custom argument to def") {
2015-11-30 20:45:03 +00:00
// Override function
2015-12-28 22:11:00 +00:00
function z() {
if (msg.sender == owner) {
def.z(); // call overridden function from def
2017-08-23 08:14:39 +00:00
super.z(); // call immediate parent overridden function
2015-12-28 22:11:00 +00:00
}
2015-11-30 20:45:03 +00:00
}
2015-12-28 22:11:00 +00:00
}
2015-11-30 20:45:03 +00:00
2015-12-28 22:11:00 +00:00
// abstract function
function someAbstractFunction(uint x);
// cannot be compiled, so used in base/abstract contracts
// that are then implemented
2015-11-30 20:45:03 +00:00
// C. Import
import "filename";
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol";
2015-12-28 22:11:00 +00:00
// Importing under active development
// Cannot currently be done at command line
2015-11-23 21:02:00 +00:00
2015-12-28 22:13:20 +00:00
2015-12-28 22:11:00 +00:00
// 8. OTHER KEYWORDS
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// A. Throwing
2015-11-23 21:02:00 +00:00
// Throwing
2015-12-28 22:11:00 +00:00
throw; // reverts unused money to sender, state is reverted
// Can't currently catch
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// Common design pattern is:
2015-11-23 21:02:00 +00:00
if (!addr.send(123)) {
2015-12-28 22:11:00 +00:00
throw;
2015-11-23 21:02:00 +00:00
}
2015-12-28 22:11:00 +00:00
// B. Selfdestruct
// selfdestruct current contract, sending funds to address (often creator)
selfdestruct(SOME_ADDRESS);
// removes storage/code from current/future blocks
// helps thin clients, but previous data persists in blockchain
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// Common pattern, lets owner end the contract and receive remaining funds
2015-11-30 20:45:03 +00:00
function remove() {
2015-12-28 22:11:00 +00:00
if(msg.sender == creator) { // Only let the contract creator do this
selfdestruct(creator); // Makes contract inactive, returns funds
}
2015-11-30 20:45:03 +00:00
}
2015-12-28 22:11:00 +00:00
// May want to deactivate contract manually, rather than selfdestruct
// (ether sent to selfdestructed contract is lost)
// 9. CONTRACT DESIGN NOTES
// A. Obfuscation
2015-12-29 03:31:48 +00:00
// All variables are publicly viewable on blockchain, so anything
2015-12-29 16:10:39 +00:00
// that is private needs to be obfuscated (e.g., hashed w/secret)
2015-12-28 22:11:00 +00:00
// Steps: 1. Commit to something, 2. Reveal commitment
sha3("some_bid_amount", "some secret"); // commit
// call contract's reveal function in the future
// showing bid plus secret that hashes to SHA3
2015-12-28 22:11:00 +00:00
reveal(100, "mySecret");
// B. Storage optimization
2015-12-29 16:10:39 +00:00
// Writing to blockchain can be expensive, as data stored forever; encourages
2015-12-28 22:11:00 +00:00
// smart ways to use memory (eventually, compilation will be better, but for now
// benefits to planning data structures - and storing min amount in blockchain)
// Cost can often be high for items like multidimensional arrays
// (cost is for storing data - not declaring unfilled variables)
2015-12-29 16:10:39 +00:00
// C. Data access in blockchain
// Cannot restrict human or computer from reading contents of
2015-12-28 22:11:00 +00:00
// transaction or transaction's state
2015-12-29 16:10:39 +00:00
// While 'private' prevents other *contracts* from reading data
// directly - any other party can still read data in blockchain
2015-11-23 21:02:00 +00:00
2015-12-29 16:10:39 +00:00
// All data to start of time is stored in blockchain, so
2015-12-28 22:11:00 +00:00
// anyone can observe all previous data and changes
2015-11-30 20:48:47 +00:00
2015-12-29 16:10:39 +00:00
// D. Cron Job
// Contracts must be manually called to handle time-based scheduling; can create external
// code to regularly ping, or provide incentives (ether) for others to
2015-11-30 20:45:03 +00:00
// E. Observer Pattern
// An Observer Pattern lets you register as a subscriber and
// register a function which is called by the oracle (note, the oracle pays
// for this action to be run)
// Some similarities to subscription in Pub/sub
// This is an abstract contract, both client and server classes import
// the client should implement
contract SomeOracleCallback {
function oracleCallback(int _value, uint _time, bytes32 info) external;
}
contract SomeOracle {
SomeOracleCallback[] callbacks; // array of all subscribers
// Register subscriber
function addSubscriber(SomeOracleCallback a) {
callbacks.push(a);
}
function notify(value, time, info) private {
for(uint i = 0;i < callbacks.length; i++) {
// all called subscribers must implement the oracleCallback
callbacks[i].oracleCallback(value, time, info);
}
}
function doSomething() public {
// Code to do something
// Notify all subscribers
notify(_value, _time, _info);
}
}
2016-01-14 19:01:45 +00:00
// Now, your client contract can addSubscriber by importing SomeOracleCallback
// and registering with Some Oracle
// F. State machines
2015-12-28 22:11:00 +00:00
// see example below for State enum and inState modifier
2015-12-28 22:13:20 +00:00
2015-12-28 22:11:00 +00:00
// *** EXAMPLE: A crowdfunding example (broadly similar to Kickstarter) ***
2015-11-30 20:45:03 +00:00
// ** START EXAMPLE **
2015-12-28 22:11:00 +00:00
// CrowdFunder.sol
/// @title CrowdFunder
/// @author nemild
contract CrowdFunder {
// Variables set on create by creator
address public creator;
address public fundRecipient; // creator may be different than recipient
uint public minimumToRaise; // required to tip, else everyone gets refund
string campaignUrl;
byte constant version = 1;
2015-12-28 22:11:00 +00:00
// Data structures
enum State {
Fundraising,
ExpiredRefund,
Successful
2015-12-28 22:11:00 +00:00
}
struct Contribution {
uint amount;
address contributor;
2015-11-30 21:51:53 +00:00
}
2015-11-23 21:02:00 +00:00
2015-12-28 22:11:00 +00:00
// State variables
State public state = State.Fundraising; // initialize on create
uint public totalRaised;
uint public raiseBy;
uint public completeAt;
2015-12-28 22:11:00 +00:00
Contribution[] contributions;
event LogFundingReceived(address addr, uint amount, uint currentTotal);
event LogWinnerPaid(address winnerAddress);
2015-12-28 22:11:00 +00:00
modifier inState(State _state) {
if (state != _state) throw;
_
}
2015-11-30 20:48:47 +00:00
2015-12-28 22:11:00 +00:00
modifier isCreator() {
if (msg.sender != creator) throw;
_
}
// Wait 6 months after final contract state before allowing contract destruction
2015-12-28 22:11:00 +00:00
modifier atEndOfLifecycle() {
if(!((state == State.ExpiredRefund || state == State.Successful) &&
completeAt + 6 months < now)) {
2015-12-28 22:11:00 +00:00
throw;
}
_
2015-12-28 22:11:00 +00:00
}
function CrowdFunder(
uint timeInHoursForFundraising,
string _campaignUrl,
address _fundRecipient,
uint _minimumToRaise)
{
creator = msg.sender;
fundRecipient = _fundRecipient;
campaignUrl = _campaignUrl;
minimumToRaise = _minimumToRaise;
raiseBy = now + (timeInHoursForFundraising * 1 hours);
}
function contribute()
public
inState(State.Fundraising)
{
contributions.push(
Contribution({
amount: msg.value,
contributor: msg.sender
}) // use array, so can iterate
);
totalRaised += msg.value;
LogFundingReceived(msg.sender, msg.value, totalRaised);
2015-12-28 22:11:00 +00:00
checkIfFundingCompleteOrExpired();
return contributions.length - 1; // return id
2015-12-28 22:11:00 +00:00
}
function checkIfFundingCompleteOrExpired() {
if (totalRaised > minimumToRaise) {
state = State.Successful;
payOut();
// could incentivize sender who initiated state change here
} else if ( now > raiseBy ) {
state = State.ExpiredRefund; // backers can now collect refunds by calling getRefund(id)
2015-12-28 22:11:00 +00:00
}
completeAt = now;
2015-12-28 22:11:00 +00:00
}
function payOut()
public
inState(State.Successful)
{
if(!fundRecipient.send(this.balance)) {
throw;
}
LogWinnerPaid(fundRecipient);
2015-12-28 22:11:00 +00:00
}
function getRefund(id)
2015-12-28 22:11:00 +00:00
public
inState(State.ExpiredRefund)
2015-12-28 22:11:00 +00:00
{
if (contributions.length <= id || id < 0 || contributions[id].amount == 0 ) {
throw;
}
uint amountToRefund = contributions[id].amount;
contributions[id].amount = 0;
if(!contributions[id].contributor.send(amountToSend)) {
contributions[id].amount = amountToSend;
return false;
2015-12-28 22:11:00 +00:00
}
return true;
2015-12-28 22:11:00 +00:00
}
function removeContract()
public
isCreator()
atEndOfLifecycle()
{
selfdestruct(msg.sender);
// creator gets all money that hasn't be claimed
2015-12-28 22:11:00 +00:00
}
function () { throw; }
}
// ** END EXAMPLE **
// 10. OTHER NATIVE FUNCTIONS
2015-11-23 21:02:00 +00:00
// Currency units
2015-12-28 22:11:00 +00:00
// Currency is defined using wei, smallest unit of Ether
2015-11-23 21:02:00 +00:00
uint minAmount = 1 wei;
2015-12-28 22:11:00 +00:00
uint a = 1 finney; // 1 ether == 1000 finney
// Other units, see: http://ether.fund/tool/converter
2015-11-23 21:02:00 +00:00
// Time units
1 == 1 second
1 minutes == 60 seconds
2015-12-28 22:11:00 +00:00
// Can multiply a variable times unit, as units are not stored in a variable
2015-11-23 21:02:00 +00:00
uint x = 5;
(x * 1 days); // 5 days
2015-12-28 22:11:00 +00:00
// Careful about leap seconds/years with equality statements for time
// (instead, prefer greater than/less than)
2015-11-23 21:02:00 +00:00
// Cryptography
2015-12-28 22:11:00 +00:00
// All strings passed are concatenated before hash action
2015-11-23 21:02:00 +00:00
sha3("ab", "cd");
ripemd160("abc");
sha256("def");
// 11. SECURITY
// Bugs can be disastrous in Ethereum contracts - and even popular patterns in Solidity,
// may be found to be antipatterns
2015-12-28 22:13:20 +00:00
// See security links at the end of this doc
// 12. LOW LEVEL FUNCTIONS
2015-12-28 22:11:00 +00:00
// call - low level, not often used, does not provide type safety
successBoolean = someContractAddress.call('function_name', 'arg1', 'arg2');
2015-11-30 20:48:47 +00:00
2015-12-28 22:11:00 +00:00
// callcode - Code at target address executed in *context* of calling contract
// provides library functionality
someContractAddress.callcode('function_name');
2015-11-23 21:02:00 +00:00
2015-12-28 22:13:20 +00:00
// 13. STYLE NOTES
2015-12-28 22:11:00 +00:00
// Based on Python's PEP8 style guide
2015-11-23 21:02:00 +00:00
2015-12-29 16:10:39 +00:00
// Quick summary:
2015-12-28 22:11:00 +00:00
// 4 spaces for indentation
// Two lines separate contract declarations (and other top level declarations)
// Avoid extraneous spaces in parentheses
// Can omit curly braces for one line statement (if, for, etc)
// else should be placed on own line
2015-11-23 21:02:00 +00:00
2015-12-28 22:13:20 +00:00
2017-08-23 08:14:39 +00:00
// 14. NATSPEC COMMENTS
2015-12-29 03:29:19 +00:00
// used for documentation, commenting, and external UIs
2015-12-28 22:11:00 +00:00
// Contract natspec - always above contract definition
/// @title Contract title
/// @author Author name
2015-11-24 05:09:10 +00:00
2015-12-28 22:11:00 +00:00
// Function natspec
/// @notice information about what function does; shown when function to execute
/// @dev Function documentation for developer
2015-11-30 20:45:03 +00:00
2015-12-28 22:11:00 +00:00
// Function parameter/return value natspec
/// @param someParam Some description of what the param does
/// @return Description of the return value
2015-11-23 21:02:00 +00:00
```
## Additional resources
2015-12-29 07:45:58 +00:00
- [Solidity Docs](https://solidity.readthedocs.org/en/latest/)
2015-11-23 21:02:00 +00:00
- [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/)
- [Gitter Solidity Chat room](https://gitter.im/ethereum/solidity)
2015-12-28 22:11:00 +00:00
- [Modular design strategies for Ethereum Contracts](https://docs.erisindustries.com/tutorials/solidity/)
2015-11-23 21:02:00 +00:00
2015-11-30 21:14:26 +00:00
## Sample contracts
2015-12-11 18:07:01 +00:00
- [Dapp Bin](https://github.com/ethereum/dapp-bin)
2015-11-30 21:14:26 +00:00
- [Solidity Baby Step Contracts](https://github.com/fivedogit/solidity-baby-steps/tree/master/contracts)
2015-12-29 07:45:58 +00:00
- [ConsenSys Contracts](https://github.com/ConsenSys/dapp-store-contracts)
2015-11-30 22:23:41 +00:00
- [State of Dapps](http://dapps.ethercasts.com/)
2015-11-30 21:14:26 +00:00
## Security
- [Thinking About Smart Contract Security](https://blog.ethereum.org/2016/06/19/thinking-smart-contract-security/)
- [Smart Contract Security](https://blog.ethereum.org/2016/06/10/smart-contract-security/)
- [Hacking Distributed Blog](http://hackingdistributed.com/)
2015-11-30 20:45:03 +00:00
## Information purposefully excluded
- Libraries
2015-12-28 22:11:00 +00:00
## Style
- Python's [PEP8](https://www.python.org/dev/peps/pep-0008/) is used as the baseline style guide, including its general philosophy
2015-12-28 22:11:00 +00:00
## Editors
- [Vim Solidity](https://github.com/tomlion/vim-solidity)
- Editor Snippets ([Ultisnips format](https://gist.github.com/nemild/98343ce6b16b747788bc))
2015-12-28 22:11:00 +00:00
## Future To Dos
- New keywords: protected, inheritable
- List of common design patterns (throttling, RNG, version upgrade)
- Common security anti patterns
2015-11-30 20:45:03 +00:00
Feel free to send a pull request with any edits - or email nemild -/at-/ gmail