-
Notifications
You must be signed in to change notification settings - Fork 1
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
Contracts first version #13
base: master
Are you sure you want to change the base?
Conversation
Closes #3. Besides, add CI.
Now Voting app can resolve challenge when vote is over without the need for another call from Curation app.
`manifest.json` and `arapp.json`
0e28030
to
8bf6651
Compare
Move IStaking to @aragon/apps-staking
Staking app registers locks per user, not globally.
Challenge amount was always being used. Although it's usually going to be the same (min deposit), it was not correct.
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.
WIP review
contracts/Curation.sol
Outdated
mapping(bytes32 => Challenge) challenges; | ||
mapping(address => mapping(uint256 => bool)) usedLocks; | ||
|
||
event NewApplication(bytes32 entryId, address applicant); |
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 should index entryId
s
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.
Btw, there already was an issue for this, #12 , I just forgot ;-) I'm going to add it to the description so it's closed automatically.
contracts/Curation.sol
Outdated
uint64 date; | ||
bool resolved; | ||
uint256 amount; | ||
uint256 lockId; |
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 may run into issues with lockIds
changing as when you remove a lock in the staking app the ids can change: https://github.com/aragon/aragon-apps/blob/b968e3ec9ab70cf6cfbfa5ebca8a58460669e7d1/future-apps/staking/contracts/Staking.sol#L173-L178
If a user has an unrelated lock with id = 0 and then creates a lock for creating a challenge with id = 1, if the lock 0 gets unlocked before the challenge is resolved, lock 1 will be lock 0 and unlocking will fail as there is no lock 1.
I think this issue may apply to PLCR voting as well. We may need to rethink how locks get stored in the Staking app.
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, since lock ids are exposed to the outside world, they should be immutable.
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.
Created aragon/aragon-apps#406 for this
contracts/Curation.sol
Outdated
|
||
function _checkLock(address user, uint256 lockId, uint64 date) internal returns (uint256) { | ||
// check lockId was not used before | ||
require(!usedLocks[user][lockId]); |
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.
Even though we can be fairly sure that the staking app is not malicious and it is not going to re-enter, we can have this check after the staking.getLock(msg.sender, lockId)
to make sure the storage is correct after the external call.
Ideally we would do a STATICCALL
but it is too cumbersome in this version of solidity.
contracts/Curation.sol
Outdated
if (application.amount < minDeposit) { | ||
registry.remove(entryId); | ||
staking.unlock(application.applicant, application.lockId); | ||
staking.unlock(msg.sender, lockId); |
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.
Why do we require a having a lock for this case if the challenge is immediately resolved?
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.
Good point!
contracts/Curation.sol
Outdated
// check application doesn't have an ongoing challenge | ||
require(!challengeExists(entryId)); | ||
// check locked tokens | ||
uint256 amount = _checkLock(msg.sender, lockId, getTimestamp().add(applyStageLen)); |
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.
This amount is never checked (it is just saved in the Challenge struct) so I assume there is no minimum amount to stake in order to challenge. Couldn't this become a spam issue in which someone can just challenge all applications without having to stake and risk virtually any money?
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.
It's checked inside _checkLock
.
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.
Totally missed it, 👍
contracts/Curation.sol
Outdated
assembly { | ||
mstore(add(executionScript, 0x20), spec) | ||
mstore(add(targetBytes, 0x20), target) | ||
mstore(add(executionScript, 0x24), mload(add(targetBytes, 0x2C))) |
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.
Why not store the target address directly here?
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.
It would be better to just mload(target)
, shift the required bits and mstore that. Otherwise we are reading from unknown memory and that's a little scary.
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 don't get why we are reading from unknown memory, but my assembly understanding is limited yet. Anyway, you are right, this way is too messy, I did it as you say, just shifting the address, way better. Thanks!
contracts/Curation.sol
Outdated
} | ||
} | ||
|
||
function registerApplication(bytes32 entryId) isInitialized public { |
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.
If this function can only be used for unchallenged applications, I would rename to signal that: registerUnchallengedApplication(bytes32 entryId)
contracts/Curation.sol
Outdated
|
||
// insert in Registry app | ||
registry.add(application.data); | ||
application.registered = true; |
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.
Is there a way to remove an entry once registered and recover the deposit?
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.
Good point, not right now. This possibility doesn't appear in any of the documents (white paper, requirements or specs) as far as I remember, but I think it makes total sense. I'll add it, but also pinging @lkngtn so he is aware of this use case.
contracts/Curation.sol
Outdated
// Unlock challenger tokens from Staking app | ||
reward = challenge.amount.mul(dispensationPct) / PCT_BASE; | ||
// Redistribute tokens | ||
staking.unlockAndMoveTokens(challenge.challenger, challenge.lockId, application.applicant, reward); |
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.
Also an issue with staking, but it would be more descriptive if the function signature referenced the fact that the unlock is partial: staking.partialUnlockAndMove(...)
contracts/Curation.sol
Outdated
// amount * (voter / total) * (1 - dispensationPct) | ||
uint256 reward = amount.mul(voterWinningStake).mul(PCT_BASE.sub(dispensationPct)) / (totalWinningStake * PCT_BASE); | ||
// Redistribute tokens | ||
staking.unlockAndMoveTokens(loser, loserLockId, msg.sender, reward); |
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 think it would be cleaner if the curation app just took all the tokens from the challenger or applicant onresolveChallenge(...)
. This way we don't need to modify the lock every time, being more efficient.
Also in terms of the user that lost some tokens, I think it is better to remove them from their account ASAP as there is no way they are going to recover them.
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.
Completely agreed. Plus:
- It would solve the
TODO
3 lines below about that truncating issue. - It would follow the same logic as our PLCR
Fixes #12 (per review). |
Fixes #12 too.
053ee46
to
6285b4e
Compare
7436fbb
to
db8d13f
Compare
* @param _voting New Voting app | ||
*/ | ||
function setVotingApp(IVoting _voting) authP(CHANGE_VOTING_APP_ROLE, arr(voting, _voting)) public { | ||
_setVotingApp(_voting); |
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.
This is quite dangerous if there are any challenges open, as they don't have a reference to the voting app, but rely on the global voting instance. I would remove the ability to hot swap the voting app after initialization.
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.
Good catch!!
mstore(add(executionScript, 0x40), entryId) | ||
} | ||
|
||
uint256 voteId = voting.newVote(executionScript, ""); |
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.
Note: currently PLCR is not executing scripts https://github.com/aragonlabs/plcr/pull/1/files#r207182982
} | ||
|
||
// check locked tokens | ||
uint256 amount = _checkLock(msg.sender, lockId, getTimestamp(), getTimestamp().add(applyStageLen)); |
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 end date should be when the voting ends, but since we don't have any guarantee that resolveChallenge(...)
will be called at that point, the lock shouldn't have an end date to avoid someone pulling the tokens deposited for the challenge before it can be resolved.
|
||
// Remove from Registry app | ||
application.registered = false; | ||
registry.remove(entryId); |
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 should have special logic for whether the application was already in the registry or the challenge is done during the applyStageLen
period. This isn't causing a crash because the registry doesn't care whether an entry was present in the registry before attempting to delete it: https://github.com/aragonlabs/registry/pull/3/files#r207185327
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, actually I think that's the reason I was not doing those checks on Registry app: it was making things easier here, as the operation would become "idempotent", i.e., it doesn't matter what's the state, the result will always be the desired (in this case, having the entry out of the registry).
vote.votersRewardPool = amount - reward; | ||
|
||
// unlock tokens from Staking app | ||
staking.unlock(application.applicant, application.lockId); |
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.
If the application enters the registry we shouldn't remove its lock
|
||
// Remove challenge, and application if needed | ||
if (vote.result == true) { | ||
delete(applications[entryId]); |
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.
Removing both application and challenge from storage once resolved is going to make it real hard to get historical data from the app. This would be a major change to how data is stored here, but let's think about it.
// rewardPool * (voter / total) | ||
uint256 reward = vote.votersRewardPool.mul(voterWinningStake) / vote.totalWinningStake; | ||
// Redistribute tokens | ||
staking.moveTokens(address(this), msg.sender, reward); |
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.
Should emit an event as PLCR does when tokens are claimed
if (!application.registered) { | ||
application.registered = true; | ||
// insert in Registry app | ||
registry.add(application.data); |
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.
Every time an application is registered or removed, the curation app should emit an event. With the current state in which we remove application and challenge data right away, it would be very hard for observers of the contract what is going on
} | ||
|
||
function _setMinDeposit(uint256 _minDeposit) internal { | ||
minDeposit = _minDeposit; |
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.
Please log when any of the params change.
import "./interfaces/IVoting.sol"; | ||
|
||
|
||
contract Curation is AragonApp { |
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 should consider all the cases in which locks are created to the curation app that may result in tokens getting locked forever, for example, locking tokens to create an application or challenge and being frontrun in the action. We need a mechanism to unlock when the locks haven't been used.
Comment on the topic for PLCR: https://github.com/aragonlabs/plcr/pull/1/files#r206916590
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, another related example: #16
Right now nobody is winning the Vote, so tokens are being locked.
First version of contracts and tests.
Fixes #1