Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/orchestration/CoreOrchestrator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ contract CoreOrchestrator is Ownable {
// ----------- Params -----------
uint256 public constant EXCHANGE_RATE_DISCOUNT = 10;

uint32 public constant INCENTIVE_GROWTH_RATE = 75; // a bit over 1 unit per 5 hours assuming 13s block time
uint32 public constant INCENTIVE_GROWTH_RATE = 25; // a bit over 1 unit per 15 hours assuming 13s block time

uint256 public constant SCALE = 100_000_000e18;
uint256 public constant BONDING_CURVE_INCENTIVE = 500e18;
Expand Down
28 changes: 19 additions & 9 deletions contracts/token/UniswapIncentive.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ contract UniswapIncentive is IUniswapIncentive, UniRef {
/// @notice the granularity of the time weight and growth rate
uint32 public constant override TIME_WEIGHT_GRANULARITY = 100_000;

uint public constant INCENTIVE_CAP_PERCENTAGE = 30;

mapping(address => bool) private _exempt;
mapping(address => bool) private _allowlist;

Expand Down Expand Up @@ -162,7 +164,7 @@ contract UniswapIncentive is IUniswapIncentive, UniRef {
return _allowlist[account];
}

/// @notice return true if burn incentive equals mint
/// @notice return true if mint equals adjusted burn incentive
function isIncentiveParity() public view override returns (bool) {
uint32 weight = getTimeWeight();
require(weight != 0, "UniswapIncentive: Incentive zero or not active");
Expand All @@ -175,7 +177,7 @@ contract UniswapIncentive is IUniswapIncentive, UniRef {
);

Decimal.D256 memory incentive = _calculateBuyIncentiveMultiplier(deviation, weight);
Decimal.D256 memory penalty = _calculateSellPenaltyMultiplier(deviation);
Decimal.D256 memory penalty = _getAdjustedSellPenaltyMultiplier(deviation);
return incentive.equals(penalty);
}

Expand Down Expand Up @@ -308,15 +310,15 @@ contract UniswapIncentive is IUniswapIncentive, UniRef {
Decimal.D256 memory deviation,
uint32 weight
) internal pure returns (Decimal.D256 memory) {
Decimal.D256 memory correspondingPenalty =
_calculateSellPenaltyMultiplier(deviation);
Decimal.D256 memory adjustedPenalty =
_getAdjustedSellPenaltyMultiplier(deviation);
Decimal.D256 memory buyMultiplier =
deviation.mul(uint256(weight)).div(
uint256(TIME_WEIGHT_GRANULARITY)
);

if (correspondingPenalty.lessThan(buyMultiplier)) {
return correspondingPenalty;
if (adjustedPenalty.lessThan(buyMultiplier)) {
return adjustedPenalty;
}

return buyMultiplier;
Expand All @@ -330,6 +332,14 @@ contract UniswapIncentive is IUniswapIncentive, UniRef {
return deviation.mul(deviation).mul(100); // m^2 * 100
}

function _getAdjustedSellPenaltyMultiplier(Decimal.D256 memory deviation)
internal
pure
returns (Decimal.D256 memory)
{
return _calculateSellPenaltyMultiplier(deviation).mul(INCENTIVE_CAP_PERCENTAGE).div(100);
}

function _updateTimeWeight(
Decimal.D256 memory initialDeviation,
Decimal.D256 memory finalDeviation,
Expand Down Expand Up @@ -357,12 +367,12 @@ contract UniswapIncentive is IUniswapIncentive, UniRef {
.asUint256();
}

// cap incentive at max penalty
// cap incentive at adjusted max penalty
uint256 maxWeight =
finalDeviation
.mul(100)
.mul(INCENTIVE_CAP_PERCENTAGE)
.mul(uint256(TIME_WEIGHT_GRANULARITY))
.asUint256(); // m^2*100 (sell) = t*m (buy)
.asUint256(); // 30*m^2 (adjusted sell) = t*m (buy)
updatedWeight = Math.min(updatedWeight, maxWeight);
_setTimeWeight(updatedWeight.toUint32(), getGrowthRate(), true);
}
Expand Down
71 changes: 38 additions & 33 deletions test/token/UniswapIncentive.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,11 @@ describe('UniswapIncentive', function () {
});

describe('Active Time Weight', function() {
// at 2% deviation, w=2 is parity
// at 2% deviation, w=.6 is adjusted parity
describe('Below Parity', function() {
beforeEach(async function() {
// w=1
await this.incentive.setTimeWeight(0, 100000, true, {from: governorAddress});
// w=.5
await this.incentive.setTimeWeight(0, 50000, true, {from: governorAddress});
await time.advanceBlock();
});
it('returns false', async function() {
Expand All @@ -139,7 +139,7 @@ describe('UniswapIncentive', function () {

describe('At Parity', function() {
beforeEach(async function() {
await this.incentive.setTimeWeight(0, 200000, true, {from: governorAddress});
await this.incentive.setTimeWeight(0, 60000, true, {from: governorAddress});
await time.advanceBlock();
});
it('returns true', async function() {
Expand All @@ -149,7 +149,7 @@ describe('UniswapIncentive', function () {

describe('Exceeds Parity', function() {
beforeEach(async function() {
await this.incentive.setTimeWeight(0, 400000, true, {from: governorAddress});
await this.incentive.setTimeWeight(0, 100000, true, {from: governorAddress});
await time.advanceBlock();
});
it('returns true', async function() {
Expand Down Expand Up @@ -449,8 +449,8 @@ describe('UniswapIncentive', function () {
// 510 FEI/ETH = 2% away
await this.pair.setReserves(100000, 51000000);
let block = await time.latestBlock();
// Set growth rate to 1 per block, starting at next block (the block preceeding the withdrawal)
await this.incentive.setTimeWeight(0, 100000, true, {from: governorAddress});
// Set growth rate to .5 per block, starting at next block (the block preceeding the withdrawal)
await this.incentive.setTimeWeight(0, 50000, true, {from: governorAddress});
});

describe('Buy', function() {
Expand Down Expand Up @@ -478,8 +478,8 @@ describe('UniswapIncentive', function () {
});

it('time weight stays active', async function() {
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(200000));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(50000));
expect(await this.incentive.isTimeWeightActive()).to.be.equal(true);
});
});
Expand Down Expand Up @@ -508,16 +508,16 @@ describe('UniswapIncentive', function () {
});

it('time weight stays active', async function() {
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(200000));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(50000));
expect(await this.incentive.isTimeWeightActive()).to.be.equal(true);
});
});

describe('to peg', function() {
beforeEach(async function() {
this.transferAmount = new BN(502500);
this.expectedMint = new BN(10049);
this.expectedMint = new BN(5024);
await this.pair.withdrawFei(userAddress, 502500);
});

Expand All @@ -538,15 +538,15 @@ describe('UniswapIncentive', function () {

it('resets time weight', async function() {
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(0));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(50000));
expect(await this.incentive.isTimeWeightActive()).to.be.equal(false);
});
});

describe('past peg', function() {
beforeEach(async function() {
this.transferAmount = new BN(1000000);
this.expectedMint = new BN(10049); // should be same as to peg
this.expectedMint = new BN(5024); // should be same as to peg
await this.pair.withdrawFei(userAddress, 1000000);
});

Expand All @@ -567,15 +567,15 @@ describe('UniswapIncentive', function () {

it('resets time weight', async function() {
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(0));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(50000));
expect(await this.incentive.isTimeWeightActive()).to.be.equal(false);
});
});

describe('short of peg', function() {
beforeEach(async function() {
this.transferAmount = new BN(100000);
this.expectedMint = new BN(2000);
this.expectedMint = new BN(1000);
await this.pair.withdrawFei(userAddress, 100000);
});

Expand All @@ -595,16 +595,16 @@ describe('UniswapIncentive', function () {
});

it('partially updates time weight', async function() {
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(80043));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(40021));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(50000));
expect(await this.incentive.isTimeWeightActive()).to.be.equal(true);
});
});

describe('double time weight', function() {
beforeEach(async function() {
this.transferAmount = new BN(502500);
this.expectedMint = new BN(20099);
this.expectedMint = new BN(6029);
await time.advanceBlock();
await this.pair.withdrawFei(userAddress, 502500);
});
Expand All @@ -626,15 +626,15 @@ describe('UniswapIncentive', function () {

it('resets time weight', async function() {
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(0));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(50000));
expect(await this.incentive.isTimeWeightActive()).to.be.equal(false);
});
});

describe('double time weight short of peg', function() {
beforeEach(async function() {
this.transferAmount = new BN(100000);
this.expectedMint = new BN(4000);
this.expectedMint = new BN(1200);
await time.advanceBlock();
await this.pair.withdrawFei(userAddress, 100000);
});
Expand All @@ -655,16 +655,16 @@ describe('UniswapIncentive', function () {
});

it('partially updates time weight', async function() {
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(160086));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(48025));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(50000));
expect(await this.incentive.isTimeWeightActive()).to.be.equal(true);
});
});

describe('time weight exceeds burn', function() {
beforeEach(async function() {
this.transferAmount = new BN(502500);
this.expectedMint = new BN(20099);
this.expectedMint = new BN(6029);
await time.advanceBlock();
await time.advanceBlock();
await time.advanceBlock();
Expand All @@ -688,7 +688,7 @@ describe('UniswapIncentive', function () {

it('resets time weight', async function() {
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(0));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(50000));
expect(await this.incentive.isTimeWeightActive()).to.be.equal(false);
});
});
Expand Down Expand Up @@ -719,8 +719,8 @@ describe('UniswapIncentive', function () {
});

it('time weight stays active', async function() {
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(200000));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(50000));
expect(await this.incentive.isTimeWeightActive()).to.be.equal(true);
});
});
Expand Down Expand Up @@ -749,8 +749,8 @@ describe('UniswapIncentive', function () {
});

it('time weight stays active', async function() {
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(200000));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(50000));
expect(await this.incentive.isTimeWeightActive()).to.be.equal(true);
});
});
Expand Down Expand Up @@ -778,8 +778,8 @@ describe('UniswapIncentive', function () {
});

it('time weight updates', async function() {
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(50000));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(50000));
expect(await this.incentive.isTimeWeightActive()).to.be.equal(true);
});
});
Expand All @@ -793,6 +793,11 @@ describe('UniswapIncentive', function () {
await time.advanceBlock();
await time.advanceBlock();
await time.advanceBlock();
await time.advanceBlock();
await time.advanceBlock();
await time.advanceBlock();
await time.advanceBlock();
await time.advanceBlock();

await this.fei.transfer(this.pair.address, 100000, {from: userAddress});
});
Expand All @@ -813,8 +818,8 @@ describe('UniswapIncentive', function () {
});

it('time weight update capped', async function() {
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(240070));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(100000));
expect(await this.incentive.getTimeWeight()).to.be.bignumber.equal(new BN(72021));
expect(await this.incentive.getGrowthRate()).to.be.bignumber.equal(new BN(50000));
expect(await this.incentive.isTimeWeightActive()).to.be.equal(true);
});
});
Expand Down