-
Notifications
You must be signed in to change notification settings - Fork 10
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
PoC discount on fees when tokens are staked #13
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
c425e70
Add interface
johnhforrest 8201d19
Added stubs for ERC900
johnhforrest f4b2ffe
Upgrading to 0.4.24 from master
johnhforrest a445bbc
WIP staking work
johnhforrest 6f1ef4d
PoC test case passing, combined mappings to save gas
johnhforrest 294f4bc
Adding fees for other functions, added more test coverage when fees a…
johnhforrest e1c52f8
Fix build
johnhforrest 9beb8b9
Removing one more reference
johnhforrest 15dead1
Fix test cases after merge & code review feedback
johnhforrest 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
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
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
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
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,24 @@ | ||
pragma solidity ^0.4.24; | ||
|
||
|
||
/** | ||
* @title ERC900 interface | ||
* @dev see https://github.com/ethereum/EIPs/issues/900 | ||
*/ | ||
contract ERC900 { | ||
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) public; | ||
function stakeFor(address user, uint256 amount, bytes data) public; | ||
function unstake(uint256 amount, bytes data) public; | ||
function totalStakedFor(address addr) public view returns (uint256); | ||
function totalStaked() public view returns (uint256); | ||
function token() public view returns (address); | ||
function supportsHistory() public pure returns (bool); | ||
|
||
// NOTE: Not implementing the optional functions | ||
// function lastStakedFor(address addr) public view returns (uint256); | ||
// function totalStakedForAt(address addr, uint256 blockNumber) public view returns (uint256); | ||
// function totalStakedAt(uint256 blockNumber) public 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,132 @@ | ||
pragma solidity ^0.4.24; | ||
|
||
import "./ERC900.sol"; | ||
import "../ERC20/ERC20.sol"; | ||
|
||
import "../library/SafeMath.sol"; | ||
|
||
|
||
/** | ||
* @title ERC900BasicStakeContainer | ||
*/ | ||
contract ERC900BasicStakeContainer is ERC900 { | ||
using SafeMath for uint256; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some gas optimization is possible here if we deploy the library separately and then point to it (as opposed to bundling it with both contracts) |
||
|
||
ERC20 stakingToken; | ||
|
||
mapping (address => StakeContainer) addresses; | ||
|
||
struct Stake { | ||
uint256 blockNumber; | ||
uint256 amount; | ||
bool exists; | ||
} | ||
|
||
// To save on gas, rather than create a separate mapping for amountStakedFor & personalStake, | ||
// both data structures are stored in a single mapping for a given addresses. | ||
// | ||
// amountStakedFor consists of all tokens staked for a given address. | ||
// personalStake is the stake made by a given address. | ||
// | ||
// It's possible to have a non-existing personalStake, but have tokens in amountStakedFor | ||
// if other users are staking on behalf of a given address. | ||
struct StakeContainer { | ||
// TODO: This data structure should change to represent "weight" instead of amount | ||
uint256 amountStakedFor; | ||
|
||
Stake personalStake; | ||
} | ||
|
||
modifier noExistingStake(address _address) { | ||
require( | ||
!addresses[_address].personalStake.exists, | ||
"Stake already exists"); | ||
_; | ||
} | ||
|
||
modifier canStake(address _address, uint256 _amount) { | ||
require( | ||
stakingToken.transferFrom(_address, this, _amount), | ||
"Stake required"); | ||
_; | ||
} | ||
|
||
constructor(ERC20 _stakingToken) public { | ||
stakingToken = _stakingToken; | ||
} | ||
|
||
function stake(uint256 _amount, bytes _data) | ||
public | ||
noExistingStake(msg.sender) | ||
canStake(msg.sender, _amount) | ||
{ | ||
addresses[msg.sender].personalStake = Stake(block.number, _amount, true); | ||
addresses[msg.sender].amountStakedFor.add(_amount); | ||
|
||
emit Staked( | ||
msg.sender, | ||
_amount, | ||
totalStakedFor(msg.sender), | ||
_data); | ||
} | ||
|
||
function stakeFor(address _user, uint256 _amount, bytes _data) | ||
public | ||
noExistingStake(msg.sender) | ||
canStake(msg.sender, _amount) | ||
{ | ||
addresses[msg.sender].personalStake = Stake(block.number, _amount, true); | ||
|
||
// Notice here that we are increasing the staked amount for _user | ||
// instead of msg.sender | ||
addresses[_user].amountStakedFor.add(_amount); | ||
|
||
emit Staked( | ||
_user, | ||
_amount, | ||
totalStakedFor(_user), | ||
_data); | ||
} | ||
|
||
function unstake(uint256 _amount, bytes _data) public { | ||
require(addresses[msg.sender].personalStake.exists, "Stake doesn't exist"); | ||
|
||
// Transfer the staked tokens from this contract back tot he sender | ||
// Notice that we are using transfer instead of transferFrom here, so | ||
// no approval is needed before hand. | ||
require( | ||
stakingToken.transfer(msg.sender, _amount), | ||
"Unable to withdraw stake"); | ||
|
||
// If this was a complete withdrawal, then delete the previous stake to reset | ||
// the block number and exists flag | ||
if (addresses[msg.sender].personalStake.amount == 0) { | ||
delete addresses[msg.sender].personalStake; | ||
addresses[msg.sender].amountStakedFor = 0; | ||
} else { | ||
addresses[msg.sender].amountStakedFor.sub(_amount); | ||
} | ||
|
||
emit Unstaked( | ||
msg.sender, | ||
_amount, | ||
totalStakedFor(msg.sender), | ||
_data); | ||
} | ||
|
||
function totalStakedFor(address _address) public view returns (uint256) { | ||
return addresses[_address].amountStakedFor; | ||
} | ||
|
||
function totalStaked() public view returns (uint256) { | ||
return stakingToken.balanceOf(this); | ||
} | ||
|
||
function token() public view returns (address) { | ||
return stakingToken; | ||
} | ||
|
||
function supportsHistory() public pure returns (bool) { | ||
return false; | ||
} | ||
} |
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 @@ | ||
const CodexToken = artifacts.require('./CodexToken.sol') | ||
const ERC900BasicStakeContainer = artifacts.require('./ERC900BasicStakeContainer.sol') | ||
|
||
module.exports = (deployer) => { | ||
deployer.deploy(ERC900BasicStakeContainer, CodexToken.address) | ||
} |
2 changes: 1 addition & 1 deletion
2
migrations/3_deploy_721token.js → migrations/4_deploy_721token.js
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,5 +1,5 @@ | ||
const CodexTitle = artifacts.require('./CodexTitle.sol') | ||
|
||
module.exports = (deployer, network, accounts) => { | ||
module.exports = (deployer) => { | ||
deployer.deploy(CodexTitle) | ||
} |
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
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.
The docs for
transferFrom
sayUsage of this method is discouraged, use
safeTransferFromwhenever possible
which seems to safely handle transfers to contracts. Should we use that instead?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.
We already know at this point the user who sent the tokens can hold tokens, so no harm in using transfer directly here and saving a bit of gas.