Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Staking #1

Merged
merged 77 commits into from
Apr 30, 2019
Merged
Show file tree
Hide file tree
Changes from 76 commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
32f4aab
WIP: Staking
bingen Nov 21, 2018
5633b51
Lint
bingen Nov 26, 2018
cb3015e
Add Truffle
bingen Nov 26, 2018
9f17039
Increase coverage to 100%
bingen Nov 27, 2018
82388be
Convert ternary conditions into if-else clauses
bingen Nov 27, 2018
a743f8e
Add Echidna scripts
bingen Nov 27, 2018
c75bc87
Fix echidna config
bingen Dec 7, 2018
afa9ad6
Fix embark test script at package.json
bingen Dec 7, 2018
70bc6b0
Remove unnecessary variable from locking test
bingen Dec 7, 2018
5e386aa
Remove unnecessary checks commented out
bingen Dec 7, 2018
e1ba0e3
Change locksCount parameter name for consistency
bingen Dec 7, 2018
1bebf18
Add function headers to Staking
bingen Dec 7, 2018
095c3e9
Fix function order
bingen Dec 7, 2018
4a0c595
Remove comment from Checkpointing
bingen Dec 9, 2018
a3af701
Optimize gas in checkpointing library
bingen Dec 9, 2018
5a0a510
Add tests to echidna
bingen Dec 9, 2018
fa26336
Change error name and description
bingen Dec 9, 2018
0bae3b2
Check that staking token is a contract
bingen Dec 9, 2018
aec8f9b
Change functions order
bingen Dec 9, 2018
145b39f
Change stake and unstake inner logic order
bingen Dec 9, 2018
6768a66
Add Transferred event
bingen Dec 9, 2018
d6372bf
Modify comments
bingen Dec 9, 2018
683ff6b
Add test for canUnlock with an EOA manager
bingen Dec 9, 2018
da944fd
Remove unnecessary check from transferFromLock function
bingen Dec 9, 2018
d31e7ae
Unlock lock if after transfer from it amount goes down to zero
bingen Dec 9, 2018
c593d71
Add Echidna tests
bingen Dec 9, 2018
bd12bee
Clean locking.js
bingen Dec 10, 2018
eba497f
Add temporary README
bingen Dec 10, 2018
e4d63cd
Change default amounts to 120
bingen Dec 10, 2018
080728f
Add `truffleit` script to `npm run test:truffle`
bingen Dec 10, 2018
605e407
Add conversions to `truffleit` script
bingen Dec 10, 2018
18fbcd4
Rename stakeHistory to stakedHistory for consistency
bingen Dec 10, 2018
4653026
Add Manticore instructions to README
bingen Dec 10, 2018
7ea5aa2
Merge branch 'master' into embark
izqui Dec 13, 2018
f042d90
Update contracts/Checkpointing.sol
izqui Dec 13, 2018
c6e723d
Update contracts/Checkpointing.sol
izqui Dec 13, 2018
7958db9
Update contracts/Staking.sol
izqui Dec 13, 2018
e633336
Update contracts/Staking.sol
izqui Dec 13, 2018
12dbe90
Update contracts/ERCStaking.sol
izqui Dec 13, 2018
ba07687
Update contracts/Staking.sol
izqui Dec 13, 2018
d4cca54
Update contracts/Staking.sol
izqui Dec 13, 2018
9b8e09b
Update contracts/Staking.sol
izqui Dec 13, 2018
87c820b
Update contracts/Staking.sol
izqui Dec 13, 2018
ec51267
Update contracts/Staking.sol
izqui Dec 13, 2018
14af231
Update contracts/Staking.sol
izqui Dec 13, 2018
b716069
Update contracts/Staking.sol
izqui Dec 13, 2018
e3d05b6
Update contracts/Staking.sol
izqui Dec 13, 2018
fe8eeaf
Update contracts/Staking.sol
izqui Dec 13, 2018
21dccd2
Update contracts/Staking.sol
izqui Dec 13, 2018
23abb03
Update contracts/Staking.sol
izqui Dec 13, 2018
cf1bb69
Move TimeLockManager to lock-managers folder
Dec 14, 2018
5b998e8
Add IStakingLocking interface
Dec 14, 2018
3b6c529
Minor changes
Dec 14, 2018
578f5eb
Account only for properly staked balance
Dec 14, 2018
d69fe52
Rearrange function order and other minor changes
Dec 14, 2018
d36ca38
Optimize _unlock function
Dec 14, 2018
6749093
Fix test, wrong staking address
bingen Jan 4, 2019
876c915
Update contracts/Staking.sol
izqui Jan 4, 2019
367c88d
Rename event
bingen Jan 8, 2019
84aaeb7
Fix checkpointing test using BN
bingen Jan 8, 2019
bbbfa54
Move packages from devDependencies to dependencies
bingen Jan 8, 2019
788a496
Fix weird issue with embark dependency
bingen Jan 8, 2019
6aeb42b
Update test_embark/locking.js
izqui Jan 21, 2019
ba8f85c
Apply suggestions from code review
izqui Jan 21, 2019
7c2faf3
Cosmetic change to contracts/Staking.sol
izqui Jan 21, 2019
7b7f70a
Cosmetic changes to tests
Jan 21, 2019
3ff2c1c
Add functions to interface
Mar 12, 2019
a05b933
Apply suggestions from code review
facuspagnuolo Apr 10, 2019
1e35f37
Add Embark blockchain connector
Apr 11, 2019
053e22b
Optimize some gas in checkpointing library?? (#4)
bingen Apr 11, 2019
6f7728f
Address PR #1 comments
Apr 12, 2019
7c9c1de
Switch from embark to Truffle
Apr 12, 2019
efae97a
Address PR #1 comments
Apr 15, 2019
e687a47
chore: update .gitignore
facuspagnuolo Apr 12, 2019
edfe9e4
chore: update readme
facuspagnuolo Apr 12, 2019
a96ca59
Address PR #1 comments
Apr 24, 2019
54d6ec1
Address PR #1 comments
Apr 29, 2019
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
19 changes: 12 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
.embark
chains.json
config/production/password
config/livenet/password
coverage
dist
# dependencies
node_modules
build

# coverage
coverage
coverage.json

# lock files
package-lock.json

# build artifacts
build
flattened_contracts
7 changes: 7 additions & 0 deletions .solcover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
copyPackages: ['@aragon/os'],
testCommand: 'truffle test test_truffle/*.js --network coverage',
skipFiles: [
'test/',
]
}
1 change: 1 addition & 0 deletions .soliumignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

23 changes: 23 additions & 0 deletions .soliumrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"extends": "solium:all",
"rules": {
"imports-on-top": ["error"],
"variable-declarations": ["error"],
"array-declarations": ["error"],
"operator-whitespace": ["error"],
"lbrace": ["error"],
"mixedcase": 0,
"camelcase": ["error"],
"uppercase": 0,
"no-empty-blocks": ["error"],
"no-unused-vars": ["error"],
"quotes": ["error"],
"indentation": 0,
"whitespace": ["error"],
"deprecated-suicide": ["error"],
"arg-overflow": ["error", 8],
"pragma-on-top": ["error"],
"security/enforce-explicit-visibility": ["error"],
"error-reason": 0
}
}
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Staking App

A Staking app with checkpointing (implementing ERC900 interface with history) and locking.

## Testing
### Truffle

Currently this app is using Truffle. You can run tests with `npm test`.

### Slither
[Install slither](https://github.com/trailofbits/slither#how-to-install) and then:
```
slither --solc /usr/local/bin/solc .
```

Some noise can be filtered with:
```
slither --solc /usr/local/bin/solc . 2>/tmp/a.txt ; grep -v "is not in mixedCase" /tmp/a.txt | grep "Contract: Staking"
```

### Echidna
Run `./scripts/flatten_echidna.sh` and then:
```
docker run -v `pwd`:/src trailofbits/echidna echidna-test /src/flattened_contracts/EchidnaStaking.sol EchidnaStaking --config="/src/echidna/config.yaml"
```

### Manticore
```
docker run --rm -ti -v `pwd`:/src trailofbits/manticore bash
ulimit -s unlimited
manticore --detect-all --contract Staking /src/flattened_contracts/Staking.sol
```

## Coverage
You can measure coverage using Truffle by running `npm run coverage`.
119 changes: 119 additions & 0 deletions contracts/Checkpointing.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
pragma solidity ^0.4.24;


library Checkpointing {
struct Checkpoint {
uint64 time;
uint192 value;
Copy link
Contributor

Choose a reason for hiding this comment

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

I see why using 192 here, but bear in mind we won't be fully compatible with ERC20, we won't be able to handle uint256 transfers. I know we are talking about huge numbers, but it's important to be aware of that IMO.

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, I know. I think 2^192 is big enough, but I'll like to bring @izqui into this discussion.

}

struct History {
Checkpoint[] history;
}

uint256 private constant MAX_UINT192 = uint256(uint192(-1));
uint256 private constant MAX_UINT64 = uint256(uint64(-1));

string private constant ERROR_PAST_CHECKPOINT = "CHECKPOINT_PAST_CHECKPOINT";
string private constant ERROR_TIME_TOO_BIG = "CHECKPOINT_TIME_TOO_BIG";
string private constant ERROR_VALUE_TOO_BIG = "CHECKPOINT_VALUE_TOO_BIG";

function add(History storage self, uint256 time, uint256 value) internal {
require(time <= MAX_UINT64, ERROR_TIME_TOO_BIG);
require(value <= MAX_UINT192, ERROR_VALUE_TOO_BIG);

add192(self, uint64(time), uint192(value));
}

function add64(History storage self, uint64 time, uint256 value) internal {
require(value <= MAX_UINT192, ERROR_VALUE_TOO_BIG);

add192(self, time, uint192(value));
}

function get(History storage self, uint256 time) internal view returns (uint256) {
require(time <= MAX_UINT64, ERROR_TIME_TOO_BIG);

return uint256(get192(self, uint64(time)));
}

function get64(History storage self, uint64 time) internal view returns (uint256) {
return uint256(get192(self, time));
}

function lastUpdated(History storage self) internal view returns (uint256) {
uint256 length = self.history.length;

if (length > 0) {
return uint256(self.history[length - 1].time);
}

return 0;
}

function getLatestValue(History storage self) internal view returns (uint256) {
uint256 length = self.history.length;
if (length > 0) {
return uint256(self.history[length - 1].value);
}

return 0;
}

function add192(History storage self, uint64 time, uint192 value) internal {
uint256 length = self.history.length;

if (length == 0) {
self.history.push(Checkpoint(time, value));
} else {
Checkpoint storage currentCheckpoint = self.history[length - 1];
uint64 currentCheckpointTime = currentCheckpoint.time;
if (time > currentCheckpointTime) {
self.history.push(Checkpoint(time, value));
} else if (time == currentCheckpointTime) {
currentCheckpoint.value = value;
} else { // ensure list ordering
revert(ERROR_PAST_CHECKPOINT);
}
}
}

function get192(History storage self, uint64 time) internal view returns (uint192) {
uint256 length = self.history.length;

if (length == 0) {
return 0;
}

uint256 lastIndex = length - 1;

// short-circuit
Checkpoint storage lastCheckpoint = self.history[lastIndex];
if (time >= lastCheckpoint.time) {
return lastCheckpoint.value;
}

if (time < self.history[0].time) {
return 0;
}

uint256 low = 0;
uint256 high = lastIndex;

while (high > low) {
uint256 mid = (high + low + 1) / 2; // average, ceil round
Checkpoint storage checkpoint = self.history[mid];
uint64 midTime = checkpoint.time;

if (time > midTime) {
low = mid;
} else if (time < midTime) {
high = mid - 1;
} else { // time == midTime
return checkpoint.value;
}
}

return self.history[low].value;
}
}
23 changes: 23 additions & 0 deletions contracts/ERCStaking.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
pragma solidity ^0.4.24;

// Interface for ERC900: https://eips.ethereum.org/EIPS/eip-900
interface ERCStaking {
event Staked(address indexed user, uint256 amount, uint256 total, bytes data);
event Unstaked(address indexed user, uint256 amount, uint256 total, bytes data);

function stake(uint256 amount, bytes data) external;
function stakeFor(address user, uint256 amount, bytes data) external;
function unstake(uint256 amount, bytes data) external;

function totalStakedFor(address addr) external view returns (uint256);
function totalStaked() external view returns (uint256);
function token() external view returns (address);

function supportsHistory() external pure returns (bool);
}

interface ERCStakingHistory {
function lastStakedFor(address addr) external view returns (uint256);
function totalStakedForAt(address addr, uint256 blockNumber) external view returns (uint256);
function totalStakedAt(uint256 blockNumber) external view returns (uint256);
}
6 changes: 6 additions & 0 deletions contracts/ILockManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pragma solidity ^0.4.24;


interface ILockManager {
function canUnlock(address account, uint256 lockId, bytes lockData) external view returns (bool);
}
33 changes: 33 additions & 0 deletions contracts/IStakingLocking.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
pragma solidity ^0.4.24;

import "./ILockManager.sol";


interface IStakingLocking {
event Locked(address indexed account, uint256 indexed lockId, uint256 amount, address manager, bytes data);
event Unlocked(address indexed account, uint256 indexed lockId, uint256 amount, address manager, bytes data);
event LockAmountChanged(address indexed account, uint256 indexed lockId, uint256 amount);
event LockManagerChanged(address indexed account, uint256 indexed lockId, address manager);
event LockDataChanged(address indexed account, uint256 indexed lockId, bytes data);

function lock(uint256 _amount, address _manager, bytes _data) external returns (uint256 _lockId);
function unlock(address _account, uint256 _lockId) external;
function decreaseLockAmount(address _account, uint256 _lockId, uint256 _newAmount) external;
function setLockManager(address _account, uint256 _lockId, ILockManager _newManager) external;
function setLockData(address _account, uint256 _lockId, bytes _newData) external;
function transfer(address _to, uint256 _toLockId, uint256 _amount) external;
function transferFromLock(address _account, uint256 _lockId, address _to, uint256 _toLockId, uint256 _amount) external;

function locksCount(address _account) external view returns (uint256);
function getLock(address _account, uint256 _lockId)
external
view
returns (
uint256 _amount,
uint64 _unlockedAt,
address _manager,
bytes _data
);
function unlockedBalanceOf(address _account) external view returns (uint256);
function canUnlock(address _account, uint256 _lockId) external view returns (bool);
}
Loading