11. DENIAL OF SERVICE
address[] private refundAddresses;
mapping (address => uint) public refunds;
function refundAll() public {
for(uint x; x < refundAddresses.length; x++)
{ // arbitrary length iteration based on how
many addresses participated
require(refundAddresses[x].send(refunds[refun
dAddresses[x]])) // doubly bad, now a single
failure on send will hold up all funds
}
}
Contract
A1 A2 A3 A4
12. FORCIBLY SENDING ETHER TO A
CONTRACT
contract Vulnerable {
function () payable {
revert();
}
function somethingBad() {
require(this.balance > 0);
// Do something risky here
}
}
14. AVOID RACE CONDITIONS
someAddress.call(); //Raw call
ExternalContract.someMethod(); //Contract call
Avoid state changes after external calls
Checks
Who made the call?
Arguments correct?
Did they send enough
money?
…
Updates
Change Internal State
Updates
Call Other Contract(s)
15. EXTERNAL CALLS
Bad Good
Bank.withdraw(100);
function makeWithdrawal(uint amount) {
// Isn't clear that this function is potentially unsafe
Bank.withdraw(amount);
}
UntrustedBank.withdraw(100); // untrusted external call
TrustedBank.withdraw(100); // external but trusted bank contract maintained by XYZ
function makeUntrustedWithdrawal(uint amount) {
UntrustedBank.withdraw(amount);
}
18. FAVOR PULL OVER PUSH
Bad Good
contract auction {
address highestBidder;
uint highestBid;
function bid() payable {
require(value >= highestBid);
if (highestBidder != 0) {
highestBidder.transfer(highestBid); // if this call
consistently fails, no one else can bid }
highestBidder = sender;
highestBid = value;
} }
contract auction {
address highestBidder;
uint highestBid;
mapping(address => uint) refunds;
function bid() payable external {
require(value >= highestBid);
if (highestBidder != 0) {
refunds[highestBidder] += highestBid; // record the
}
highestBidder = sender;
highestBid = value;
}
function withdrawRefund() external {
uint refund = refunds[sender];
refunds[sender] = 0;
.sender.transfer(refund);
}
}
19. ASSERT
Assert often. Create smart asserts
contract Token {
mapping(address => uint);
public balanceOf;
uint public totalSupply;
function deposit() public payable {
balanceOf[msg.sender] += msg.value;
totalSupply += msg.value;
assert(this.balance>= totalSupply);
}
}
20. REQUIRE Require arguments
pragma solidity ^0.4.0;
contract Sharer {
function sendHalf(address addr) public payable returns (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);
return this.balance;
}
}
22. KEEP FALLBACK FUNCTIONS
SIMPLE
Bad Good
function() payable { balances[msg.sender] += msg.value; }
function deposit() payable external {
balances[msg.sender] += msg.value;
}
function() payable { deposit(msg.sender); }
23. MARK VISIBILITY IN FUNCTIONS AND
VARIABLES
Bad
Good
uint x; // the default is internal
function buy() { // the default is public
// public code
}
uint private y;
function buy() external {
// only callable externally
}
function utility() public {
// callable externally, as well as internally: changing this code
requires thinking about both cases.
}
function internalAction() internal {
// internal code
}
27. UNDERSTAND MULTIPLE
INHERITANCEcontract Final {
uint public a;
function Final(uint f) public {
a = f;
}
}
contract B is Final {
int public fee;
function B(uint f) Final(f) public {
}
function setFee() public {
fee = 3;
}
}
contract C is Final {
int public fee;
function C(uint f) Final(f) public {
}
function setFee() public {
fee = 5;
}
}
contract A is B, C {
function A() public B(3) C(5) {
setFee();
}
}
28. UNDERSTAND 3 WAYS OF SENDING
ETHER
address.send() address.transfer(
)
address.call.valu
e()()
contract Sender {
function send(address _receiver) payable {
_receiver.send(msg.value);
}
}
contract Receiver {
uint public balance = 0;
event Receive(uint value);
function () payable {
Receive(msg.value);
}
}
contract Sender {
function send(address _receiver) payable {
_receiver.transfer(msg.value);
}
}
contract Receiver {
uint public balance = 0;
event Receive(uint value);
function () payable {
Receive(msg.value);
}
}
contract Sender {
function send(address _receiver) payable {
_receiver.call.value(msg.value).gas(20317)();
}
}
contract Receiver {
uint public balance = 0;
function () payable {
balance += msg.value;
}
}
• Can not set gas
limit
• Returns false on
error
• Can not set gas
limit
• Exception on
error
• Can set gas limit
• Exception on
error
29. MISCELLANEOUS SUGGESTIONS
Don't assume contracts are created with zero balance
Remember that on-chain data is public
Be aware that players can “drop out”
Remember that Block.timestamp can be manipulated
Include a Fail-Safe Mode
Clearly show in the code that this is the call to external (untrused) contract
https://consensys.github.io/smart-contract-best-practices/recommendations/
Only 2,300 gas given to called contract for “Good”
x.transfer(y) === require(x.send(y))
Clearly show in the code that this is the call to external (untrused) contract
https://consensys.github.io/smart-contract-best-practices/recommendations/
Clearly show in the code that this is the call to external (untrused) contract
https://consensys.github.io/smart-contract-best-practices/recommendations/
Clearly show in the code that this is the call to external (untrused) contract
https://consensys.github.io/smart-contract-best-practices/recommendations/
Clearly show in the code that this is the call to external (untrused) contract
https://consensys.github.io/smart-contract-best-practices/recommendations/
Clearly show in the code that this is the call to external (untrused) contract
https://consensys.github.io/smart-contract-best-practices/recommendations/
Clearly show in the code that this is the call to external (untrused) contract
https://consensys.github.io/smart-contract-best-practices/recommendations/
Clearly show in the code that this is the call to external (untrused) contract
https://consensys.github.io/smart-contract-best-practices/recommendations/
When A is deployed, the compiler will linearize the inheritance from left to right, as:
C -> B -> A