Skip to content

Commit 884a9a2

Browse files
use transcoder -> rewardCaller mapping direction to prevent frontruns
1 parent 5bce7ca commit 884a9a2

File tree

3 files changed

+71
-77
lines changed

3 files changed

+71
-77
lines changed

contracts/bonding/BondingManager.sol

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ contract BondingManager is ManagerProxyTarget, IBondingManager {
102102
// If the balance of the treasury in LPT is above this value, automatic treasury contributions will halt.
103103
uint256 public treasuryBalanceCeiling;
104104

105-
// Allow reward() calls by pre-defined set of addresses
106-
mapping(address => address) private rewardCallerToTranscoder;
105+
// Allow reward() calls from one pre-defined address per transcoder
106+
mapping(address => address) private transcoderToRewardCaller;
107107

108108
// Check if sender is TicketBroker
109109
modifier onlyTicketBroker() {
@@ -194,23 +194,13 @@ contract BondingManager is ManagerProxyTarget, IBondingManager {
194194
/**
195195
* @notice Set a reward caller for a transcoder
196196
* @param _rewardCaller Address of the new reward caller
197+
* @dev By providing address(0) the reward caller can be unset
197198
*/
198199
function setRewardCaller(address _rewardCaller) external whenSystemNotPaused {
199-
require(rewardCallerToTranscoder[_rewardCaller] == address(0), "reward caller is already set");
200-
rewardCallerToTranscoder[_rewardCaller] = msg.sender;
200+
transcoderToRewardCaller[msg.sender] = _rewardCaller;
201201
emit RewardCallerSet(msg.sender, _rewardCaller);
202202
}
203203

204-
/**
205-
* @notice Unset a reward caller for a transcoder
206-
* @param _rewardCaller Address of the existing reward caller
207-
*/
208-
function unsetRewardCaller(address _rewardCaller) external whenSystemNotPaused {
209-
require(rewardCallerToTranscoder[_rewardCaller] == msg.sender, "only relevant transcoder can unset");
210-
rewardCallerToTranscoder[_rewardCaller] = address(0);
211-
emit RewardCallerUnset(msg.sender, _rewardCaller);
212-
}
213-
214204
/**
215205
* @notice Sets commission rates as a transcoder and if the caller is not in the transcoder pool tries to add it
216206
* @dev Percentages are represented as numerators of fractions over MathUtils.PERC_DIVISOR
@@ -317,6 +307,15 @@ contract BondingManager is ManagerProxyTarget, IBondingManager {
317307
rewardWithHint(address(0), address(0));
318308
}
319309

310+
/**
311+
* @notice Mint token rewards for an active transcoder and its delegators
312+
* @param _transcoder Address of the transcoder on behalf of which the reward is called
313+
* @dev Only callable by trusted rewardCaller
314+
*/
315+
function rewardForTranscoder(address _transcoder) external {
316+
rewardForTranscoderWithHint(_transcoder, address(0), address(0));
317+
}
318+
320319
/**
321320
* @notice Update transcoder's fee pool. Only callable by the TicketBroker
322321
* @param _transcoder Transcoder address
@@ -884,25 +883,53 @@ contract BondingManager is ManagerProxyTarget, IBondingManager {
884883
* @param _newPosPrev Address of previous transcoder in pool if the caller is in the pool
885884
* @param _newPosNext Address of next transcoder in pool if the caller is in the pool
886885
*/
887-
function rewardWithHint(address _newPosPrev, address _newPosNext)
888-
public
889-
whenSystemNotPaused
890-
currentRoundInitialized
891-
autoCheckpoint(msg.sender)
892-
{
886+
function rewardWithHint(address _newPosPrev, address _newPosNext) public {
887+
_rewardWithHint(msg.sender, _newPosPrev, _newPosNext);
888+
}
889+
890+
/**
891+
* @notice Mint token rewards for an active transcoder and its delegators and update the transcoder pool using an optional list hint if needed
892+
* @dev If the caller is in the transcoder pool, the caller can provide an optional hint for its insertion position in the
893+
* pool via the `_newPosPrev` and `_newPosNext` params. A linear search will be executed starting at the hint to find the correct position.
894+
* In the best case, the hint is the correct position so no search is executed. See SortedDoublyLL.sol for details on list hints
895+
* @dev Only callable by trusted rewardCaller
896+
* @param _transcoder Address of the transcoder on behalf of which the reward is called
897+
* @param _newPosPrev Address of previous transcoder in pool if the caller is in the pool
898+
* @param _newPosNext Address of next transcoder in pool if the caller is in the pool
899+
*/
900+
function rewardForTranscoderWithHint(
901+
address _transcoder,
902+
address _newPosPrev,
903+
address _newPosNext
904+
) public {
905+
address rewardCaller = transcoderToRewardCaller[_transcoder];
906+
require(rewardCaller == msg.sender, "caller must be a reward caller set by the transcoder");
907+
_rewardWithHint(_transcoder, _newPosPrev, _newPosNext);
908+
}
909+
910+
/**
911+
* @notice Mint token rewards for an active transcoder and its delegators and update the transcoder pool using an optional list hint if needed
912+
* @dev If the caller is in the transcoder pool, the caller can provide an optional hint for its insertion position in the
913+
* pool via the `_newPosPrev` and `_newPosNext` params. A linear search will be executed starting at the hint to find the correct position.
914+
* In the best case, the hint is the correct position so no search is executed. See SortedDoublyLL.sol for details on list hints
915+
* @param _transcoder Address of the transcoder on behalf of which the reward is called
916+
* @param _newPosPrev Address of previous transcoder in pool if the caller is in the pool
917+
* @param _newPosNext Address of next transcoder in pool if the caller is in the pool
918+
*/
919+
function _rewardWithHint(
920+
address _transcoder,
921+
address _newPosPrev,
922+
address _newPosNext
923+
) private whenSystemNotPaused currentRoundInitialized autoCheckpoint(_transcoder) {
893924
uint256 currentRound = roundsManager().currentRound();
894925

895-
address transcoderAddress = msg.sender;
896-
if (!isActiveTranscoder(transcoderAddress)) {
897-
transcoderAddress = rewardCallerToTranscoder[msg.sender];
898-
require(isActiveTranscoder(transcoderAddress), "caller must be an active transcoder or rewardCaller");
899-
}
926+
require(isActiveTranscoder(_transcoder), "transcoder must be an active");
900927
require(
901-
transcoders[transcoderAddress].lastRewardRound != currentRound,
928+
transcoders[_transcoder].lastRewardRound != currentRound,
902929
"caller has already called reward for the current round"
903930
);
904931

905-
Transcoder storage t = transcoders[transcoderAddress];
932+
Transcoder storage t = transcoders[_transcoder];
906933
EarningsPool.Data storage earningsPool = t.earningsPoolPerRound[currentRound];
907934

908935
// Set last round that transcoder called reward
@@ -935,17 +962,17 @@ contract BondingManager is ManagerProxyTarget, IBondingManager {
935962

936963
mtr.trustedTransferTokens(trsry, treasuryRewards);
937964

938-
emit TreasuryReward(transcoderAddress, trsry, treasuryRewards);
965+
emit TreasuryReward(_transcoder, trsry, treasuryRewards);
939966
}
940967

941968
uint256 transcoderRewards = totalRewardTokens.sub(treasuryRewards);
942969

943-
updateTranscoderWithRewards(transcoderAddress, transcoderRewards, currentRound, _newPosPrev, _newPosNext);
970+
updateTranscoderWithRewards(_transcoder, transcoderRewards, currentRound, _newPosPrev, _newPosNext);
944971

945972
// Set last round that transcoder called reward
946973
t.lastRewardRound = currentRound;
947974

948-
emit Reward(transcoderAddress, transcoderRewards);
975+
emit Reward(_transcoder, transcoderRewards);
949976
}
950977

951978
/**

contracts/bonding/IBondingManager.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ interface IBondingManager {
4545
uint256 endRound
4646
);
4747
event RewardCallerSet(address indexed transcoder, address indexed rewardCaller);
48-
event RewardCallerUnset(address indexed transcoder, address indexed rewardCaller);
4948

5049
// Deprecated events
5150
// These event signatures can be used to construct the appropriate topic hashes to filter for past logs corresponding

test/unit/BondingManager.js

Lines changed: 14 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5474,9 +5474,7 @@ describe("BondingManager", () => {
54745474
it("should fail if caller is not a transcoder", async () => {
54755475
await expect(
54765476
bondingManager.connect(nonTranscoder).reward()
5477-
).to.be.revertedWith(
5478-
"caller must be an active transcoder or rewardCaller"
5479-
)
5477+
).to.be.revertedWith("transcoder must be an active")
54805478
})
54815479

54825480
it("should fail if caller is registered but not an active transcoder yet in the current round", async () => {
@@ -5486,9 +5484,7 @@ describe("BondingManager", () => {
54865484
)
54875485
await expect(
54885486
bondingManager.connect(transcoder).reward()
5489-
).to.be.revertedWith(
5490-
"caller must be an active transcoder or rewardCaller"
5491-
)
5487+
).to.be.revertedWith("transcoder must be an active")
54925488
})
54935489

54945490
it("should fail if caller already called reward during the current round", async () => {
@@ -6128,10 +6124,10 @@ describe("BondingManager", () => {
61286124

61296125
const unsetRewardCallerTx = bondingManager
61306126
.connect(transcoder)
6131-
.unsetRewardCaller(nonTranscoder.address)
6127+
.setRewardCaller(ZERO_ADDRESS)
61326128
await expect(unsetRewardCallerTx)
6133-
.to.emit(bondingManager, "RewardCallerUnset")
6134-
.withArgs(transcoder.address, nonTranscoder.address)
6129+
.to.emit(bondingManager, "RewardCallerSet")
6130+
.withArgs(transcoder.address, ZERO_ADDRESS)
61356131

61366132
const rewardTx2 = bondingManager.connect(transcoder).reward()
61376133
await expect(rewardTx2)
@@ -6147,7 +6143,9 @@ describe("BondingManager", () => {
61476143
.to.emit(bondingManager, "RewardCallerSet")
61486144
.withArgs(transcoder.address, nonTranscoder.address)
61496145

6150-
const rewardTx = bondingManager.connect(nonTranscoder).reward()
6146+
const rewardTx = bondingManager
6147+
.connect(nonTranscoder)
6148+
.rewardForTranscoder(transcoder.address)
61516149
await expect(rewardTx)
61526150
.to.emit(bondingManager, "Reward")
61536151
.withArgs(transcoder.address, transcoderRewards)
@@ -6159,46 +6157,16 @@ describe("BondingManager", () => {
61596157

61606158
const unsetRewardCallerTx = bondingManager
61616159
.connect(transcoder)
6162-
.unsetRewardCaller(nonTranscoder.address)
6160+
.setRewardCaller(ZERO_ADDRESS)
61636161
await expect(unsetRewardCallerTx)
6164-
.to.emit(bondingManager, "RewardCallerUnset")
6165-
.withArgs(transcoder.address, nonTranscoder.address)
6166-
6167-
const rewardTx2 = bondingManager.connect(nonTranscoder).reward()
6168-
await expect(rewardTx2).to.be.revertedWith(
6169-
"caller must be an active transcoder or rewardCaller"
6170-
)
6171-
})
6172-
6173-
it("impossible to set the same RewardCaller twice", async () => {
6174-
const setRewardCallerTx = bondingManager
6175-
.connect(transcoder)
6176-
.setRewardCaller(nonTranscoder.address)
6177-
await expect(setRewardCallerTx)
6178-
.to.emit(bondingManager, "RewardCallerSet")
6179-
.withArgs(transcoder.address, nonTranscoder.address)
6180-
6181-
const setRewardCallerTx2 = bondingManager
6182-
.connect(transcoder)
6183-
.setRewardCaller(nonTranscoder.address)
6184-
await expect(setRewardCallerTx2).to.be.revertedWith(
6185-
"reward caller is already set"
6186-
)
6187-
})
6188-
6189-
it("impossible to unset the RewardCaller for another transcoder", async () => {
6190-
const setRewardCallerTx = bondingManager
6191-
.connect(transcoder)
6192-
.setRewardCaller(nonTranscoder.address)
6193-
await expect(setRewardCallerTx)
61946162
.to.emit(bondingManager, "RewardCallerSet")
6195-
.withArgs(transcoder.address, nonTranscoder.address)
6163+
.withArgs(transcoder.address, ZERO_ADDRESS)
61966164

6197-
const unsetRewardCallerTx = bondingManager
6165+
const rewardTx2 = bondingManager
61986166
.connect(nonTranscoder)
6199-
.unsetRewardCaller(nonTranscoder.address)
6200-
await expect(unsetRewardCallerTx).to.be.revertedWith(
6201-
"only relevant transcoder can unset"
6167+
.rewardForTranscoder(transcoder.address)
6168+
await expect(rewardTx2).to.be.revertedWith(
6169+
"caller must be a reward caller set by the transcoder"
62026170
)
62036171
})
62046172
})

0 commit comments

Comments
 (0)