Skip to content

Commit

Permalink
Incentive fee
Browse files Browse the repository at this point in the history
  • Loading branch information
0xkorin committed Dec 24, 2023
1 parent a72659f commit 8970d08
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 5 deletions.
15 changes: 14 additions & 1 deletion contracts/governance/InclusionIncentives.vy
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ voting: public(immutable(Voting))
management: public(address)
pending_management: public(address)
treasury: public(address)
fee_rate: public(uint256)
incentives: public(HashMap[uint256, HashMap[address, HashMap[address, uint256]]]) # epoch => candidate => incentive token => incentive amount
incentives_depositor: public(HashMap[address, HashMap[uint256, HashMap[address, HashMap[address, uint256]]]]) # depositor => epoch => candidate => incentive token => incentive amount
unclaimed: public(HashMap[uint256, HashMap[address, uint256]]) # epoch => incentive token => incentive amount
Expand Down Expand Up @@ -77,6 +78,7 @@ event SetManagement:

WEEK: constant(uint256) = 7 * 24 * 60 * 60
EPOCH_LENGTH: constant(uint256) = 4 * WEEK
FEE_SCALE: constant(uint256) = 10_000

@external
def __init__(_voting: address):
Expand Down Expand Up @@ -123,7 +125,8 @@ def deposit(_candidate: address, _token: address, _amount: uint256):
"""
assert (block.timestamp - genesis) % EPOCH_LENGTH <= self.deposit_deadline
epoch: uint256 = self._epoch()
self.incentives[epoch][_candidate][_token] += _amount
fee: uint256 = _amount * self.fee_rate / FEE_SCALE
self.incentives[epoch][_candidate][_token] += _amount - fee
self.incentives_depositor[msg.sender][epoch][_candidate][_token] += _amount
self.unclaimed[epoch][_token] += _amount

Expand Down Expand Up @@ -302,6 +305,16 @@ def set_claim_deadline(_deadline: uint256):
self.claim_deadline = _deadline
log SetClaimDeadline(_deadline)

@external
def set_fee_rate(_fee_rate: uint256):
"""
@notice Set the incentive fee rate
@param _fee_rate New fee rate (bps)
"""
assert msg.sender == self.management
assert _fee_rate <= FEE_SCALE / 10
self.fee_rate = _fee_rate

@external
def set_management(_management: address):
"""
Expand Down
18 changes: 16 additions & 2 deletions contracts/governance/WeightIncentives.vy
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ voting: public(immutable(Voting))
management: public(address)
pending_management: public(address)
treasury: public(address)
fee_rate: public(uint256)
incentives: public(HashMap[uint256, HashMap[uint256, HashMap[address, uint256]]]) # epoch => idx => incentive token => incentive amount
unclaimed: public(HashMap[uint256, HashMap[address, uint256]]) # epoch => incentive token => incentive amount
user_claimed: public(HashMap[address, HashMap[uint256, HashMap[uint256, HashMap[address, bool]]]]) # account => epoch => idx => incentive token => claimed?
Expand Down Expand Up @@ -62,6 +63,7 @@ WEEK: constant(uint256) = 7 * 24 * 60 * 60
EPOCH_LENGTH: constant(uint256) = 4 * WEEK
VOTE_LENGTH: constant(uint256) = WEEK
VOTE_START: constant(uint256) = EPOCH_LENGTH - VOTE_LENGTH
FEE_SCALE: constant(uint256) = 10_000

@external
def __init__(_pool: address, _voting: address):
Expand Down Expand Up @@ -111,7 +113,8 @@ def deposit(_idx: uint256, _token: address, _amount: uint256):
assert (block.timestamp - genesis) % EPOCH_LENGTH <= self.deposit_deadline
assert pool.num_assets() >= _idx
epoch: uint256 = self._epoch()
self.incentives[epoch][_idx][_token] += _amount
fee: uint256 = _amount * self.fee_rate / FEE_SCALE
self.incentives[epoch][_idx][_token] += _amount - fee
self.unclaimed[epoch][_token] += _amount

assert ERC20(_token).transferFrom(msg.sender, self, _amount, default_return_value=True)
Expand Down Expand Up @@ -188,9 +191,10 @@ def _claim(_epoch: uint256, _idx: uint256, _token: address, _account: address):
@view
def sweepable(_epoch: uint256, _token: address) -> uint256:
"""
@notice Query whether an incentive can be swept
@notice Query how much of an incentive can be swept
@param _epoch Epoch to query for
@param _token Incentive token to query for
@return Amount of tokens that can be swept
"""
if self._epoch() <= _epoch + self.claim_deadline:
return 0
Expand Down Expand Up @@ -250,6 +254,16 @@ def set_claim_deadline(_deadline: uint256):
assert _deadline >= 1
self.claim_deadline = _deadline

@external
def set_fee_rate(_fee_rate: uint256):
"""
@notice Set the incentive fee rate
@param _fee_rate New fee rate (bps)
"""
assert msg.sender == self.management
assert _fee_rate <= FEE_SCALE / 10
self.fee_rate = _fee_rate

@external
def set_management(_management: address):
"""
Expand Down
29 changes: 28 additions & 1 deletion tests/governance/test_inclusion_incentives.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,33 @@ def test_claim(chain, deployer, alice, bob, measure, token, incentive_token, vot
incentives.claim(epoch, incentive_token, bob, sender=bob)
assert incentive_token.balanceOf(bob) == 4 * UNIT

def test_claim_fee(chain, deployer, alice, measure, token, incentive_token, voting, incentives):
epoch = incentives.epoch()
incentives.set_fee_rate(1000, sender=deployer)
incentive_token.mint(alice, 10 * UNIT, sender=alice)
incentive_token.approve(incentives, 10 * UNIT, sender=alice)
incentives.deposit(token, incentive_token, 10 * UNIT, sender=alice)
voting.set_rate_provider(token, RATE_PROVIDER, sender=deployer)
voting.apply(token, sender=alice)
measure.set_vote_weight(alice, UNIT, sender=alice)
chain.pending_timestamp += VOTE_START
voting.vote([0, 10000], sender=alice)
chain.pending_timestamp += WEEK
voting.finalize_epoch(sender=alice)

assert incentives.claimable(epoch, incentive_token, alice) == 9 * UNIT
incentives.claim(epoch, incentive_token, sender=alice)
assert incentives.claimable(epoch, incentive_token, alice) == 0
assert incentive_token.balanceOf(alice) == 9 * UNIT
assert incentives.unclaimed(epoch, incentive_token) == UNIT

# claim fee through a sweep
chain.pending_timestamp += EPOCH_LENGTH
chain.mine()
assert incentives.sweepable(epoch, incentive_token) == UNIT
incentives.sweep(epoch, incentive_token, sender=deployer)
assert incentive_token.balanceOf(deployer) == UNIT

def test_refund(chain, deployer, alice, bob, measure, token, incentive_token, voting, incentives):
epoch = incentives.epoch()
incentive_token.mint(alice, UNIT, sender=alice)
Expand Down Expand Up @@ -131,7 +158,7 @@ def test_sweep(chain, deployer, alice, bob, charlie, measure, token, incentive_t
incentives.claim(epoch, incentive_token, alice, sender=bob)

# unclaimed incentives cannot be swept yet
incentives.sweepable(epoch, incentive_token) == 0
assert incentives.sweepable(epoch, incentive_token) == 0
with ape.reverts():
incentives.sweep(epoch, incentive_token, sender=deployer)

Expand Down
26 changes: 25 additions & 1 deletion tests/governance/test_weight_incentives.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,30 @@ def test_claim(chain, alice, bob, measure, incentive_token, voting, incentives):
incentives.claim(epoch, 2, incentive_token, bob, sender=bob)
assert incentive_token.balanceOf(bob) == 4 * UNIT

def test_claim_fee(chain, deployer, alice, measure, incentive_token, voting, incentives):
epoch = incentives.epoch()
incentives.set_fee_rate(1000, sender=deployer)
incentive_token.mint(alice, 10 * UNIT, sender=alice)
incentive_token.approve(incentives, 10 * UNIT, sender=alice)
incentives.deposit(2, incentive_token, 10 * UNIT, sender=alice)
measure.set_vote_weight(alice, UNIT, sender=alice)
chain.pending_timestamp += VOTE_START
voting.vote([5000, 0, 5000], sender=alice)
chain.pending_timestamp += WEEK
chain.mine()
assert incentives.claimable(epoch, 2, incentive_token, alice) == 9 * UNIT
incentives.claim(epoch, 2, incentive_token, sender=alice)
assert incentives.claimable(epoch, 2, incentive_token, alice) == 0
assert incentive_token.balanceOf(alice) == 9 * UNIT
assert incentives.unclaimed(epoch, incentive_token) == UNIT

# claim fee through a sweep
chain.pending_timestamp += EPOCH_LENGTH
chain.mine()
assert incentives.sweepable(epoch, incentive_token) == UNIT
incentives.sweep(epoch, incentive_token, sender=deployer)
assert incentive_token.balanceOf(deployer) == UNIT

def test_sweep(chain, deployer, alice, bob, charlie, measure, incentive_token, voting, incentives):
epoch = incentives.epoch()
incentive_token.mint(alice, 6 * UNIT, sender=alice)
Expand All @@ -96,7 +120,7 @@ def test_sweep(chain, deployer, alice, bob, charlie, measure, incentive_token, v
incentives.claim(epoch, 2, incentive_token, alice, sender=bob)

# unclaimed incentives cannot be swept yet
incentives.sweepable(epoch, incentive_token) == 0
assert incentives.sweepable(epoch, incentive_token) == 0
with ape.reverts():
incentives.sweep(epoch, incentive_token, sender=deployer)

Expand Down

0 comments on commit 8970d08

Please sign in to comment.