Skip to content
This repository has been archived by the owner on Dec 16, 2022. It is now read-only.

File split and removing solc warnings #12

Merged
merged 9 commits into from
Mar 13, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 44 additions & 86 deletions smart_contract.sol → contracts/ForkDelta.sol
Original file line number Diff line number Diff line change
@@ -1,64 +1,12 @@
pragma solidity ^0.4.20;
pragma solidity ^0.4.21;

contract SafeMath {
function safeMul(uint a, uint b) pure internal returns (uint) {
uint c = a * b;
assert(a == 0 || c / a == b);
return c;
}

function safeSub(uint a, uint b) pure internal returns (uint) {
assert(b <= a);
return a - b;
}

function safeAdd(uint a, uint b) pure internal returns (uint) {
uint c = a + b;
assert(c>=a && c>=b);
return c;
}
}

contract Token {
/// @return total amount of tokens
function totalSupply() public constant returns (uint256 supply) {}

/// @param _owner The address from which the balance will be retrieved
/// @return The balance
function balanceOf(address _owner) public constant returns (uint256 balance) {}

/// @notice send `_value` token to `_to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transfer(address _to, uint256 _value) public returns (bool success) {}
import "./Token.sol";
import "./SafeMath.sol";

/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {}
contract ForkDelta {

using SafeMath for uint;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<3


/// @notice `msg.sender` approves `_addr` to spend `_value` tokens
/// @param _spender The address of the account able to transfer the tokens
/// @param _value The amount of wei to be approved for transfer
/// @return Whether the approval was successful or not
function approve(address _spender, uint256 _value) public returns (bool success) {}

/// @param _owner The address of the account owning tokens
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens allowed to spent
function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {}

event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);

uint public decimals;
string public name;
}

contract ForkDelta is SafeMath {
address public admin; //the admin address
address public feeAccount; //the account that will receive fees
uint public feeMake; //percentage times (1 ether)
Expand Down Expand Up @@ -115,31 +63,31 @@ contract ForkDelta is SafeMath {
}

function deposit() public payable {
tokens[0][msg.sender] = safeAdd(tokens[0][msg.sender], msg.value);
Deposit(0, msg.sender, msg.value, tokens[0][msg.sender]);
tokens[0][msg.sender] = tokens[0][msg.sender].add(msg.value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So much more syntactically pleasing. Good addition.

emit Deposit(0, msg.sender, msg.value, tokens[0][msg.sender]);
}

function withdraw(uint amount) public {
require(tokens[0][msg.sender] >= amount);
tokens[0][msg.sender] = safeSub(tokens[0][msg.sender], amount);
require(msg.sender.call.value(amount)());
Withdraw(0, msg.sender, amount, tokens[0][msg.sender]);
tokens[0][msg.sender] = tokens[0][msg.sender].sub(amount);
msg.sender.transfer(amount);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we remove the require here?
Why did we change call.value to .transfer throughout?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously: require throws when function returns false, e.g. when not enough eth.
New version: transfer will not return a result, but throw on fail itself, so no require needed

You could also say that transfer is safer to use, because you don't have to check the result.

Reason for the change: Safety against reentrancy as explained in:

https://consensys.github.io/smart-contract-best-practices/recommendations/#be-aware-of-the-tradeoffs-between-send-transfer-and-callvalue

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beautiful.

emit Withdraw(0, msg.sender, amount, tokens[0][msg.sender]);
}

function depositToken(address token, uint amount) public {
//remember to call Token(address).approve(this, amount) or this contract will not be able to do the transfer on your behalf.
require(token!=0);
require(Token(token).transferFrom(msg.sender, this, amount));
tokens[token][msg.sender] = safeAdd(tokens[token][msg.sender], amount);
Deposit(token, msg.sender, amount, tokens[token][msg.sender]);
tokens[token][msg.sender] = tokens[token][msg.sender].add(amount);
emit Deposit(token, msg.sender, amount, tokens[token][msg.sender]);
}

function withdrawToken(address token, uint amount) public {
require(token!=0);
require(tokens[token][msg.sender] >= amount);
tokens[token][msg.sender] = safeSub(tokens[token][msg.sender], amount);
tokens[token][msg.sender] = tokens[token][msg.sender].sub(amount);
require(Token(token).transfer(msg.sender, amount));
Withdraw(token, msg.sender, amount, tokens[token][msg.sender]);
emit Withdraw(token, msg.sender, amount, tokens[token][msg.sender]);
}

//support ERC223
Expand All @@ -154,7 +102,7 @@ contract ForkDelta is SafeMath {
function order(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce) public {
bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce);
orders[msg.sender][hash] = true;
Order(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, msg.sender);
emit Order(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, msg.sender);
}

function trade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s, uint amount) public {
Expand All @@ -163,11 +111,11 @@ contract ForkDelta is SafeMath {
require((
(orders[user][hash] || ecrecover(keccak256("\x19Ethereum Signed Message:\n32", hash),v,r,s) == user) &&
block.number <= expires &&
safeAdd(orderFills[user][hash], amount) <= amountGet
orderFills[user][hash].add(amount) <= amountGet
));
tradeBalances(tokenGet, amountGet, tokenGive, amountGive, user, amount);
orderFills[user][hash] = safeAdd(orderFills[user][hash], amount);
Trade(tokenGet, amount, tokenGive, amountGive * amount / amountGet, user, msg.sender);
orderFills[user][hash] = orderFills[user][hash].add(amount);
emit Trade(tokenGet, amount, tokenGive, amountGive * amount / amountGet, user, msg.sender);
}

function tradeBalances(address tokenGet, uint amountGet, address tokenGive, uint amountGive, address user, uint amount) private {
Expand All @@ -176,35 +124,45 @@ contract ForkDelta is SafeMath {
uint feeMakeXfer = 0;
uint feeTakeXfer = 0;
if (now >= freeUntilDate) {
feeMakeXfer = safeMul(amount, feeMake) / (1 ether);
feeTakeXfer = safeMul(amount, feeTake) / (1 ether);
feeMakeXfer = amount.mul(feeMake) / (1 ether);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a SafeMath division?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Dividing by 1 ether will never result in an overflow.

feeTakeXfer = amount.mul(feeTake) / (1 ether);
}

tokens[tokenGet][msg.sender] = safeSub(tokens[tokenGet][msg.sender], safeAdd(amount, feeTakeXfer));
tokens[tokenGet][user] = safeAdd(tokens[tokenGet][user], safeSub(amount, feeMakeXfer));
tokens[tokenGet][feeAccount] = safeAdd(tokens[tokenGet][feeAccount], safeAdd(feeMakeXfer, feeTakeXfer));
tokens[tokenGive][user] = safeSub(tokens[tokenGive][user], safeMul(amountGive, amount) / amountGet);
tokens[tokenGive][msg.sender] = safeAdd(tokens[tokenGive][msg.sender], safeMul(amountGive, amount) / amountGet);
tokens[tokenGet][msg.sender] = tokens[tokenGet][msg.sender].sub(amount.add(feeTakeXfer));
tokens[tokenGet][user] = tokens[tokenGet][user].add(amount.sub(feeMakeXfer));
tokens[tokenGet][feeAccount] = tokens[tokenGet][feeAccount].add(feeMakeXfer.sub(feeTakeXfer));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, typo:

feeMakeXfer.sub(feeTakeXfer)

Should be

feeMakeXfer.add(feeTakeXfer)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ups.. Will fix.

tokens[tokenGive][user] = tokens[tokenGive][user].sub(amountGive.mul(amount) / amountGet);
tokens[tokenGive][user] = tokens[tokenGive][user].sub(amountGive.mul(amount) / amountGet);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ups. Will fix.

tokens[tokenGive][msg.sender] = tokens[tokenGive][msg.sender].add(amountGive.mul(amount) / amountGet);
}

function testTrade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s, uint amount, address sender) public constant returns(bool) {
if (!(
tokens[tokenGet][sender] >= amount &&
availableVolume(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, user, v, r, s) >= amount
)) return false;
return true;
)) {
return false;
} else {
return true;
}
}

function availableVolume(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s) public constant returns(uint) {
bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce);
if (!(
(orders[user][hash] || ecrecover(keccak256("\x19Ethereum Signed Message:\n32", hash),v,r,s) == user) &&
block.number <= expires
)) return 0;
uint available1 = safeSub(amountGet, orderFills[user][hash]);
uint available2 = safeMul(tokens[tokenGive][user], amountGet) / amountGive;
if (available1<available2) return available1;
return available2;
)) {
return 0;
}
uint[2] memory available;
available[0] = amountGet.sub(orderFills[user][hash]);
available[1] = tokens[tokenGive][user].mul(amountGet) / amountGive;
if (available[0]<available[1]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make this (available[0] < available[1]) for code formatting consistency.

return available[0];
} else {
return available[1];
}
}

function amountFilled(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s) public constant returns(uint) {
Expand All @@ -216,6 +174,6 @@ contract ForkDelta is SafeMath {
bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce);
require ((orders[msg.sender][hash] || ecrecover(keccak256("\x19Ethereum Signed Message:\n32", hash),v,r,s) == msg.sender));
orderFills[msg.sender][hash] = amountGet;
Cancel(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, msg.sender, v, r, s);
emit Cancel(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, msg.sender, v, r, s);
}
}
47 changes: 47 additions & 0 deletions contracts/SafeMath.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
pragma solidity ^0.4.21;

/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {

/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the logic be if a == 0 OR b == 0 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SafeMath.sol is from https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/math/SafeMath.sol

They kind of know what they are doing, at least much better than I do.

Regarding your comment, no, for the check that follows it's only vital that a is not 0.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, if either A or B is 0, the function will return 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but if a is 0 the function would in fact throw an division by zero exception. In contrast to b = 0.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that's true.

return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}

/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
Copy link
Contributor

@JonathonDunford JonathonDunford Mar 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want a throw; here do we?
Assert/require/revert would probably be better? Or does that comment mean it will revert?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, Solidity will automatically throw/revert on division by zero.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it revert or does it throw? Very different outcomes.

Copy link
Contributor Author

@mmhh1910 mmhh1910 Mar 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the solidity docs:

An assert-style exception is generated in the following situations:
...
If you divide or modulo by zero (e.g. 5 / 0 or 23 % 0).

So, the OpenZeppelin removed the assert, because it is implicitly done by solidity already.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to revert? Save users gas if you can?

uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}

/**
* @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}

/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think the contract should revert if you add by 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. :-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. We should be using require everywhere instead of assert AFAIK.

"assert(false) compiles to 0xfe, which is an invalid opcode, using up all remaining gas, and reverting all changes.

require(false) compiles to 0xfd which is the REVERT opcode, meaning it will refund the remaining gas. The opcode can also return a value (useful for debugging), but I don't believe that is supported in Solidity as of this moment. (2017-11-21)"

  1. assert(c >= a); will revert if you try to add something by 0 as it is assuming the next value will be larger. Not sure this is behavior we want?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@betterdelta

Thoughts on this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re 1.): You are in opposition to https://solidity.readthedocs.io/en/develop/control-structures.html#error-handling-assert-require-revert-and-exceptions:

"The assert function should only be used to test for internal errors, and to check invariants. The require function should be used to ensure valid conditions, such as inputs, or contract state variables are met, or to validate return values from calls to external contracts. If used properly, analysis tools can evaluate your contract to identify the conditions and function calls which will reach a failing assert. Properly functioning code should never reach a failing assert statement; if this happens there is a bug in your contract which you should fix."

Re 2:
a.) Assert will throw an exception, not revert.
b.) I don't get it. Neither 10.add(0), now 0.add(10), nor 0,add(0) will make the assert statement throw an exception.

Copy link
Contributor

@JonathonDunford JonathonDunford Mar 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I understand it's in opposition to their proposed structure, however doesn't it provides a better user experience to revert transactions and return gas instead of tipping the miners extra?

  2. I was reading that wrong. I guess it just doesn't support addition by negatives.

return c;
}
}
40 changes: 40 additions & 0 deletions contracts/Token.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
pragma solidity ^0.4.21;

contract Token {
/// @return total amount of tokens
function totalSupply() public constant returns (uint256 supply) {}

/// @param _owner The address from which the balance will be retrieved
/// @return The balance
function balanceOf(address _owner) public constant returns (uint256 balance) {}

/// @notice send `_value` token to `_to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transfer(address _to, uint256 _value) public returns (bool success) {}

/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {}

/// @notice `msg.sender` approves `_addr` to spend `_value` tokens
/// @param _spender The address of the account able to transfer the tokens
/// @param _value The amount of wei to be approved for transfer
/// @return Whether the approval was successful or not
function approve(address _spender, uint256 _value) public returns (bool success) {}

/// @param _owner The address of the account owning tokens
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens allowed to spent
function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {}

event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);

uint public decimals;
string public name;
}