Conversation
| import 'zeppelin-solidity/contracts/token/MintableToken.sol'; | ||
|
|
||
| contract KeepToken is MintableToken { | ||
| string public name = "KEEP TOKEN"; |
| @@ -0,0 +1,50 @@ | |||
| pragma solidity ^0.4.18; | |||
|
|
|||
| import 'zeppelin-solidity/contracts/token/MintableToken.sol'; | |||
There was a problem hiding this comment.
Do we want this to be mintable if it has a fixed supply?
There was a problem hiding this comment.
yeah I think it may put off some investors knowing that more tokens can be issued, I'll remove
|
|
||
| mapping(address => uint256) stakeBalances; | ||
|
|
||
| event StakeIn(address indexed from, uint256 value); |
There was a problem hiding this comment.
I think Stake and Unstake might make more sense as event names?
| function stakeOut(uint256 _value) public returns (bool) { | ||
| require(_value <= stakeBalances[msg.sender]); | ||
|
|
||
| stakeBalances[msg.sender] = stakeBalances[msg.sender].sub(_value); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
| * @param _value The amount to be staked in | ||
| */ | ||
| function stakeIn(uint256 _value) public returns (bool) { | ||
| require(_value <= balances[msg.sender]); |
There was a problem hiding this comment.
What if msg.sender has no balance?
There was a problem hiding this comment.
(Easy to imagine the mapping defaults uint256s to 0, just confirming)
There was a problem hiding this comment.
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
| string public symbol = "KEEP"; | ||
| uint256 public decimals = 18; | ||
|
|
||
| mapping(address => uint256) stakeBalances; |
There was a problem hiding this comment.
This won't work with https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/TokenVesting.sol as is
|
Oh- and tests! Tests, tests, tests! |
|
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 ) |
|
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? |
|
@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 :) |
|
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. |
good idea! updated |
|
🙇 |
|
@ngrinkevich can you put your PGP key on GH? I think you have code signing but no verified key on your account |
|
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 |
yeah I agree if we plan upgrades that definitely a must |
|
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 |
|
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.
There is a known issue with this approve/transferFrom https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit#heading=h.m9fhqynw2xvt
|
|
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? |
|
@mhluongo Yes 👍 |
| gas: 6000000 | ||
| }, | ||
| testnet: { | ||
| host: "10.51.244.207", |
There was a problem hiding this comment.
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 :)
|
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 |
|
Added an issue |
|
@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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
yeah it's only "shoot themselves in the foot" scenarios:
-
sendercan approve transfer only of his tokens to any_spendercontract address. It's basically the same assendercan transfer his tokens to any contract address. -
_spendercontract might not comply withtokenRecipientinterface i.e. doesn't havereceiveApproval()function, in that case_spenderstill 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]; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) ...
There was a problem hiding this comment.
Would be interested in @mhluongo 's thoughts on this one.
| // Cleanup. | ||
| delete withdrawals[_id]; | ||
|
|
||
| FinishedUnstake(); |
There was a problem hiding this comment.
Would it make sense to carry the id with this event? Can't remember if I already asked this heh.
There was a problem hiding this comment.
I'm thinking you won't get data with that id anymore, but might be useful just in case, I'll update
| @@ -0,0 +1 @@ | |||
| ## KEEP Network smart contracts | |||
There was a problem hiding this comment.
This should be “Keep Network”. KEEP will only ever be used to refer to the token.
Uh oh!
There was an error while loading. Please reload this page.