Skip to content

Add KEEP token contract 🚀#10

Merged
mhluongo merged 45 commits into
masterfrom
contracts
Feb 28, 2018
Merged

Add KEEP token contract 🚀#10
mhluongo merged 45 commits into
masterfrom
contracts

Conversation

@ngrinkevich
Copy link
Copy Markdown
Contributor

@ngrinkevich ngrinkevich commented Nov 16, 2017

  • Support for vesting grants
  • Support for staking. Include a collection of each staker's public address and how much they've staked.
  • Support for staking vested balances
  • Withdrawal delay for staking
  • Withdrawal delay for staking vested tokens
  • Revoke for vesting
  • Solidity unit tests
  • Truffle e2e tests for contracts
  • Make it easy / efficient to refer to a staker outside the staking contract

@ngrinkevich ngrinkevich requested a review from mhluongo November 16, 2017 18:41
Comment thread solidity/contracts/KeepToken.sol Outdated
import 'zeppelin-solidity/contracts/token/MintableToken.sol';

contract KeepToken is MintableToken {
string public name = "KEEP TOKEN";
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this be all caps?

Comment thread solidity/contracts/KeepToken.sol Outdated
@@ -0,0 +1,50 @@
pragma solidity ^0.4.18;

import 'zeppelin-solidity/contracts/token/MintableToken.sol';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we want this to be mintable if it has a fixed supply?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeah I think it may put off some investors knowing that more tokens can be issued, I'll remove

Comment thread solidity/contracts/KeepToken.sol Outdated

mapping(address => uint256) stakeBalances;

event StakeIn(address indexed from, uint256 value);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think Stake and Unstake might make more sense as event names?

Comment thread solidity/contracts/KeepToken.sol Outdated
function stakeOut(uint256 _value) public returns (bool) {
require(_value <= stakeBalances[msg.sender]);

stakeBalances[msg.sender] = stakeBalances[msg.sender].sub(_value);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We want a configurable withdrawal delay for staking, ideally in a separate, reusable token contract that KEEP can inherit from.

Eg, StakableToken, with a function initiateUnstake that starts the delay timer, and another, finishUnstake... or similar. Thoughts on terminology @Shadowfiend?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

finishUnstake would be internal or invokable on-chain? The terminology makes sense to me in general, assuming I've correctly understood that this is to implement the functionality for someone trying to wind down their participation in the relay/keep network.

Comment thread solidity/contracts/KeepToken.sol Outdated
* @param _value The amount to be staked in
*/
function stakeIn(uint256 _value) public returns (bool) {
require(_value <= balances[msg.sender]);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What if msg.sender has no balance?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(Easy to imagine the mapping defaults uint256s to 0, just confirming)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yep, defaults to 0
Mappings can be seen as hash tables which are virtually initialized such that every possible key exists and is mapped to a value whose byte-representation is all zeros.
from http://solidity.readthedocs.io/en/develop/introduction-to-smart-contracts.html

Comment thread solidity/contracts/KeepToken.sol Outdated
string public symbol = "KEEP";
uint256 public decimals = 18;

mapping(address => uint256) stakeBalances;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@mhluongo
Copy link
Copy Markdown
Member

Oh- and tests! Tests, tests, tests!

@mhluongo mhluongo requested a review from Shadowfiend November 20, 2017 04:14
@mhluongo
Copy link
Copy Markdown
Member

mhluongo commented Nov 20, 2017

Benefit of separating any token changes into their own contract that we inherit from- we can submit them upstream if they're generally useful.

@ngrinkevich
Copy link
Copy Markdown
Contributor Author

Benefit of separating any token changes into their own contract that we inherit from- we can submit them upstream if they're generally useful.

yep, splitting Staking into separate stakable.sol looks easy enough, just need to add configurable withdrawal.

Tricky part is how to neatly plugin vesting functionality, but I'm sure I'll figure it out pretty soon )

@Shadowfiend
Copy link
Copy Markdown
Contributor

Just to check, is our goal here a long-term PR that will eventually merge the full Solidity token contract, or is the goal rather to do small PRs that slowly move us closer to the overall token contract?

@ngrinkevich
Copy link
Copy Markdown
Contributor Author

@Shadowfiend long-term PR I think suits better here if thats ok. Code may change quite a bit in the process. It's not straight forward development as I initially thought :)

@Shadowfiend
Copy link
Copy Markdown
Contributor

I'm down with that. It may help to add some checkboxes to the description with the milestones we're trying to hit in that case, so we can have a clear sense of where we are with respect to our final merge goals.

@ngrinkevich
Copy link
Copy Markdown
Contributor Author

It may help to add some checkboxes to the description with the milestones we're trying to hit in that case, so we can have a clear sense of where we are with respect to our final merge goals.

good idea! updated

@Shadowfiend
Copy link
Copy Markdown
Contributor

🙇

@mhluongo
Copy link
Copy Markdown
Member

@ngrinkevich can you put your PGP key on GH? I think you have code signing but no verified key on your account

@mhluongo
Copy link
Copy Markdown
Member

Overarching concern here- we want to be able to upgrade the staking contract without upgrading the token contract (as upgrading the token contract could mean the team can inflate the supply, etc). That means breaking staking / vesting out of the token contract and into their own

@ngrinkevich
Copy link
Copy Markdown
Contributor Author

That means breaking staking / vesting out of the token contract and into their own

yeah I agree if we plan upgrades that definitely a must
good news I've been working on solutions already will show something on Monday

@mhluongo
Copy link
Copy Markdown
Member

Awesome. I've also started thinking about how the threshold relay verification and MPC can slash or redistribute stakes when people misbehave- when we add that to staking we'll need a way to set the "stake controller" contract address that's distinct from the owner. That contract will be allowed to "burn" staked coins, and move staked coins between addresses. I figure we close out this PR before we worry about that, but your call

@ngrinkevich
Copy link
Copy Markdown
Contributor Author

Nearly done on the contracts split up, quick info on this:

Initially I thought staking contract can call KEEP token transfer() on behalf of the user who calls staking contract, but turned out can't use msg.sender since in that case it becomes staking contract address, some examples on the internet use tx.origin instead which represents the initial user in this chain call, but at the same time some say its considered bad practice and that tx.origin somehow can be spoofed.

So seems like we’re back to 2 steps process, we call "approve” step on KEEP token first and then stake, i.e.

  • user call token contract approve() function with the amount and the address of the staking contract
  • user call stake() on staking contract with the amount, we then do aditional checks for approved amount and modify the balances etc...

There is a known issue with this approve/transferFrom https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit#heading=h.m9fhqynw2xvt
but looks like easily can be avoided by reducing the spender's allowance to 0 and set the desired value afterwards also another way to mitigate the threat is

to approve token transfers only to smart contracts with verified source code that does not contain logic for performing attacks like described above, and to accounts owned by the people you may trust.

@mhluongo
Copy link
Copy Markdown
Member

Let's get this out. @Shadowfiend what do you think about Nik presenting this to the rest of the team on a call and doing one last round of review?

@Shadowfiend
Copy link
Copy Markdown
Contributor

@mhluongo Yes 👍

Comment thread solidity/truffle.js Outdated
gas: 6000000
},
testnet: {
host: "10.51.244.207",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like something we won't want in a public repo… If that's correct, let's:
(a) change the IP and
(b) use a hostname

If that's not correct, then never mind :)

@Shadowfiend
Copy link
Copy Markdown
Contributor

One note here: we've discussed the staking registry containing a public key. We'll need to either include that in the staking contract, or maintain a separate registry address => public key somewhere that references the staking registry, I guess?

@mhluongo
Copy link
Copy Markdown
Member

mhluongo commented Feb 6, 2018

Added an issue

@Shadowfiend
Copy link
Copy Markdown
Contributor

@ngrinkevich I think all I'm waiting on to approve here is a response on my last note. If you can get to that before anything else, we can merge this PR and start basing stuff (e.g. the pubkey/stake registry work) on master.

* @param _extraData Extra information to send to the approved contract.
*/
function approveAndCall(address _spender, uint256 _value, bytes _extraData) public returns (bool success) {
tokenRecipient spender = tokenRecipient(_spender);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just to double-check here, there's no guarantee that _spender will actually conform to tokenRecipient, right? Is there a danger anyone can do anything weird other than shoot themselves in the foot? I assume not because approve will ensure they can only spend their own tokens.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeah it's only "shoot themselves in the foot" scenarios:

  • sender can approve transfer only of his tokens to any _spender contract address. It's basically the same as sender can transfer his tokens to any contract address.

  • _spender contract might not comply with tokenRecipient interface i.e. doesn't have receiveApproval() function, in that case _spender still authorized to spend amount manually. So responsibility is for the sender where he's sending tokens

function getGrants(address _beneficiary) public constant returns (uint256[]) {
return grantIndices[_beneficiary];
function getGrants(address _beneficiaryOrCreator) public constant returns (uint256[]) {
return grantIndices[_beneficiaryOrCreator];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we be tracking these separately? In particular, should we require that you call different functions to get grants you made vs grants you received, rather than using one function for both?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeah was my thought initially but then I was doing web UI dashboard and it seemed easier to getGrants in one call and filter by creator/beneficiary on the frontend since grant struct clearly differentiates beneficiary/creator, but maybe I'm trying to be too clever haha

Separating looks bit more code, I'm 50/50 on this let me know what you think and I'll update to something like this

grantCreatorIndices[_address];
grantBeneficiaryIndices[_address];
function getGrantsByCreator(address _address) ...
function getGrantsByBeneficiary(address _address) ...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would be interested in @mhluongo 's thoughts on this one.

Comment thread solidity/contracts/TokenStaking.sol Outdated
// Cleanup.
delete withdrawals[_id];

FinishedUnstake();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would it make sense to carry the id with this event? Can't remember if I already asked this heh.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm thinking you won't get data with that id anymore, but might be useful just in case, I'll update

Comment thread solidity/README.md Outdated
@@ -0,0 +1 @@
## KEEP Network smart contracts
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This should be “Keep Network”. KEEP will only ever be used to refer to the token.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants