1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
|
---
language: Solidity
filename: learnSolidity.sol
contributors:
- ["Nemil Dalal", "https://www.nemil.com"]
- ["Joseph Chow", ""]
---
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 needing centralized or trusted parties.
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.
Some Ethereum contract examples include crowdfunding, voting, and blind auctions.
As Solidity and Ethereum are under active development, experimental or beta
features are explicitly marked, and subject to change. Pull requests welcome.
```javascript
// First, a simple Bank contract
// Allows deposits, withdrawals, and balance checks
// simple_bank.sol (note .sol extension)
/* **** START EXAMPLE **** */
// 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 { // CamelCase
// Declare state variables outside function, persist through life of contract
// dictionary that maps addresses to balances
mapping (address => uint) private balances;
// "private" means that other contracts can't directly query balances
// but data is still viewable to other parties on blockchain
address public owner;
// 'public' makes externally readable (not writeable) by users or contracts
// Events - publicize actions to external listeners
event DepositMade(address accountAddress, uint amount);
// Constructor, can receive one or many variables here; only one allowed
function AcmeBank() {
// msg provides details about the message that's sent to the contract
// msg.sender is contract caller (address of contract creator)
owner = msg.sender;
}
/// @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
// all values set to data type's initial value by default
DepositMade(msg.sender, msg.value); // fire event
return balances[msg.sender];
}
/// @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) {
balances[msg.sender] -= withdrawAmount;
if (!msg.sender.send(withdrawAmount)) {
// to be safe, may be sending to contract that
// has overridden 'send' which may then fail
balances[msg.sender] += withdrawAmount;
}
}
return balances[msg.sender];
}
/// @notice Get balance
/// @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) {
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 **
// Now, the basics of Solidity
// 1. DATA TYPES AND ASSOCIATED METHODS
// uint used for currency amount (there are no doubles
// or floats) and for dates (in unix time)
uint x;
// int of 256 bits, cannot be changed after instantiation
int constant a = 8;
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
// For int and uint, can explicitly set space in steps of 8 up to 256
// e.g., int8, int16, int24
uint8 b;
int64 c;
uint248 e;
// Be careful that you don't overflow, and protect against attacks that do
// No random functions built in, use other contracts for randomness
// Type casting
int x = int(b);
bool b = true; // or do 'var b = true;' for inferred typing
// Addresses - holds 20 byte/160 bit Ethereum addresses
// No arithmetic allowed
address public owner;
// 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
owner.send(SOME_BALANCE); // returns false on failure
if (owner.send) {} // typically wrap in 'if', as contract addresses have
// functions have executed on send and can fail
// can override send by defining your own
// Can check balance
owner.balance; // the balance of the owner (user or contract)
// Bytes available from 1 to 32
byte a; // byte is same as bytes1
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
// same as bytes, but does not allow length or index access (for now)
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
// Type inferrence
// var does inferred typing based on first assignment,
// can't be used in functions parameters
var a = true;
// 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
// by default, all values are set to 0 on instantiation
// Delete can be called on most types
// (does NOT destroy value, but sets value to 0, the initial value)
uint x = 5;
// Destructuring/Tuples
(x, y) = (2, 7); // assign/swap multiple value
// 2. DATA STRUCTURES
// Arrays
bytes32[5] nicknames; // static array
bytes32[] names; // dynamic array
uint newLength = names.push("John"); // adding returns new length of the array
// Length
names.length; // get length
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)
// Dictionaries (any type to any other type)
mapping (string => uint) public balances;
balances["charles"] = 1;
console.log(balances["ada"]); // is 0, all non-set key values return zeroes
// 'public' allows following from another contract
contractName.balances("claude"); // returns 1
// 'public' created a getter (but not setter) like the following:
function balances(address _account) returns (uint balance) {
return balances[_account];
}
// Nested mappings
mapping (address => mapping (address => uint)) public custodians;
// To delete
delete balances["John"];
delete balances; // sets all elements to 0
// Unlike other languages, CANNOT iterate through all elements in
// mapping, without knowing source keys - can build data structure
// on top to do this
// Structs and enums
struct Bank {
address owner;
uint balance;
}
Bank b = Bank({
owner: msg.sender,
balance: 5
});
// 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
// Enums
enum State { Created, Locked, Inactive }; // often used for state machine
State public state; // Declare variable from enum
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,
// structs) have a data location
// 'memory' does not persist, 'storage' does
// 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
// 3. Simple operators
// Comparisons, bit operators and arithmetic operators are provided
// exponentiation: **
// exclusive or: ^
// bitwise negation: ~
// 4. Global Variables of note
// ** this **
this; // address of contract
// often used at end of contract life to send 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.data; // bytes, complete call data
msg.gas; // remaining gas
// ** tx - This transaction **
tx.origin; // address of sender of the transaction
tx.gasprice; // gas price of the transaction
// ** block - Information about current block **
now; // current time (approximately), alias for block.timestamp (uses Unix time)
block.number; // current block number
block.difficulty; // current block difficulty
block.blockhash(1); // returns bytes32, only works for most recent 256 blocks
block.gasLimit();
// ** storage - Persistent storage hash **
storage['abc'] = 'def'; // maps 256 bit words to 256 bit words
// 4. FUNCTIONS AND MORE
// A. Functions
// Simple function
function increment(uint x) returns (uint) {
x += 1;
return x;
}
// Functions can return many arguments, and by specifying returned arguments
// name don't need to explicitly return
function increment(uint x, uint y) returns (uint x, uint y) {
x += 1;
y += 1;
}
// Call previous functon
uint (a,b) = increment(1,1);
// 'constant' indicates that function does not/cannot change persistent vars
// Constant function execute locally, not on blockchain
uint y;
function increment(uint x) constant returns (uint x) {
x += 1;
y += 1; // this line would fail
// y is a state variable, and can't be changed in a constant function
}
// 'Function Visibility specifiers'
// These can be placed where 'constant' is, including:
// public - visible externally and internally (default)
// external
// private - only visible in the current contract
// internal - only visible in current contract, and those deriving from it
// Functions hoisted - and can assign a function to a variable
function a() {
var z = b;
b();
}
function b() {
}
// Prefer loops to recursion (max call stack depth is 1024)
// B. Events
// Events are notify external parties; easy to search and
// access events from outside blockchain (with lightweight clients)
// typically declare after contract parameters
// Declare
event Sent(address from, address to, uint amount); // note capital first letter
// Call
Sent(from, to, amount);
// For an external party (a contract or external entity), to watch:
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));
}
}
// Common paradigm for one contract to depend on another (e.g., a
// contract that depends on current exchange rate provided by another)
// C. Modifiers
// Modifiers validate inputs to functions such as minimal balance or user auth;
// similar to guard clause in other languages
// '_' (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) _ }
// commonly used with state machines
modifier onlyIfState (State currState) { if (currState != State.A) _ }
// Append right after function declaration
function changeOwner(newOwner)
onlyAfter(someTime)
onlyOwner()
onlyIfState(State.A)
{
owner = newOwner;
}
// underscore can be included before end of body,
// but explicitly returning will skip, so use carefully
modifier checkValue(uint amount) {
_
if (msg.value > amount) {
msg.sender.send(amount - msg.value);
}
}
// 6. BRANCHING AND LOOPS
// All basic logic blocks work - including if/else, for, while, break, continue
// return - but no switch
// Syntax same as javascript, but no type conversion from non-boolean
// to boolean (comparison operators must be used to get the boolean val)
// 7. OBJECTS/CONTRACTS
// A. Calling external contract
contract infoFeed {
function info() returns (uint ret) { return 42; }
}
contract Consumer {
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() {
feed = new InfoFeed(); // new instance created; constructor called
}
function callFeed() {
// final parentheses call contract, can optionally add
// custom ether value or gas
feed.info.value(10).gas(800)();
}
}
// B. Inheritance
// 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") {
// Override function
function z() {
if (msg.sender == owner) {
def.z(); // call overridden function from def
super.z(); // call immediate parent overriden function
}
}
}
// abstract function
function someAbstractFunction(uint x);
// cannot be compiled, so used in base/abstract contracts
// that are then implemented
// C. Import
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
// 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
// Common pattern, lets owner end the contract and receive remaining funds
function remove() {
if(msg.sender == creator) { // Only let the contract creator do this
selfdestruct(creator); // Makes contract inactive, returns funds
}
}
// May want to deactivate contract manually, rather than selfdestruct
// (ether sent to selfdestructed contract is lost)
// 9. CONTRACT DESIGN NOTES
// A. Obfuscation
// All variables are publicly viewable on blockchain, so anything
// 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
// call contract's reveal function in the future
// showing bid plus secret that hashes to SHA3
reveal(100, "mySecret");
// B. Storage optimization
// Writing to blockchain can be expensive, as data stored forever; encourages
// 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)
// C. Data access in blockchain
// Cannot restrict human or computer from reading contents of
// transaction or transaction's state
// While 'private' prevents other *contracts* from reading data
// directly - any other party can still read data in blockchain
// All data to start of time is stored in blockchain, so
// anyone can observe all previous data and changes
// 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
// 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);
}
}
// Now, your client contract can addSubscriber by importing SampleOracleCallback
// and registering with Sample Oracle
// F. State machines
// see example below for State enum and inState modifier
// *** EXAMPLE: A crowdfunding example (broadly similar to Kickstarter) ***
// ** START EXAMPLE **
// 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;
// Data structures
enum State {
Fundraising,
ExpiredRefundPending,
Successful,
ExpiredRefundComplete
}
struct Contribution {
uint amount;
address contributor;
}
// State variables
State public state = State.Fundraising; // initialize on create
uint public totalRaised;
uint public raiseBy;
Contribution[] contributions;
event fundingReceived(address addr, uint amount, uint currentTotal);
event allRefundsSent();
event winnerPaid(address winnerAddress);
modifier inState(State _state) {
if (state != _state) throw;
_
}
modifier isCreator() {
if (msg.sender != creator) throw;
_
}
modifier atEndOfLifecycle() {
if(state != State.ExpiredRefundComplete && state != State.Successful) {
throw;
}
}
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;
fundingReceived(msg.sender, msg.value, totalRaised);
checkIfFundingCompleteOrExpired();
}
function checkIfFundingCompleteOrExpired() {
if (totalRaised > minimumToRaise) {
state = State.Successful;
payOut();
// could incentivize sender who initiated state change here
} else if ( now > raiseBy ) {
state = State.ExpiredRefundPending;
refundAll();
}
}
function payOut()
public
inState(State.Successful)
{
if(!fundRecipient.send(this.balance)) {
throw;
}
winnerPaid(fundRecipient);
}
function refundAll()
public
inState(State.ExpiredRefundPending)
{
uint length = contributions.length;
for (uint i = 0; i < length; i++) {
if(!contributions[i].contributor.send(contributions[i].amount)) {
throw;
}
}
allRefundsSent();
state = State.ExpiredRefundComplete;
}
function removeContract()
public
isCreator()
atEndOfLifecycle()
{
selfdestruct(msg.sender);
}
function () { throw; }
}
// ** END EXAMPLE **
// 10. OTHER NATIVE FUNCTIONS
// Currency units
// Currency is defined using wei, smallest unit of Ether
uint minAmount = 1 wei;
uint a = 1 finney; // 1 ether == 1000 finney
// Other units, see: http://ether.fund/tool/converter
// Time units
1 == 1 second
1 minutes == 60 seconds
// Can multiply a variable times unit, as units are not stored in a variable
uint x = 5;
(x * 1 days); // 5 days
// Careful about leap seconds/years with equality statements for time
// (instead, prefer greater than/less than)
// Cryptography
// All strings passed are concatenated before hash action
sha3("ab", "cd");
ripemd160("abc");
sha256("def");
// 11. LOW LEVEL FUNCTIONS
// call - low level, not often used, does not provide type safety
successBoolean = someContractAddress.call('function_name', 'arg1', 'arg2');
// callcode - Code at target address executed in *context* of calling contract
// provides library functionality
someContractAddress.callcode('function_name');
// 12. STYLE NOTES
// Based on Python's PEP8 style guide
// Quick summary:
// 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
// 13. NATSPEC COMENTS
// used for documentation, commenting, and external UIs
// Contract natspec - always above contract definition
/// @title Contract title
/// @author Author name
// Function natspec
/// @notice information about what function does; shown when function to execute
/// @dev Function documentation for developer
// Function parameter/return value natspec
/// @param someParam Some description of what the param does
/// @return Description of the return value
```
## Additional resources
- [Solidity Docs](https://solidity.readthedocs.org/en/latest/)
- [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 Chat room](https://gitter.im/ethereum/solidity)
- [Modular design strategies for Ethereum Contracts](https://docs.erisindustries.com/tutorials/solidity/)
- Editor Snippets ([Ultisnips format](https://gist.github.com/nemild/98343ce6b16b747788bc))
## Sample contracts
- [Dapp Bin](https://github.com/ethereum/dapp-bin)
- [Solidity Baby Step Contracts](https://github.com/fivedogit/solidity-baby-steps/tree/master/contracts)
- [ConsenSys Contracts](https://github.com/ConsenSys/dapp-store-contracts)
- [State of Dapps](http://dapps.ethercasts.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
## Future To Dos
- New keywords: protected, inheritable
Feel free to send a pull request with any edits - or email nemild -/at-/ gmail
|