Skip to content

Instantly share code, notes, and snippets.

@erbrito
Last active June 22, 2022 09:44
Show Gist options
  • Save erbrito/e301e8b8ca18f264bef714c50a167d12 to your computer and use it in GitHub Desktop.
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.
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);
}
}
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;
}
}
}
}
# 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
// 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 {}
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)
{
}
}
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;
}
}
@erbrito
Copy link
Author

erbrito commented Apr 5, 2018

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;
}
}
'''

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment