Skip to content

Commit e05d4a3

Browse files
authored
Merge pull request #304 from api3dao/small-fixes
Small fixes
2 parents 8560326 + a8008a6 commit e05d4a3

23 files changed

Lines changed: 279 additions & 98 deletions

packages/api3-voting/contracts/Api3Voting.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ contract Api3Voting is IForwarder, AragonApp {
264264
, // lastDelegationUpdateTimestamp
265265
uint256 lastProposalTimestamp
266266
) = api3Pool.getUser(msg.sender);
267-
require(lastProposalTimestamp.add(api3Pool.EPOCH_LENGTH()) < now, "API3_HIT_PROPOSAL_COOLDOWN");
267+
require(lastProposalTimestamp.add(voteTime) < now, "API3_HIT_PROPOSAL_COOLDOWN");
268268
api3Pool.updateLastProposalTimestamp(msg.sender);
269269

270270
uint64 snapshotBlock = getBlockNumber64() - 1; // avoid double voting in this very block

packages/convenience/contracts/Convenience.sol

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ contract Convenience is Ownable {
1616
/// @notice Staking pool of the DAO
1717
IApi3PoolExtended public immutable api3Pool;
1818
/// @notice List of ERC20 addresses that will be displayed in the DAO
19-
/// treasury
19+
/// treasury. The ETH balance will also be displayed by default.
2020
/// @dev These are set by the owner of this contract
2121
address[] public erc20Addresses;
2222
/// @notice Links to the discussion venues for each vote
@@ -74,6 +74,26 @@ contract Convenience is Ownable {
7474

7575
/// @notice Used by the DAO dashboard client to retrieve user staking data
7676
/// @param userAddress User address
77+
/// @return apr Staking reward APR
78+
/// @return api3Supply API3 total supply
79+
/// @return totalStake Total amount staked at the pool
80+
/// @return totalShares Total pool shares (also represents total voting
81+
/// power)
82+
/// @return stakeTarget Pool stake target in percentages
83+
/// @return userApi3Balance User API3 balance
84+
/// @return userStaked Amount of staked tokens the user has at the pool
85+
/// @return userUnstaked Amount of non-staked tokens the user has at the
86+
/// pool
87+
/// @return userVesting Amount of tokens not yet vested to the user (it is
88+
/// not withdrawable, similar to `userLocked`)
89+
/// @return userUnstakeAmount Amount of tokens the user scheduled to
90+
/// unstake
91+
/// @return userUnstakeShares Amount of shares the user gave up to schedule
92+
/// the unstaking
93+
/// @return userUnstakeScheduledFor Time when the scheduled unstake will
94+
/// mature
95+
/// @return userLocked Amount of rewards the user has received that are not
96+
/// withdrawable yet
7797
function getUserStakingData(address userAddress)
7898
external
7999
view
@@ -114,7 +134,24 @@ contract Convenience is Ownable {
114134

115135
/// @notice Used by the DAO dashboard client to retrieve the treasury and
116136
/// user delegation data
137+
/// @dev In addition to the ERC20 tokens, it returns the ETH balances of
138+
/// the treasuries
117139
/// @param userAddress User address
140+
/// @return names ERC20 (+ Ethereum) names
141+
/// @return symbols ERC20 (+ Ethereum) symbols
142+
/// @return decimals ERC20 (+ Ethereum) decimals
143+
/// @return balancesOfPrimaryAgent ERC20 (+ Ethereum) balances of the
144+
/// primary agent
145+
/// @return balancesOfSecondaryAgent ERC20 (+ Ethereum) balances of the
146+
/// secondary agent
147+
/// @return proposalVotingPowerThreshold Proposal voting power threshold in
148+
/// percentages
149+
/// @return userVotingPower Voting power of the user, including delegations
150+
/// @return delegatedToUser Voting power delegated to user
151+
/// @return delegate Address that the user has delegated to
152+
/// @return lastDelegationUpdateTimestamp When the user has last updated
153+
/// their delegation
154+
/// @return lastProposalTimestamp When the user has last made a proposal
118155
function getTreasuryAndUserDelegationData(address userAddress)
119156
external
120157
view
@@ -171,6 +208,17 @@ contract Convenience is Ownable {
171208
/// @param votingAppType Enumerated voting app type (primary or secondary)
172209
/// @param userAddress User address
173210
/// @param voteIds Array of vote IDs for which data will be retrieved
211+
/// @return startDate Start date of the vote
212+
/// @return supportRequired Support required for the vote to pass in
213+
/// percentages
214+
/// @return minAcceptQuorum Minimum acceptance quorum required for the vote
215+
/// to pass in percentages
216+
/// @return votingPower Total voting power at the time the vote was created
217+
/// @return script The EVMScript that will be run if the vote passes
218+
/// @return userVotingPowerAt User's voting power at the time the vote was
219+
/// created
220+
/// @return discussionUrl Discussion URL set for the vote by the contract
221+
/// owner
174222
function getStaticVoteData(
175223
VotingAppType votingAppType,
176224
address userAddress,
@@ -230,6 +278,13 @@ contract Convenience is Ownable {
230278
/// @param votingAppType Enumerated voting app type (primary or secondary)
231279
/// @param userAddress User address
232280
/// @param voteIds Array of vote IDs for which data will be retrieved
281+
/// @return executed If the vote has been executed
282+
/// @return yea Total voting power voted for "For"
283+
/// @return nay Total voting power voted for "Against"
284+
/// @return voterState Vote cast by the user
285+
/// @return delegateAt Address the user has delegated to at the time the
286+
/// vote was created
287+
/// @return delegateState Vote cast by the delegate of the user
233288
function getDynamicVoteData(
234289
VotingAppType votingAppType,
235290
address userAddress,
@@ -268,7 +323,7 @@ contract Convenience is Ownable {
268323
, // open
269324
executed[i],
270325
, // startDate
271-
snapshotBlock ,
326+
snapshotBlock,
272327
, // supportRequired
273328
, // minAcceptQuorum
274329
yea[i],

packages/convenience/test/Convenience.sol.js

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -356,25 +356,25 @@ describe("getTreasuryAndUserDelegationData", function () {
356356
await erc20Tokens[i].balanceOf(await api3Pool.agentAppSecondary())
357357
);
358358
}
359-
expect(TreasuryAndUserDelegationData.names[erc20Tokens.length]).to.equal(
360-
"Ethereum"
361-
);
362-
expect(TreasuryAndUserDelegationData.symbols[erc20Tokens.length]).to.equal(
363-
"ETH"
364-
);
365-
expect(TreasuryAndUserDelegationData.decimals[erc20Tokens.length]).to.equal(
366-
18
367-
);
368359
expect(
369-
TreasuryAndUserDelegationData.balancesOfPrimaryAgent[erc20Tokens.length]
370-
).to.equal(
371-
ethers.utils.parseEther("12")
372-
);
360+
TreasuryAndUserDelegationData.names[erc20Tokens.length]
361+
).to.equal("Ethereum");
373362
expect(
374-
TreasuryAndUserDelegationData.balancesOfSecondaryAgent[erc20Tokens.length]
375-
).to.equal(
376-
ethers.utils.parseEther("34")
377-
);
363+
TreasuryAndUserDelegationData.symbols[erc20Tokens.length]
364+
).to.equal("ETH");
365+
expect(
366+
TreasuryAndUserDelegationData.decimals[erc20Tokens.length]
367+
).to.equal(18);
368+
expect(
369+
TreasuryAndUserDelegationData.balancesOfPrimaryAgent[
370+
erc20Tokens.length
371+
]
372+
).to.equal(ethers.utils.parseEther("12"));
373+
expect(
374+
TreasuryAndUserDelegationData.balancesOfSecondaryAgent[
375+
erc20Tokens.length
376+
]
377+
).to.equal(ethers.utils.parseEther("34"));
378378

379379
expect(
380380
TreasuryAndUserDelegationData.proposalVotingPowerThreshold

packages/dao/scripts/deploy.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ module.exports = async (callback) => {
9090
[supportRequiredPct2, minAcceptQuorumPct2],
9191
api3VotingAppId
9292
);
93+
const dao = getEventArgument(tx, "Api3DaoDeployed", "dao");
94+
const acl = getEventArgument(tx, "Api3DaoDeployed", "acl");
9395
const primaryVoting = getEventArgument(
9496
tx,
9597
"Api3DaoDeployed",
@@ -126,6 +128,8 @@ module.exports = async (callback) => {
126128
timelockManager: timelockManager.address,
127129
api3Pool: api3Pool.address,
128130
convenience: convenience.address,
131+
dao: dao,
132+
acl: acl,
129133
votingAppPrimary: getEventArgument(
130134
set_tx,
131135
"SetDaoApps",

packages/pool/contracts/ClaimUtils.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ abstract contract ClaimUtils is StakeUtils, IClaimUtils {
4444
assert(api3Token.transfer(recipient, amount));
4545
emit PaidOutClaim(
4646
recipient,
47-
amount
47+
amount,
48+
totalStake
4849
);
4950
}
5051
}

packages/pool/contracts/DelegationUtils.sol

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,10 @@ abstract contract DelegationUtils is RewardUtils, IDelegationUtils {
5353
}
5454

5555
// Assign the new delegation
56+
uint256 delegatedToUpdate = delegatedToUser(delegate) + userShares;
5657
updateCheckpointArray(
5758
users[delegate].delegatedTo,
58-
delegatedToUser(delegate) + userShares
59+
delegatedToUpdate
5960
);
6061

6162
// Record the new delegate for the user
@@ -66,7 +67,8 @@ abstract contract DelegationUtils is RewardUtils, IDelegationUtils {
6667
emit Delegated(
6768
msg.sender,
6869
delegate,
69-
userShares
70+
userShares,
71+
delegatedToUpdate
7072
);
7173
}
7274

@@ -89,9 +91,10 @@ abstract contract DelegationUtils is RewardUtils, IDelegationUtils {
8991
user.lastDelegationUpdateTimestamp = block.timestamp;
9092

9193
uint256 userShares = userShares(msg.sender);
94+
uint256 delegatedToUpdate = delegatedToUser(previousDelegate) - userShares;
9295
updateCheckpointArray(
9396
users[previousDelegate].delegatedTo,
94-
delegatedToUser(previousDelegate) - userShares
97+
delegatedToUpdate
9598
);
9699
updateAddressCheckpointArray(
97100
user.delegates,
@@ -100,7 +103,8 @@ abstract contract DelegationUtils is RewardUtils, IDelegationUtils {
100103
emit Undelegated(
101104
msg.sender,
102105
previousDelegate,
103-
userShares
106+
userShares,
107+
delegatedToUpdate
104108
);
105109
}
106110

@@ -122,17 +126,19 @@ abstract contract DelegationUtils is RewardUtils, IDelegationUtils {
122126
return;
123127
}
124128
uint256 currentDelegatedTo = delegatedToUser(delegate);
125-
uint256 newDelegatedTo = delta
129+
uint256 delegatedToUpdate = delta
126130
? currentDelegatedTo + shares
127131
: currentDelegatedTo - shares;
128132
updateCheckpointArray(
129133
users[delegate].delegatedTo,
130-
newDelegatedTo
134+
delegatedToUpdate
131135
);
132-
emit Delegated(
136+
emit UpdatedDelegation(
133137
msg.sender,
134138
delegate,
135-
newDelegatedTo
139+
delta,
140+
shares,
141+
delegatedToUpdate
136142
);
137143
}
138144
}

packages/pool/contracts/RewardUtils.sol

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,22 @@ abstract contract RewardUtils is GetterUtils, IRewardUtils {
2222
if (api3Token.getMinterStatus(address(this)))
2323
{
2424
uint256 rewardAmount = totalStake * apr * EPOCH_LENGTH / 365 days / HUNDRED_PERCENT;
25+
assert(block.number <= MAX_UINT32);
26+
assert(rewardAmount <= MAX_UINT224);
2527
epochIndexToReward[currentEpoch] = Reward({
2628
atBlock: uint32(block.number),
2729
amount: uint224(rewardAmount),
28-
totalSharesThen: totalShares()
30+
totalSharesThen: totalShares(),
31+
totalStakeThen: totalStake
2932
});
3033
api3Token.mint(address(this), rewardAmount);
3134
totalStake += rewardAmount;
3235
updateCurrentApr();
3336
emit MintedReward(
3437
currentEpoch,
3538
rewardAmount,
36-
apr
39+
apr,
40+
totalStake
3741
);
3842
}
3943
epochIndexOfLastReward = currentEpoch;

packages/pool/contracts/StakeUtils.sol

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,24 @@ abstract contract StakeUtils is TransferUtils, IStakeUtils {
2121
user.unstaked -= amount;
2222
uint256 totalSharesNow = totalShares();
2323
uint256 sharesToMint = amount * totalSharesNow / totalStake;
24-
uint256 userSharesNow = userShares(msg.sender);
24+
uint256 userSharesUpdate = userShares(msg.sender) + sharesToMint;
2525
updateCheckpointArray(
2626
user.shares,
27-
userSharesNow + sharesToMint
27+
userSharesUpdate
2828
);
29+
uint256 totalSharesUpdate = totalSharesNow + sharesToMint;
2930
updateCheckpointArray(
3031
poolShares,
31-
totalSharesNow + sharesToMint
32+
totalSharesUpdate
3233
);
3334
totalStake += amount;
3435
updateDelegatedVotingPower(sharesToMint, true);
3536
emit Staked(
3637
msg.sender,
3738
amount,
38-
sharesToMint
39+
sharesToMint,
40+
userSharesUpdate,
41+
totalSharesUpdate
3942
);
4043
}
4144

@@ -91,23 +94,24 @@ abstract contract StakeUtils is TransferUtils, IStakeUtils {
9194
user.unstakeScheduledFor = unstakeScheduledFor;
9295
user.unstakeAmount = amount;
9396
user.unstakeShares = sharesToUnstake;
97+
uint256 userSharesUpdate = userSharesNow - sharesToUnstake;
9498
updateCheckpointArray(
9599
user.shares,
96-
userSharesNow - sharesToUnstake
100+
userSharesUpdate
97101
);
98102
updateDelegatedVotingPower(sharesToUnstake, false);
99103
emit ScheduledUnstake(
100104
msg.sender,
101105
amount,
102106
sharesToUnstake,
103-
unstakeScheduledFor
107+
unstakeScheduledFor,
108+
userSharesUpdate
104109
);
105110
}
106111

107112
/// @notice Called to execute a pre-scheduled unstake
108-
/// @dev Note that anyone can execute a matured unstake. This is to allow
109-
/// the user to use bots, etc. to execute their unstaking as soon as
110-
/// possible.
113+
/// @dev Anyone can execute a matured unstake. This is to allow the user to
114+
/// use bots, etc. to execute their unstaking as soon as possible.
111115
/// @param userAddress User address
112116
/// @return Amount of tokens that are unstaked
113117
function unstake(address userAddress)
@@ -128,31 +132,37 @@ abstract contract StakeUtils is TransferUtils, IStakeUtils {
128132
uint256 totalShares = totalShares();
129133
uint256 unstakeAmount = user.unstakeAmount;
130134
uint256 unstakeAmountByShares = user.unstakeShares * totalStake / totalShares;
131-
// If there was a claim payout in between the scheduling and the actual unstake
132-
// then the amount might be lower than expected at scheduling time
135+
// If there was a claim payout in between the scheduling and the actual
136+
// unstake then the amount might be lower than expected at scheduling
137+
// time
133138
if (unstakeAmount > unstakeAmountByShares)
134139
{
135140
unstakeAmount = unstakeAmountByShares;
136141
}
137142
user.unstaked += unstakeAmount;
138143

144+
uint256 totalSharesUpdate = totalShares - user.unstakeShares;
139145
updateCheckpointArray(
140146
poolShares,
141-
totalShares - user.unstakeShares
147+
totalSharesUpdate
142148
);
143149
totalStake -= unstakeAmount;
144150

145151
user.unstakeAmount = 0;
146152
user.unstakeShares = 0;
147153
user.unstakeScheduledFor = 0;
148-
emit Unstaked(userAddress, unstakeAmount);
154+
emit Unstaked(
155+
userAddress,
156+
unstakeAmount,
157+
totalSharesUpdate
158+
);
149159
return unstakeAmount;
150160
}
151161

152162
/// @notice Convenience method to execute an unstake and withdraw to the
153163
/// user's wallet in a single transaction
154-
/// @dev Note that the withdrawal may revert because the user may have less
155-
/// than `unstaked` tokens that are withdrawable
164+
/// @dev The withdrawal will revert if the user has less than
165+
/// `unstakeAmount` tokens that are withdrawable
156166
function unstakeAndWithdraw()
157167
external
158168
override

0 commit comments

Comments
 (0)