-
Notifications
You must be signed in to change notification settings - Fork 9
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
Staking #1
Changes from 76 commits
Commits
Show all changes
77 commits
Select commit
Hold shift + click to select a range
32f4aab
WIP: Staking
bingen 5633b51
Lint
bingen cb3015e
Add Truffle
bingen 9f17039
Increase coverage to 100%
bingen 82388be
Convert ternary conditions into if-else clauses
bingen a743f8e
Add Echidna scripts
bingen c75bc87
Fix echidna config
bingen afa9ad6
Fix embark test script at package.json
bingen 70bc6b0
Remove unnecessary variable from locking test
bingen 5e386aa
Remove unnecessary checks commented out
bingen e1ba0e3
Change locksCount parameter name for consistency
bingen 1bebf18
Add function headers to Staking
bingen 095c3e9
Fix function order
bingen 4a0c595
Remove comment from Checkpointing
bingen a3af701
Optimize gas in checkpointing library
bingen 5a0a510
Add tests to echidna
bingen fa26336
Change error name and description
bingen 0bae3b2
Check that staking token is a contract
bingen aec8f9b
Change functions order
bingen 145b39f
Change stake and unstake inner logic order
bingen 6768a66
Add Transferred event
bingen d6372bf
Modify comments
bingen 683ff6b
Add test for canUnlock with an EOA manager
bingen da944fd
Remove unnecessary check from transferFromLock function
bingen d31e7ae
Unlock lock if after transfer from it amount goes down to zero
bingen c593d71
Add Echidna tests
bingen bd12bee
Clean locking.js
bingen eba497f
Add temporary README
bingen e4d63cd
Change default amounts to 120
bingen 080728f
Add `truffleit` script to `npm run test:truffle`
bingen 605e407
Add conversions to `truffleit` script
bingen 18fbcd4
Rename stakeHistory to stakedHistory for consistency
bingen 4653026
Add Manticore instructions to README
bingen 7ea5aa2
Merge branch 'master' into embark
izqui f042d90
Update contracts/Checkpointing.sol
izqui c6e723d
Update contracts/Checkpointing.sol
izqui 7958db9
Update contracts/Staking.sol
izqui e633336
Update contracts/Staking.sol
izqui 12dbe90
Update contracts/ERCStaking.sol
izqui ba07687
Update contracts/Staking.sol
izqui d4cca54
Update contracts/Staking.sol
izqui 9b8e09b
Update contracts/Staking.sol
izqui 87c820b
Update contracts/Staking.sol
izqui ec51267
Update contracts/Staking.sol
izqui 14af231
Update contracts/Staking.sol
izqui b716069
Update contracts/Staking.sol
izqui e3d05b6
Update contracts/Staking.sol
izqui fe8eeaf
Update contracts/Staking.sol
izqui 21dccd2
Update contracts/Staking.sol
izqui 23abb03
Update contracts/Staking.sol
izqui cf1bb69
Move TimeLockManager to lock-managers folder
5b998e8
Add IStakingLocking interface
3b6c529
Minor changes
578f5eb
Account only for properly staked balance
d69fe52
Rearrange function order and other minor changes
d36ca38
Optimize _unlock function
6749093
Fix test, wrong staking address
bingen 876c915
Update contracts/Staking.sol
izqui 367c88d
Rename event
bingen 84aaeb7
Fix checkpointing test using BN
bingen bbbfa54
Move packages from devDependencies to dependencies
bingen 788a496
Fix weird issue with embark dependency
bingen 6aeb42b
Update test_embark/locking.js
izqui ba8f85c
Apply suggestions from code review
izqui 7c2faf3
Cosmetic change to contracts/Staking.sol
izqui 7b7f70a
Cosmetic changes to tests
3ff2c1c
Add functions to interface
a05b933
Apply suggestions from code review
facuspagnuolo 1e35f37
Add Embark blockchain connector
053e22b
Optimize some gas in checkpointing library?? (#4)
bingen 6f7728f
Address PR #1 comments
7c9c1de
Switch from embark to Truffle
efae97a
Address PR #1 comments
e687a47
chore: update .gitignore
facuspagnuolo edfe9e4
chore: update readme
facuspagnuolo a96ca59
Address PR #1 comments
54d6ec1
Address PR #1 comments
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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 |
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
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/', | ||
] | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
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
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 | ||
} | ||
} |
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
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`. |
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
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; | ||
} | ||
|
||
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"; | ||
facuspagnuolo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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 { | ||
facuspagnuolo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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) { | ||
facuspagnuolo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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); | ||
} | ||
} | ||
facuspagnuolo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
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; | ||
} | ||
facuspagnuolo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
return self.history[low].value; | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
pragma solidity ^0.4.24; | ||
|
||
izqui marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// 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); | ||
} |
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
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); | ||
} |
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
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); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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 withERC20
, we won't be able to handleuint256
transfers. I know we are talking about huge numbers, but it's important to be aware of that IMO.There was a problem hiding this comment.
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.