Last active
June 22, 2022 09:44
-
-
Save erbrito/e301e8b8ca18f264bef714c50a167d12 to your computer and use it in GitHub Desktop.
A template solidity contract that uses a state machine based on common patterns described at https://solidity.readthedocs.io/en/develop/common-patterns.html#state-machine. Other use cases with best practices.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.4.16; | |
contract owned { | |
address public owner; | |
function owned() public { | |
owner = msg.sender; | |
} | |
modifier onlyOwner { | |
require(msg.sender == owner); | |
_; | |
} | |
function transferOwnership(address newOwner) onlyOwner public { | |
owner = newOwner; | |
} | |
} | |
contract tokenRecipient { | |
event receivedEther(address sender, uint amount); | |
event receivedTokens(address _from, uint256 _value, address _token, bytes _extraData); | |
function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public { | |
Token t = Token(_token); | |
require(t.transferFrom(_from, this, _value)); | |
receivedTokens(_from, _value, _token, _extraData); | |
} | |
function () payable public { | |
receivedEther(msg.sender, msg.value); | |
} | |
} | |
interface Token { | |
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); | |
} | |
contract Congress is owned, tokenRecipient { | |
// Contract Variables and events | |
uint public minimumQuorum; | |
uint public debatingPeriodInMinutes; | |
int public majorityMargin; | |
Proposal[] public proposals; | |
uint public numProposals; | |
mapping (address => uint) public memberId; | |
Member[] public members; | |
event ProposalAdded(uint proposalID, address recipient, uint amount, string description); | |
event Voted(uint proposalID, bool position, address voter, string justification); | |
event ProposalTallied(uint proposalID, int result, uint quorum, bool active); | |
event MembershipChanged(address member, bool isMember); | |
event ChangeOfRules(uint newMinimumQuorum, uint newDebatingPeriodInMinutes, int newMajorityMargin); | |
struct Proposal { | |
address recipient; | |
uint amount; | |
string description; | |
uint votingDeadline; | |
bool executed; | |
bool proposalPassed; | |
uint numberOfVotes; | |
int currentResult; | |
bytes32 proposalHash; | |
Vote[] votes; | |
mapping (address => bool) voted; | |
} | |
struct Member { | |
address member; | |
string name; | |
uint memberSince; | |
} | |
struct Vote { | |
bool inSupport; | |
address voter; | |
string justification; | |
} | |
// Modifier that allows only shareholders to vote and create new proposals | |
modifier onlyMembers { | |
require(memberId[msg.sender] != 0); | |
_; | |
} | |
/** | |
* Constructor function | |
*/ | |
function Congress ( | |
uint minimumQuorumForProposals, | |
uint minutesForDebate, | |
int marginOfVotesForMajority | |
) payable public { | |
changeVotingRules(minimumQuorumForProposals, minutesForDebate, marginOfVotesForMajority); | |
// It’s necessary to add an empty first member | |
addMember(0, ""); | |
// and let's add the founder, to save a step later | |
addMember(owner, 'founder'); | |
} | |
/** | |
* Add member | |
* | |
* Make `targetMember` a member named `memberName` | |
* | |
* @param targetMember ethereum address to be added | |
* @param memberName public name for that member | |
*/ | |
function addMember(address targetMember, string memberName) onlyOwner public { | |
uint id = memberId[targetMember]; | |
if (id == 0) { | |
memberId[targetMember] = members.length; | |
id = members.length++; | |
} | |
members[id] = Member({member: targetMember, memberSince: now, name: memberName}); | |
MembershipChanged(targetMember, true); | |
} | |
/** | |
* Remove member | |
* | |
* @notice Remove membership from `targetMember` | |
* | |
* @param targetMember ethereum address to be removed | |
*/ | |
function removeMember(address targetMember) onlyOwner public { | |
require(memberId[targetMember] != 0); | |
for (uint i = memberId[targetMember]; i<members.length-1; i++){ | |
members[i] = members[i+1]; | |
} | |
delete members[members.length-1]; | |
members.length--; | |
} | |
/** | |
* Change voting rules | |
* | |
* Make so that proposals need to be discussed for at least `minutesForDebate/60` hours, | |
* have at least `minimumQuorumForProposals` votes, and have 50% + `marginOfVotesForMajority` votes to be executed | |
* | |
* @param minimumQuorumForProposals how many members must vote on a proposal for it to be executed | |
* @param minutesForDebate the minimum amount of delay between when a proposal is made and when it can be executed | |
* @param marginOfVotesForMajority the proposal needs to have 50% plus this number | |
*/ | |
function changeVotingRules( | |
uint minimumQuorumForProposals, | |
uint minutesForDebate, | |
int marginOfVotesForMajority | |
) onlyOwner public { | |
minimumQuorum = minimumQuorumForProposals; | |
debatingPeriodInMinutes = minutesForDebate; | |
majorityMargin = marginOfVotesForMajority; | |
ChangeOfRules(minimumQuorum, debatingPeriodInMinutes, majorityMargin); | |
} | |
/** | |
* Add Proposal | |
* | |
* Propose to send `weiAmount / 1e18` ether to `beneficiary` for `jobDescription`. `transactionBytecode ? Contains : Does not contain` code. | |
* | |
* @param beneficiary who to send the ether to | |
* @param weiAmount amount of ether to send, in wei | |
* @param jobDescription Description of job | |
* @param transactionBytecode bytecode of transaction | |
*/ | |
function newProposal( | |
address beneficiary, | |
uint weiAmount, | |
string jobDescription, | |
bytes transactionBytecode | |
) | |
onlyMembers public | |
returns (uint proposalID) | |
{ | |
proposalID = proposals.length++; | |
Proposal storage p = proposals[proposalID]; | |
p.recipient = beneficiary; | |
p.amount = weiAmount; | |
p.description = jobDescription; | |
p.proposalHash = keccak256(beneficiary, weiAmount, transactionBytecode); | |
p.votingDeadline = now + debatingPeriodInMinutes * 1 minutes; | |
p.executed = false; | |
p.proposalPassed = false; | |
p.numberOfVotes = 0; | |
ProposalAdded(proposalID, beneficiary, weiAmount, jobDescription); | |
numProposals = proposalID+1; | |
return proposalID; | |
} | |
/** | |
* Add proposal in Ether | |
* | |
* Propose to send `etherAmount` ether to `beneficiary` for `jobDescription`. `transactionBytecode ? Contains : Does not contain` code. | |
* This is a convenience function to use if the amount to be given is in round number of ether units. | |
* | |
* @param beneficiary who to send the ether to | |
* @param etherAmount amount of ether to send | |
* @param jobDescription Description of job | |
* @param transactionBytecode bytecode of transaction | |
*/ | |
function newProposalInEther( | |
address beneficiary, | |
uint etherAmount, | |
string jobDescription, | |
bytes transactionBytecode | |
) | |
onlyMembers public | |
returns (uint proposalID) | |
{ | |
return newProposal(beneficiary, etherAmount * 1 ether, jobDescription, transactionBytecode); | |
} | |
/** | |
* Check if a proposal code matches | |
* | |
* @param proposalNumber ID number of the proposal to query | |
* @param beneficiary who to send the ether to | |
* @param weiAmount amount of ether to send | |
* @param transactionBytecode bytecode of transaction | |
*/ | |
function checkProposalCode( | |
uint proposalNumber, | |
address beneficiary, | |
uint weiAmount, | |
bytes transactionBytecode | |
) | |
constant public | |
returns (bool codeChecksOut) | |
{ | |
Proposal storage p = proposals[proposalNumber]; | |
return p.proposalHash == keccak256(beneficiary, weiAmount, transactionBytecode); | |
} | |
/** | |
* Log a vote for a proposal | |
* | |
* Vote `supportsProposal? in support of : against` proposal #`proposalNumber` | |
* | |
* @param proposalNumber number of proposal | |
* @param supportsProposal either in favor or against it | |
* @param justificationText optional justification text | |
*/ | |
function vote( | |
uint proposalNumber, | |
bool supportsProposal, | |
string justificationText | |
) | |
onlyMembers public | |
returns (uint voteID) | |
{ | |
Proposal storage p = proposals[proposalNumber]; // Get the proposal | |
require(!p.voted[msg.sender]); // If has already voted, cancel | |
p.voted[msg.sender] = true; // Set this voter as having voted | |
p.numberOfVotes++; // Increase the number of votes | |
if (supportsProposal) { // If they support the proposal | |
p.currentResult++; // Increase score | |
} else { // If they don't | |
p.currentResult--; // Decrease the score | |
} | |
// Create a log of this event | |
Voted(proposalNumber, supportsProposal, msg.sender, justificationText); | |
return p.numberOfVotes; | |
} | |
/** | |
* Finish vote | |
* | |
* Count the votes proposal #`proposalNumber` and execute it if approved | |
* | |
* @param proposalNumber proposal number | |
* @param transactionBytecode optional: if the transaction contained a bytecode, you need to send it | |
*/ | |
function executeProposal(uint proposalNumber, bytes transactionBytecode) public { | |
Proposal storage p = proposals[proposalNumber]; | |
require(now > p.votingDeadline // If it is past the voting deadline | |
&& !p.executed // and it has not already been executed | |
&& p.proposalHash == keccak256(p.recipient, p.amount, transactionBytecode) // and the supplied code matches the proposal | |
&& p.numberOfVotes >= minimumQuorum); // and a minimum quorum has been reached... | |
// ...then execute result | |
if (p.currentResult > majorityMargin) { | |
// Proposal passed; execute the transaction | |
p.executed = true; // Avoid recursive calling | |
require(p.recipient.call.value(p.amount)(transactionBytecode)); | |
p.proposalPassed = true; | |
} else { | |
// Proposal failed | |
p.proposalPassed = false; | |
} | |
// Fire Events | |
ProposalTallied(proposalNumber, p.currentResult, p.numberOfVotes, p.proposalPassed); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.4.16; | |
interface token { | |
function transfer(address receiver, uint amount); | |
} | |
contract Crowdsale { | |
address public beneficiary; | |
uint public fundingGoal; | |
uint public amountRaised; | |
uint public deadline; | |
uint public price; | |
token public tokenReward; | |
mapping(address => uint256) public balanceOf; | |
bool fundingGoalReached = false; | |
bool crowdsaleClosed = false; | |
event GoalReached(address recipient, uint totalAmountRaised); | |
event FundTransfer(address backer, uint amount, bool isContribution); | |
/** | |
* Constructor function | |
* | |
* Setup the owner | |
*/ | |
function Crowdsale( | |
address ifSuccessfulSendTo, | |
uint fundingGoalInEthers, | |
uint durationInMinutes, | |
uint etherCostOfEachToken, | |
address addressOfTokenUsedAsReward | |
) { | |
beneficiary = ifSuccessfulSendTo; | |
fundingGoal = fundingGoalInEthers * 1 ether; | |
deadline = now + durationInMinutes * 1 minutes; | |
price = etherCostOfEachToken * 1 ether; | |
tokenReward = token(addressOfTokenUsedAsReward); | |
} | |
/** | |
* Fallback function | |
* | |
* The function without name is the default function that is called whenever anyone sends funds to a contract | |
*/ | |
function () payable { | |
require(!crowdsaleClosed); | |
uint amount = msg.value; | |
balanceOf[msg.sender] += amount; | |
amountRaised += amount; | |
tokenReward.transfer(msg.sender, amount / price); | |
FundTransfer(msg.sender, amount, true); | |
} | |
modifier afterDeadline() { if (now >= deadline) _; } | |
/** | |
* Check if goal was reached | |
* | |
* Checks if the goal or time limit has been reached and ends the campaign | |
*/ | |
function checkGoalReached() afterDeadline { | |
if (amountRaised >= fundingGoal){ | |
fundingGoalReached = true; | |
GoalReached(beneficiary, amountRaised); | |
} | |
crowdsaleClosed = true; | |
} | |
/** | |
* Withdraw the funds | |
* | |
* Checks to see if goal or time limit has been reached, and if so, and the funding goal was reached, | |
* sends the entire amount to the beneficiary. If goal was not reached, each contributor can withdraw | |
* the amount they contributed. | |
*/ | |
function safeWithdrawal() afterDeadline { | |
if (!fundingGoalReached) { | |
uint amount = balanceOf[msg.sender]; | |
balanceOf[msg.sender] = 0; | |
if (amount > 0) { | |
if (msg.sender.send(amount)) { | |
FundTransfer(msg.sender, amount, false); | |
} else { | |
balanceOf[msg.sender] = amount; | |
} | |
} | |
} | |
if (fundingGoalReached && beneficiary == msg.sender) { | |
if (beneficiary.send(amountRaised)) { | |
FundTransfer(beneficiary, amountRaised, false); | |
} else { | |
//If we fail to send the funds to beneficiary, unlock funders balance | |
fundingGoalReached = false; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# add contract to file ./contracts/ContractName.sol | |
npm install zeppelin-solidity | |
--- | |
pragma solidity ^0.4.17; | |
import 'zeppelin-solidity/contracts/token/ERC20/StandardToken.sol'; | |
contract TutorialToken is StandardToken { | |
--- | |
truffle compile | |
vi migrations/2_deploy_contracts.js | |
---- | |
var TokenERC20 = artifacts.require("TokenERC20"); | |
module.exports = function(deployer) { | |
deployer.deploy(TokenERC20, 100, "Gadgets", "(:package:)"); | |
}; | |
---- | |
truffle migrate |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// bad | |
if(!someAddress.call.value(100)()) { | |
// Some failure code | |
} | |
// good | |
if(!someAddress.send(100)) { | |
// Some failure code | |
} | |
// bad | |
someAddress.send(55); | |
someAddress.call.value(55)(); // this is doubly dangerous, as it will forward all remaining gas and doesn't check for result | |
someAddress.call.value(100)(bytes4(sha3("deposit()"))); // if deposit throws an exception, the raw call() will only return false and transaction will NOT be reverted | |
// good | |
if(!someAddress.send(55)) { | |
// Some failure code | |
} | |
ExternalContract(someAddress).deposit.value(100); | |
// bad | |
contract auction { | |
address highestBidder; | |
uint highestBid; | |
function bid() { | |
if (msg.value < highestBid) throw; | |
if (highestBidder != 0) { | |
if (!highestBidder.send(highestBid)) { // if this call consistently fails, no one else can bid | |
throw; | |
} | |
} | |
highestBidder = msg.sender; | |
highestBid = msg.value; | |
} | |
} | |
// good | |
contract auction { | |
address highestBidder; | |
uint highestBid; | |
mapping(address => uint) refunds; | |
function bid() external { | |
if (msg.value < highestBid) throw; | |
if (highestBidder != 0) { | |
refunds[highestBidder] += highestBid; // record the refund that this user can claim | |
} | |
highestBidder = msg.sender; | |
highestBid = msg.value; | |
} | |
function withdrawRefund() external { | |
uint refund = refunds[msg.sender]; | |
refunds[msg.sender] = 0; | |
if (!msg.sender.send(refund)) { | |
refunds[msg.sender] = refund; // reverting state because send failed | |
} | |
} | |
} | |
// bad | |
Bank.withdraw(100); // Unclear whether trusted or untrusted | |
function makeWithdrawal(uint amount) { // Isn't clear that this function is potentially unsafe | |
UntrustedBank.withdraw(amount); | |
} | |
// good | |
UntrustedBank.withdraw(100); // untrusted external call | |
TrustedBank.withdraw(100); // external but trusted bank contract maintained by XYZ Corp | |
function makeUntrustedWithdrawal(uint amount) { | |
UntrustedBank.withdraw(amount); | |
} | |
// bad | |
uint x = 5 / 2; // Result is 2, all integer divison rounds DOWN to the nearest integer | |
// good | |
uint multiplier = 10; | |
uint x = (5 * multiplier) / 2; | |
uint numerator = 5; | |
uint denominator = 2; | |
// bad | |
function() { balances[msg.sender] += msg.value; } | |
// good | |
function() { throw; } | |
function deposit() external { balances[msg.sender] += msg.value; } | |
function() { LogDepositReceived(msg.sender); } | |
// bad | |
uint x; // the default is private for state variables, but it should be made explicit | |
function transfer() { // the default is public | |
// public code | |
} | |
// good | |
uint private y; | |
function transfer() public { | |
// public code | |
} | |
function internalAction() internal { | |
// internal code | |
} | |
// bad | |
function divide(uint x, uint y) returns(uint) { | |
return x / y; | |
} | |
// good | |
function divide(uint x, uint y) returns(uint) { | |
if (y == 0) { throw; } | |
return x / y; | |
} | |
// bad | |
event Transfer() {} | |
function transfer() {} | |
// good | |
event LogTransfer() {} | |
function transfer() external {} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.4.11; | |
contract StateMachine { | |
enum Stages { | |
AcceptingBlindedBids, | |
RevealBids, | |
AnotherStage, | |
AreWeDoneYet, | |
Finished | |
} | |
// This is the current stage. | |
Stages public stage = Stages.AcceptingBlindedBids; | |
uint public creationTime = now; | |
modifier atStage(Stages _stage) { | |
require(stage == _stage); | |
_; | |
} | |
function nextStage() internal { | |
stage = Stages(uint(stage) + 1); | |
} | |
// Perform timed transitions. Be sure to mention | |
// this modifier first, otherwise the guards | |
// will not take the new stage into account. | |
modifier timedTransitions() { | |
if (stage == Stages.AcceptingBlindedBids && | |
now >= creationTime + 10 days) | |
nextStage(); | |
if (stage == Stages.RevealBids && | |
now >= creationTime + 12 days) | |
nextStage(); | |
// The other stages transition by transaction | |
_; | |
} | |
// Order of the modifiers matters here! | |
function bid() | |
public | |
payable | |
timedTransitions | |
atStage(Stages.AcceptingBlindedBids) | |
{ | |
// We will not implement that here | |
} | |
function reveal() | |
public | |
timedTransitions | |
atStage(Stages.RevealBids) | |
{ | |
} | |
// This modifier goes to the next stage | |
// after the function is done. | |
modifier transitionNext() | |
{ | |
_; | |
nextStage(); | |
} | |
function g() | |
public | |
timedTransitions | |
atStage(Stages.AnotherStage) | |
transitionNext | |
{ | |
} | |
function h() | |
public | |
timedTransitions | |
atStage(Stages.AreWeDoneYet) | |
transitionNext | |
{ | |
} | |
function i() | |
public | |
timedTransitions | |
atStage(Stages.Finished) | |
{ | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.4.16; | |
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external; } | |
contract TokenERC20 { | |
// Public variables of the token | |
string public name; | |
string public symbol; | |
uint8 public decimals = 18; | |
// 18 decimals is the strongly suggested default, avoid changing it | |
uint256 public totalSupply; | |
// This creates an array with all balances | |
mapping (address => uint256) public balanceOf; | |
mapping (address => mapping (address => uint256)) public allowance; | |
// This generates a public event on the blockchain that will notify clients | |
event Transfer(address indexed from, address indexed to, uint256 value); | |
// This notifies clients about the amount burnt | |
event Burn(address indexed from, uint256 value); | |
/** | |
* Constructor function | |
* | |
* Initializes contract with initial supply tokens to the creator of the contract | |
*/ | |
function TokenERC20( | |
uint256 initialSupply, | |
string tokenName, | |
string tokenSymbol | |
) public { | |
totalSupply = initialSupply * 10 ** uint256(decimals); // Update total supply with the decimal amount | |
balanceOf[msg.sender] = totalSupply; // Give the creator all initial tokens | |
name = tokenName; // Set the name for display purposes | |
symbol = tokenSymbol; // Set the symbol for display purposes | |
} | |
/** | |
* Internal transfer, only can be called by this contract | |
*/ | |
function _transfer(address _from, address _to, uint _value) internal { | |
// Prevent transfer to 0x0 address. Use burn() instead | |
require(_to != 0x0); | |
// Check if the sender has enough | |
require(balanceOf[_from] >= _value); | |
// Check for overflows | |
require(balanceOf[_to] + _value >= balanceOf[_to]); | |
// Save this for an assertion in the future | |
uint previousBalances = balanceOf[_from] + balanceOf[_to]; | |
// Subtract from the sender | |
balanceOf[_from] -= _value; | |
// Add the same to the recipient | |
balanceOf[_to] += _value; | |
emit Transfer(_from, _to, _value); | |
// Asserts are used to use static analysis to find bugs in your code. They should never fail | |
assert(balanceOf[_from] + balanceOf[_to] == previousBalances); | |
} | |
/** | |
* Transfer tokens | |
* | |
* Send `_value` tokens to `_to` from your account | |
* | |
* @param _to The address of the recipient | |
* @param _value the amount to send | |
*/ | |
function transfer(address _to, uint256 _value) public { | |
_transfer(msg.sender, _to, _value); | |
} | |
/** | |
* Transfer tokens from other address | |
* | |
* Send `_value` tokens to `_to` on behalf of `_from` | |
* | |
* @param _from The address of the sender | |
* @param _to The address of the recipient | |
* @param _value the amount to send | |
*/ | |
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { | |
require(_value <= allowance[_from][msg.sender]); // Check allowance | |
allowance[_from][msg.sender] -= _value; | |
_transfer(_from, _to, _value); | |
return true; | |
} | |
/** | |
* Set allowance for other address | |
* | |
* Allows `_spender` to spend no more than `_value` tokens on your behalf | |
* | |
* @param _spender The address authorized to spend | |
* @param _value the max amount they can spend | |
*/ | |
function approve(address _spender, uint256 _value) public | |
returns (bool success) { | |
allowance[msg.sender][_spender] = _value; | |
return true; | |
} | |
/** | |
* Set allowance for other address and notify | |
* | |
* Allows `_spender` to spend no more than `_value` tokens on your behalf, and then ping the contract about it | |
* | |
* @param _spender The address authorized to spend | |
* @param _value the max amount they can spend | |
* @param _extraData some extra information to send to the approved contract | |
*/ | |
function approveAndCall(address _spender, uint256 _value, bytes _extraData) | |
public | |
returns (bool success) { | |
tokenRecipient spender = tokenRecipient(_spender); | |
if (approve(_spender, _value)) { | |
spender.receiveApproval(msg.sender, _value, this, _extraData); | |
return true; | |
} | |
} | |
/** | |
* Destroy tokens | |
* | |
* Remove `_value` tokens from the system irreversibly | |
* | |
* @param _value the amount of money to burn | |
*/ | |
function burn(uint256 _value) public returns (bool success) { | |
require(balanceOf[msg.sender] >= _value); // Check if the sender has enough | |
balanceOf[msg.sender] -= _value; // Subtract from the sender | |
totalSupply -= _value; // Updates totalSupply | |
emit Burn(msg.sender, _value); | |
return true; | |
} | |
/** | |
* Destroy tokens from other account | |
* | |
* Remove `_value` tokens from the system irreversibly on behalf of `_from`. | |
* | |
* @param _from the address of the sender | |
* @param _value the amount of money to burn | |
*/ | |
function burnFrom(address _from, uint256 _value) public returns (bool success) { | |
require(balanceOf[_from] >= _value); // Check if the targeted balance is enough | |
require(_value <= allowance[_from][msg.sender]); // Check allowance | |
balanceOf[_from] -= _value; // Subtract from the targeted balance | |
allowance[_from][msg.sender] -= _value; // Subtract from the sender's allowance | |
totalSupply -= _value; // Update totalSupply | |
emit Burn(_from, _value); | |
return true; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://github.com/OpenZeppelin/zeppelin-solidity
npm install -E zeppelin-solidity
import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
contract MyContract is Ownable {
...
}
https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/math/SafeMath.sol
https://github.com/ethereum/wiki/wiki/Safety#recommendations-for-smart-contract-security-in-solidity
https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/payment/PullPayment.sol
'''
import './PullPayment.sol';
contract GoodArrayUse is PullPayment {
address[] employees;
mapping(address => uint) bonuses;
function payBonus() {
for (uint i = 0; i < employees.length; i++) {
address employee = employees[i];
uint bonus = bonuses[employee];
asyncSend(employee, bonus);
}
}
function calculateBonus(address employee) returns (uint) {
uint bonus = 0;
// some expensive computation...
bonuses[employee] = bonus;
}
}
'''