contract Coin { // The keyword "public" makes those variables // readable from outside. address public minter; mapping (address => uint) public balances; // 可把mapping 當成是hash tables // 將address當參數 映射到balances中會得到uint型態的回傳值
// Events allow light clients to react on // changes efficiently. event Sent(address from, address to, uint amount);
msg.sender:呼叫合約的人,若contract A呼叫contract B,那msg.sender在contract A B都是相同的。
Ethereum Virtual Machine
The Ethereum Virtual Machine or EVM is the runtime environment for smart contracts in Ethereum.
Features:
Overview
The Ethereum Virtual Machine or EVM is the runtime environment for smart contracts in Ethereum
Accounts External accounts that are controlled by public-private key pairs (i.e. humans) Contract accounts which are controlled by the code stored together with the account.
----Every account has a persistent key-value store mapping 256-bit words to 256-bit words called storage.
Transactions
Gas
The gas price is a value set by the creator of the transaction
合約的手續費,用來執行合約的燃料,避免Contract有bug會把錢一直轉走
Storage, Memory and the Stack
Storage:
每個Contract都會持有自己的storage,storage為一個key-value store that maps 256-bit words to 256-bit words.
Memory:
of which a contract obtains a freshly cleared instance for each message call
/// @title Voting with delegation. contract Ballot { // This declares a new complex type which will // be used for variables later. // It will represent a single voter. struct Voter { uint weight; // weight is accumulated by delegation bool voted; // if true, that person already voted address delegate; // person delegated to uint vote; // index of the voted proposal }
// This is a type for a single proposal. struct Proposal { bytes32 name; // short name (up to 32 bytes) uint voteCount; // number of accumulated votes }
address public chairperson;
// This declares a state variable that // stores a `Voter` struct for each possible address. // 將每一個地址映射到對應的 Voter struct. mapping(address => Voter) public voters;
// A dynamically-sized array of `Proposal` structs. Proposal[] public proposals;
/// Create a new ballot to choose one of `proposalNames`. functionBallot(bytes32[] proposalNames) { chairperson = msg.sender; voters[chairperson].weight = 1;
// For each of the provided proposal names, // create a new proposal object and add it // to the end of the array. for (uint i = 0; i < proposalNames.length; i++) { // `Proposal({...})` creates a temporary // Proposal object and `proposals.push(...)` // appends it to the end of `proposals`. proposals.push(Proposal({ name: proposalNames[i], voteCount: 0 })); } }
// Give `voter` the right to vote on this ballot. // May only be called by `chairperson`. functiongiveRightToVote(address voter) { // If the argument of `require` evaluates to `false`, // it terminates and reverts all changes to // the state and to Ether balances. It is often // a good idea to use this if functions are // called incorrectly. But watch out, this // will currently also consume all provided gas // (this is planned to change in the future). require((msg.sender == chairperson) && !voters[voter].voted && (voters[voter].weight == 0)); voters[voter].weight = 1; }
/// Delegate your vote to the voter `to`. functiondelegate(address to) { // assigns reference Voter storage sender = voters[msg.sender]; require(!sender.voted);
// Self-delegation is not allowed. require(to != msg.sender);
// Forward the delegation as long as // `to` also delegated. // In general, such loops are very dangerous, // because if they run too long, they might // need more gas than is available in a block. // In this case, the delegation will not be executed, // but in other situations, such loops might // cause a contract to get "stuck" completely. while (voters[to].delegate != address(0)) { to = voters[to].delegate;
// We found a loop in the delegation, not allowed. require(to != msg.sender); }
// Since `sender` is a reference, this // modifies `voters[msg.sender].voted` sender.voted = true; sender.delegate = to; Voter delegate = voters[to]; if (delegate.voted) { // If the delegate already voted, // directly add to the number of votes proposals[delegate.vote].voteCount += sender.weight; } else { // If the delegate did not vote yet, // add to her weight. delegate.weight += sender.weight; } }
/// Give your vote (including votes delegated to you) /// to proposal `proposals[proposal].name`. functionvote(uint proposal) { Voter storage sender = voters[msg.sender]; require(!sender.voted); sender.voted = true; sender.vote = proposal;
// If `proposal` is out of the range of the array, // this will throw automatically and revert all // changes. proposals[proposal].voteCount += sender.weight; }
/// @dev Computes the winning proposal taking all /// previous votes into account. functionwinningProposal() constantreturns (uint winningProposal) { uint winningVoteCount = 0; for (uint p = 0; p < proposals.length; p++) { if (proposals[p].voteCount > winningVoteCount) { winningVoteCount = proposals[p].voteCount; winningProposal = p; } } }
// Calls winningProposal() function to get the index // of the winner contained in the proposals array and then // returns the name of the winner functionwinnerName() constant returns (bytes32 winnerName) { winnerName = proposals[winningProposal()].name; } }
contract SimpleAuction { // Parameters of the auction. Times are either // absolute unix timestamps (seconds since 1970-01-01) // or time periods in seconds. address public beneficiary; uint public auctionStart; uint public biddingTime;
// Current state of the auction. address public highestBidder; uint public highestBid;
// Allowed withdrawals of previous bids mapping(address => uint) pendingReturns;
// Set to true at the end, disallows any change bool ended;
// Events that will be fired on changes. event HighestBidIncreased(address bidder, uint amount); event AuctionEnded(address winner, uint amount);
// The following is a so-called natspec comment, // recognizable by the three slashes. // It will be shown when the user is asked to // confirm a transaction.
/// Create a simple auction with `_biddingTime` /// seconds bidding time on behalf of the /// beneficiary address `_beneficiary`. functionSimpleAuction( uint _biddingTime, address _beneficiary ) { beneficiary = _beneficiary; auctionStart = now; biddingTime = _biddingTime; }
/// Bid on the auction with the value sent /// together with this transaction. /// The value will only be refunded if the /// auction is not won. functionbid() payable{ // No arguments are necessary, all // information is already part of // the transaction. The keyword payable // is required for the function to // be able to receive Ether.
// Revert the call if the bidding // period is over. require(now <= (auctionStart + biddingTime));
// If the bid is not higher, send the // money back. require(msg.value > highestBid);
if (highestBidder != 0) { // Sending back the money by simply using // highestBidder.send(highestBid) is a security risk // because it could execute an untrusted contract. // It is always safer to let the recipients // withdraw their money themselves. pendingReturns[highestBidder] += highestBid; } highestBidder = msg.sender; highestBid = msg.value; HighestBidIncreased(msg.sender, msg.value); }
/// Withdraw a bid that was overbid. functionwithdraw() returns (bool) { uint amount = pendingReturns[msg.sender]; if (amount > 0) { // It is important to set this to zero because the recipient // can call this function again as part of the receiving call // before `send` returns. pendingReturns[msg.sender] = 0;
if (!msg.sender.send(amount)) { // No need to call throw here, just reset the amount owing pendingReturns[msg.sender] = amount; returnfalse; } } returntrue; }
/// End the auction and send the highest bid /// to the beneficiary. functionauctionEnd() { // It is a good guideline to structure functions that interact // with other contracts (i.e. they call functions or send Ether) // into three phases: // 1. checking conditions // 2. performing actions (potentially changing conditions) // 3. interacting with other contracts // If these phases are mixed up, the other contract could call // back into the current contract and modify the state or cause // effects (ether payout) to be performed multiple times. // If functions called internally include interaction with external // contracts, they also have to be considered interaction with // external contracts.
// 1. Conditions require(now >= (auctionStart + biddingTime)); // auction did not yet end require(!ended); // this function has already been called
/// Modifiers are a convenient way to validate inputs to /// functions. `onlyBefore` is applied to `bid` below: /// The new function body is the modifier's body where /// `_` is replaced by the old function body. modifier onlyBefore(uint _time) { require(now < _time); _; } modifier onlyAfter(uint _time) { require(now > _time); _; }
/// Place a blinded bid with `_blindedBid` = keccak256(value, /// fake, secret). /// The sent ether is only refunded if the bid is correctly /// revealed in the revealing phase. The bid is valid if the /// ether sent together with the bid is at least "value" and /// "fake" is not true. Setting "fake" to true and sending /// not the exact amount are ways to hide the real bid but /// still make the required deposit. The same address can /// place multiple bids. functionbid(bytes32 _blindedBid) payable onlyBefore(biddingEnd) { bids[msg.sender].push(Bid({ blindedBid: _blindedBid, deposit: msg.value })); }
/// Reveal your blinded bids. You will get a refund for all /// correctly blinded invalid bids and for all bids except for /// the totally highest. functionreveal( uint[] _values, bool[] _fake, bytes32[] _secret ) onlyAfter(biddingEnd) onlyBefore(revealEnd) { uint length = bids[msg.sender].length; require(_values.length == length); require(_fake.length == length); require(_secret.length == length);
uint refund; for (uint i = 0; i < length; i++) { var bid = bids[msg.sender][i]; var (value, fake, secret) = (_values[i], _fake[i], _secret[i]); if (bid.blindedBid != keccak256(value, fake, secret)) { // Bid was not actually revealed. // Do not refund deposit. continue; } refund += bid.deposit; if (!fake && bid.deposit >= value) { if (placeBid(msg.sender, value)) refund -= value; } // Make it impossible for the sender to re-claim // the same deposit. bid.blindedBid = 0; } msg.sender.transfer(refund); }
// This is an "internal" function which means that it // can only be called from the contract itself (or from // derived contracts). functionplaceBid(address bidder, uint value) internal returns (bool success) { if (value <= highestBid) { returnfalse; } if (highestBidder != 0) { // Refund the previously highest bidder. pendingReturns[highestBidder] += highestBid; } highestBid = value; highestBidder = bidder; returntrue; }
/// Withdraw a bid that was overbid. functionwithdraw() { uint amount = pendingReturns[msg.sender]; if (amount > 0) { // It is important to set this to zero because the recipient // can call this function again as part of the receiving call // before `send` returns (see the remark above about // conditions -> effects -> interaction). pendingReturns[msg.sender] = 0;
msg.sender.transfer(amount); } }
/// End the auction and send the highest bid /// to the beneficiary. functionauctionEnd() onlyAfter(revealEnd) { require(!ended); AuctionEnded(highestBidder, highestBid); ended = true; // We send all the money we have, because some // of the refunds might have failed. beneficiary.transfer(this.balance); } }
/// Abort the purchase and reclaim the ether. /// Can only be called by the seller before /// the contract is locked. functionabort() onlySeller inState(State.Created) { Aborted(); state = State.Inactive; seller.transfer(this.balance); }
/// Confirm the purchase as buyer. /// Transaction has to include `2 * value` ether. /// The ether will be locked until confirmReceived /// is called. functionconfirmPurchase() inState(State.Created) condition(msg.value == (2 * value)) payable { PurchaseConfirmed(); buyer = msg.sender; state = State.Locked; }
/// Confirm that you (the buyer) received the item. /// This will release the locked ether. functionconfirmReceived() onlyBuyer inState(State.Locked) { ItemReceived(); // It is important to change the state first because // otherwise, the contracts called using `send` below // can call in again here. state = State.Inactive;
// NOTE: This actually allows both the buyer and the seller to // block the refund - the withdraw pattern should be used.
// Since enum types are not part of the ABI, the signature of "getChoice" // will automatically be changed to "getChoice() returns (uint8)" // for all matters external to Solidity. The integer type used is just // large enough to hold all enum values, i.e. if you have more values, // `uint16` will be used and so on. functiongetChoice() returns (ActionChoices) { return choice; }
library ArrayUtils { // internal functions can be used in internal library functions because // they will be part of the same code context functionmap(uint[] memory self, function (uint) returns (uint) f) internal returns (uint[] memory r) { r = new uint[](self.length); for (uint i = 0; i < self.length; i++) { r[i] = f(self[i]); } } functionreduce( uint[] memory self, function (uint, uint) returns (uint) f ) internal returns (uint r) { r = self[0]; for (uint i = 1; i < self.length; i++) { r = f(r, self[i]); } } functionrange(uint length) internalreturns (uint[] memory r) { r = new uint[](length); for (uint i = 0; i < r.length; i++) { r[i] = i; } } }
contract Pyramid { using ArrayUtils for *; functionpyramid(uint l) returns (uint) { return ArrayUtils.range(l).map(square).reduce(sum); } functionsquare(uint x) internalreturns (uint) { return x * x; } functionsum(uint x, uint y) internalreturns (uint) { return x + y; } }
block.blockhash(uint blockNumber) returns (bytes32): hash of the given block - only works for 256 most recent blocks excluding current
block.coinbase (address): current block miner’s address
block.difficulty (uint): current block difficulty
block.gaslimit (uint): current block gaslimit
block.number (uint): current block number
block.timestamp (uint): current block timestamp as seconds since unix epoch
msg.data (bytes): complete calldata
msg.gas (uint): remaining gas
msg.sender (address): sender of the message (current call)
msg.sig (bytes4): first four bytes of the calldata (i.e. function identifier)
msg.value (uint): number of wei sent with the message
now (uint): current block timestamp (alias for block.timestamp)
tx.gasprice (uint): gas price of the transaction
tx.origin (address): sender of the transaction (full call chain)(不建議用)
Error Handling
assert(bool condition):
throws if the condition is not met - to be used for internal errors.
require(bool condition):
throws if the condition is not met - to be used for errors in inputs or external components.
revert():
abort execution and revert state changes
Mathematical and Cryptographic Functions
addmod(uint x, uint y, uint k) returns (uint):
compute (x + y) % k where the addition is performed with arbitrary precision and does not wrap around at 2**256.
mulmod(uint x, uint y, uint k) returns (uint):
compute (x * y) % k where the multiplication is performed with arbitrary precision and does not wrap around at 2**256.
keccak256(…) returns (bytes32):
compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments
sha256(…) returns (bytes32):
compute the SHA-256 hash of the (tightly packed) arguments
sha3(…) returns (bytes32):
alias to keccak256
ripemd160(…) returns (bytes20):
compute RIPEMD-160 hash of the (tightly packed) arguments
ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address):
recover the address associated with the public key from elliptic curve signature or return zero on error (example usage)
Solidity的表達和控制的結構(Expressions and Control Structures)
require can be used to easily check conditions on inputs.
assert can be used for internal error checking.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
pragma solidity ^0.4.0;
contract Sharer { functionsendHalf(address addr) payablereturns (uint balance) { require(msg.value % 2 == 0); // Only allow even numbers uint balanceBeforeTransfer = this.balance; addr.transfer(msg.value / 2); // Since transfer throws an exception on failure and // cannot call back here, there should be no way for us to // still have half of the money. assert(this.balance == balanceBeforeTransfer - msg.value / 2); returnthis.balance; } }
assert 發生的狀況:
If you access an array at a too large or negative index (i.e. x[i] where i >= x.length or i < 0).
If you access a fixed-length bytesN at a too large or negative index.
If you divide or modulo by zero (e.g. 5 / 0 or 23 % 0).
If you shift by a negative amount.
If you convert a value too big or negative into an enum type.
If you call a zero-initialized variable of internal function type.
If you call assert with an argument that evaluates to false.
require 發生的狀況:
Calling throw.
Calling require with an argument that evaluates to false.
If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation call, send, delegatecall or callcode is used. The low level operations never throw exceptions but indicate failures by returning false.
If you create a contract using the new keyword but the contract creation does not finish properly (see above for the definition of “not finish properly”).
If you perform an external function call targeting a contract that contains no code.
If your contract receives Ether via a public function without payable modifier (including the constructor and the fallback function).
If your contract receives Ether via a public getter function.
If a .transfer() fails.
Solidity Contract(即Class)
創建contract的時候,constructor只會被呼叫一次
合約的Visibility and Getters
Visibility:
external:
External functions are part of the contract interface, which means they can be called from other contracts and via transactions. An external function f cannot be called internally (i.e. f() does not work, but this.f() works). External functions are sometimes more efficient when they receive large arrays of data.
public:
Public functions are part of the contract interface and can be either called internally or via messages. For public state variables, an automatic getter function (see below) is generated.
internal:
Those functions and state variables can only be accessed internally (i.e. from within the current contract or contracts deriving from it), without using this.
private:
Private functions and state variables are only visible for the contract they are defined in and not in derived contracts.
Getter Functions
1 2 3 4 5 6 7 8 9
pragma solidity ^0.4.0;
contract C { uint public data; functionx() { data = 3; // internal access 當成state變數 uint val = this.data(); // external access 當成是function } }
// The following will not compile, but even // if someone sends ether to that contract, // the transaction will fail and reject the // Ether. //test.send(2 ether); } }
functiondeposit(bytes32 _id) payable{ // Any call to this function (even deeply nested) can // be detected from the JavaScript API by filtering // for `Deposit` to be called. Deposit(msg.sender, _id, msg.value); } }
var abi = /* abi as generated by the compiler */; var ClientReceipt = web3.eth.contract(abi); var clientReceipt = ClientReceipt.at(0x123/* address */);
var event = clientReceipt.Deposit();
// watch for changes event.watch(function(error, result){ // result will contain various information // including the argumets given to the Deposit // call. if (!error) console.log(result); });
// Or pass a callback to start watching immediately var event = clientReceipt.Deposit(function(error, result) { if (!error) console.log(result); });
library Set { // We define a new struct datatype that will be used to // hold its data in the calling contract. struct Data { mapping(uint => bool) flags; }
// Note that the first parameter is of type "storage // reference" and thus only its storage address and not // its contents is passed as part of the call. This is a // special feature of library functions. It is idiomatic // to call the first parameter 'self', if the function can // be seen as a method of that object. functioninsert(Data storage self, uint value) returns (bool) { if (self.flags[value]) returnfalse; // already there self.flags[value] = true; returntrue; }
functionremove(Data storage self, uint value) returns (bool) { if (!self.flags[value]) returnfalse; // not there self.flags[value] = false; returntrue; }
functionregister(uint value) { // The library functions can be called without a // specific instance of the library, since the // "instance" will be the current contract. require(Set.insert(knownValues, value)); } // In this contract, we can also directly access knownValues.flags, if we want. }