1
0
mirror of https://github.com/adambard/learnxinyminutes-docs.git synced 2025-08-09 08:16:58 +02:00

Merge pull request #3053 from nemild/update-to-latest-solidity

[solidity/en] Initial update to latest solidity
This commit is contained in:
Adam Bard
2018-02-19 23:58:47 -08:00
committed by GitHub

View File

@@ -37,8 +37,8 @@ features are typically marked, and subject to change. Pull requests welcome.
// simple_bank.sol (note .sol extension) // simple_bank.sol (note .sol extension)
/* **** START EXAMPLE **** */ /* **** START EXAMPLE **** */
// Declare the source file compiler version. // Declare the source file compiler version
pragma solidity ^0.4.2; pragma solidity ^0.4.19;
// Start with Natspec comment (the three slashes) // Start with Natspec comment (the three slashes)
// used for documentation - and as descriptive data for UI elements/actions // used for documentation - and as descriptive data for UI elements/actions
@@ -65,7 +65,7 @@ contract SimpleBank { // CapWords
event LogDepositMade(address accountAddress, uint amount); event LogDepositMade(address accountAddress, uint amount);
// Constructor, can receive one or many variables here; only one allowed // 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 provides details about the message that's sent to the contract
// msg.sender is contract caller (address of contract creator) // msg.sender is contract caller (address of contract creator)
owner = msg.sender; owner = msg.sender;
@@ -73,7 +73,11 @@ contract SimpleBank { // CapWords
/// @notice Deposit ether into bank /// @notice Deposit ether into bank
/// @return The balance of the user after the deposit is made /// @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; balances[msg.sender] += msg.value;
// no "this." or "self." required with state variable // no "this." or "self." required with state variable
// all values set to data type's initial value by default // 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 /// @param withdrawAmount amount you want to withdraw
/// @return The balance remaining for the user /// @return The balance remaining for the user
function withdraw(uint withdrawAmount) public returns (uint remainingBal) { function withdraw(uint withdrawAmount) public returns (uint remainingBal) {
if(balances[msg.sender] >= withdrawAmount) { require(withdrawAmount <= balances[msg.sender]);
// 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;
if (!msg.sender.send(withdrawAmount)) { // Note the way we deduct the balance right away, before sending
// increment back only on fail, as may be sending to contract that // Every .transfer/.send from this contract can call an external function
// has overridden 'send' on the receipt end // This may allow the caller to request an amount greater
balances[msg.sender] += withdrawAmount; // 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]; return balances[msg.sender];
} }
@@ -108,18 +111,9 @@ contract SimpleBank { // CapWords
/// @return The balance of the user /// @return The balance of the user
// 'constant' prevents function from editing state variables; // 'constant' prevents function from editing state variables;
// allows function to run locally/off blockchain // allows function to run locally/off blockchain
function balance() constant returns (uint) { function balance() constant public returns (uint) {
return balances[msg.sender]; 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 ** // ** 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 uint constant VERSION_ID = 0x123A1; // A hex constant
// with 'constant', compiler replaces each occurrence with actual value // 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 // For int and uint, can explicitly set space in steps of 8 up to 256
// e.g., int8, int16, int24 // e.g., int8, int16, int24
@@ -145,6 +144,12 @@ int64 c;
uint248 e; uint248 e;
// Be careful that you don't overflow, and protect against attacks that do // 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 // 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 // a getter is automatically created, but NOT a setter
// All addresses can be sent ether // All addresses can be sent ether
owner.send(SOME_BALANCE); // returns false on failure owner.transfer(SOME_BALANCE); // fails and reverts on failure
if (owner.send) {} // REMEMBER: wrap in 'if', as contract addresses have
// 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 // 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 // Also, make sure to deduct balances BEFORE attempting a send, as there is a risk of a recursive
// call that can drain the contract // call that can drain the contract
// can override send by defining your own
// Can check balance // Can check balance
owner.balance; // the balance of the owner (user or contract) owner.balance; // the balance of the owner (user or contract)
@@ -213,7 +218,7 @@ uint x = 5;
// Destructuring/Tuples // Destructuring/Tuples
(x, y) = (2, 7); // assign/swap multiple value (x, y) = (2, 7); // assign/swap multiple values
// 2. DATA STRUCTURES // 2. DATA STRUCTURES
@@ -250,7 +255,7 @@ delete balances; // sets all elements to 0
// mapping, without knowing source keys - can build data structure // mapping, without knowing source keys - can build data structure
// on top to do this // on top to do this
// Structs and enums // Structs
struct Bank { struct Bank {
address owner; address owner;
uint balance; uint balance;
@@ -273,7 +278,7 @@ state = State.Created;
// enums can be explicitly converted to ints // enums can be explicitly converted to ints
uint createdState = uint(State.Created); // 0 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 // structs) have a data location
// 'memory' does not persist, 'storage' does // 'memory' does not persist, 'storage' does
// Default is 'storage' for local and state variables; 'memory' for func params // 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 // 4. Global Variables of note
// ** this ** // ** this **
this; // address of contract 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.balance;
this.someFunction(); // calls func externally via call, not via internal jump this.someFunction(); // calls func externally via call, not via internal jump
// ** msg - Current message received by the contract ** ** // ** msg - Current message received by the contract ** **
msg.sender; // address of sender 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.data; // bytes, complete call data
msg.gas; // remaining gas msg.gas; // remaining gas
@@ -308,6 +313,8 @@ tx.gasprice; // gas price of the transaction
// ** block - Information about current block ** // ** block - Information about current block **
now; // current time (approximately), alias for block.timestamp (uses Unix time) 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.number; // current block number
block.difficulty; // current block difficulty block.difficulty; // current block difficulty
block.blockhash(1); // returns bytes32, only works for most recent 256 blocks 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 // Call previous functon
uint (a,b) = increment(1,1); 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 // Constant function execute locally, not on blockchain
uint y; uint y = 1;
function increment(uint x) constant returns (uint x) { function increment(uint x) constant returns (uint x) {
x += 1; 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 // 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' // 'Function Visibility specifiers'
// These can be placed where 'constant' is, including: // These can be placed where 'constant' is, including:
// public - visible externally and internally (default) // public - visible externally and internally (default for function)
// external // external - only visible externally (including a call made with this.)
// private - only visible in the current contract // private - only visible in the current contract
// internal - only visible in current contract, and those deriving from it // 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 // Functions hoisted - and can assign a function to a variable
function a() { function a() {
var z = b; 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) // 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 // B. Events
// Events are notify external parties; easy to search and // 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 // Call
Sent(from, to, amount); 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) { Coin.Sent().watch({}, '', function(error, result) {
if (!error) { if (!error) {
console.log("Coin transfer: " + result.args.amount + 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 // '_' (underscore) often included as last line in body, and indicates
// function being called should be placed there // function being called should be placed there
modifier onlyAfter(uint _time) { if (now <= _time) throw; _ } modifier onlyAfter(uint _time) { require (now >= _time); _; }
modifier onlyOwner { if (msg.sender == owner) _ } modifier onlyOwner { require(msg.sender == owner) _; }
// commonly used with state machines // 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 // Append right after function declaration
function changeOwner(newOwner) function changeOwner(newOwner)
@@ -415,12 +439,10 @@ onlyIfState(State.A)
// underscore can be included before end of body, // underscore can be included before end of body,
// but explicitly returning will skip, so use carefully // but explicitly returning will skip, so use carefully
modifier checkValue(uint amount) { modifier checkValue(uint amount) {
_ _;
if (msg.value > amount) { if (msg.value > amount) {
uint amountToRefund = amount - msg.value; uint amountToRefund = amount - msg.value;
if (!msg.sender.send(amountToRefund)) { msg.sender.transfer(amountToRefund);
throw;
}
} }
} }
@@ -437,22 +459,21 @@ modifier checkValue(uint amount) {
// amount of gas for a block of code - and will fail if that is exceeded // amount of gas for a block of code - and will fail if that is exceeded
// For example: // For example:
for(uint x = 0; x < refundAddressList.length; x++) { for(uint x = 0; x < refundAddressList.length; x++) {
if (!refundAddressList[x].send(SOME_AMOUNT)) { refundAddressList[x].transfer(SOME_AMOUNT);
throw;
}
} }
// Two errors above: // 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 // 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 // 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 // Instead, you should let people withdraw individually from their subaccount, and mark withdrawn
// e.g., favor pull payments over push payments
// 7. OBJECTS/CONTRACTS // 7. OBJECTS/CONTRACTS
// A. Calling external contract // A. Calling external contract
contract infoFeed { contract InfoFeed {
function info() returns (uint ret) { return 42; } function info() returns (uint ret) { return 42; }
} }
@@ -502,23 +523,10 @@ function someAbstractFunction(uint x);
import "filename"; import "filename";
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol"; import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol";
// Importing under active development
// Cannot currently be done at command line
// 8. OTHER KEYWORDS // 8. OTHER KEYWORDS
// A. Throwing // A. Selfdestruct
// 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 current contract, sending funds to address (often creator)
selfdestruct(SOME_ADDRESS); selfdestruct(SOME_ADDRESS);
@@ -543,7 +551,7 @@ function remove() {
// that is private needs to be obfuscated (e.g., hashed w/secret) // that is private needs to be obfuscated (e.g., hashed w/secret)
// Steps: 1. Commit to something, 2. Reveal commitment // 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 // call contract's reveal function in the future
// showing bid plus secret that hashes to SHA3 // showing bid plus secret that hashes to SHA3
@@ -617,6 +625,7 @@ contract SomeOracle {
// ** START EXAMPLE ** // ** START EXAMPLE **
// CrowdFunder.sol // CrowdFunder.sol
pragma solidity ^0.4.19;
/// @title CrowdFunder /// @title CrowdFunder
/// @author nemild /// @author nemild
@@ -650,22 +659,20 @@ contract CrowdFunder {
event LogWinnerPaid(address winnerAddress); event LogWinnerPaid(address winnerAddress);
modifier inState(State _state) { modifier inState(State _state) {
if (state != _state) throw; require(state == _state);
_ _;
} }
modifier isCreator() { 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() { modifier atEndOfLifecycle() {
if(!((state == State.ExpiredRefund || state == State.Successful) && require(((state == State.ExpiredRefund || state == State.Successful) &&
completeAt + 6 months < now)) { completeAt + 24 weeks < now));
throw; _;
}
_
} }
function CrowdFunder( function CrowdFunder(
@@ -673,6 +680,7 @@ contract CrowdFunder {
string _campaignUrl, string _campaignUrl,
address _fundRecipient, address _fundRecipient,
uint _minimumToRaise) uint _minimumToRaise)
public
{ {
creator = msg.sender; creator = msg.sender;
fundRecipient = _fundRecipient; fundRecipient = _fundRecipient;
@@ -683,7 +691,9 @@ contract CrowdFunder {
function contribute() function contribute()
public public
payable
inState(State.Fundraising) inState(State.Fundraising)
returns(uint256 id)
{ {
contributions.push( contributions.push(
Contribution({ Contribution({
@@ -699,7 +709,9 @@ contract CrowdFunder {
return contributions.length - 1; // return id return contributions.length - 1; // return id
} }
function checkIfFundingCompleteOrExpired() { function checkIfFundingCompleteOrExpired()
public
{
if (totalRaised > minimumToRaise) { if (totalRaised > minimumToRaise) {
state = State.Successful; state = State.Successful;
payOut(); payOut();
@@ -715,31 +727,23 @@ contract CrowdFunder {
public public
inState(State.Successful) inState(State.Successful)
{ {
if(!fundRecipient.send(this.balance)) { fundRecipient.transfer(this.balance);
throw;
}
LogWinnerPaid(fundRecipient); LogWinnerPaid(fundRecipient);
} }
function getRefund(id) function getRefund(uint256 id)
public
inState(State.ExpiredRefund) inState(State.ExpiredRefund)
public
returns(bool)
{ {
if (contributions.length <= id || id < 0 || contributions[id].amount == 0 ) { require(contributions.length > id && id >= 0 && contributions[id].amount != 0 );
throw;
}
uint amountToRefund = contributions[id].amount; uint256 amountToRefund = contributions[id].amount;
contributions[id].amount = 0; contributions[id].amount = 0;
if(!contributions[id].contributor.send(amountToSend)) { contributions[id].contributor.transfer(amountToRefund);
contributions[id].amount = amountToSend;
return false;
}
return true; return true;
} }
function removeContract() function removeContract()
@@ -750,8 +754,6 @@ contract CrowdFunder {
selfdestruct(msg.sender); selfdestruct(msg.sender);
// creator gets all money that hasn't be claimed // creator gets all money that hasn't be claimed
} }
function () { throw; }
} }
// ** END EXAMPLE ** // ** END EXAMPLE **
@@ -798,6 +800,7 @@ someContractAddress.callcode('function_name');
// 13. STYLE NOTES // 13. STYLE NOTES
// Based on Python's PEP8 style guide // Based on Python's PEP8 style guide
// Full Style guide: http://solidity.readthedocs.io/en/develop/style-guide.html
// Quick summary: // Quick summary:
// 4 spaces for indentation // 4 spaces for indentation
@@ -825,12 +828,16 @@ someContractAddress.callcode('function_name');
## Additional resources ## Additional resources
- [Solidity Docs](https://solidity.readthedocs.org/en/latest/) - [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. - [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.
- [EthFiddle - The JsFiddle for Solidity](https://ethfiddle.com/) - [EthFiddle - The JsFiddle for Solidity](https://ethfiddle.com/)
- [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) - [Gitter Solidity Chat room](https://gitter.im/ethereum/solidity)
- [Modular design strategies for Ethereum Contracts](https://docs.erisindustries.com/tutorials/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 ## Sample contracts
- [Dapp Bin](https://github.com/ethereum/dapp-bin) - [Dapp Bin](https://github.com/ethereum/dapp-bin)
- [Solidity Baby Step Contracts](https://github.com/fivedogit/solidity-baby-steps/tree/master/contracts) - [Solidity Baby Step Contracts](https://github.com/fivedogit/solidity-baby-steps/tree/master/contracts)
@@ -842,9 +849,6 @@ someContractAddress.callcode('function_name');
- [Smart Contract Security](https://blog.ethereum.org/2016/06/10/smart-contract-security/) - [Smart Contract Security](https://blog.ethereum.org/2016/06/10/smart-contract-security/)
- [Hacking Distributed Blog](http://hackingdistributed.com/) - [Hacking Distributed Blog](http://hackingdistributed.com/)
## Information purposefully excluded
- Libraries
## Style ## Style
- Python's [PEP8](https://www.python.org/dev/peps/pep-0008/) is used as the baseline style guide, including its general philosophy - Python's [PEP8](https://www.python.org/dev/peps/pep-0008/) is used as the baseline style guide, including its general philosophy