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
25 changes: 25 additions & 0 deletions contracts/bondingcurve/BondingCurve.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ contract BondingCurve is IBondingCurve, OracleRef, PCVSplitter, Timed, Incentivi
/// @notice the discount applied on top of peg before at Scale
uint256 public override discount;

/// @notice the cap on how much FEI can be minted by the bonding curve
uint256 public override mintCap;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we upgrade the contracts, are we doing it with a proxy/storage pattern or some other method?
If we are using a proxy/storage pattern, the variable should be added after BASIS_POINTS_GRANULARITY.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use proxy/storage its just drop-and-replace


uint256 public constant BASIS_POINTS_GRANULARITY = 10_000;

/// @notice constructor
Expand Down Expand Up @@ -64,6 +67,7 @@ contract BondingCurve is IBondingCurve, OracleRef, PCVSplitter, Timed, Incentivi
Timed(_duration)
Incentivized(_incentive)
{
_setMintCap(_scale); // default mint cap is scale
_setScale(_scale);
token = _token;

Expand Down Expand Up @@ -142,6 +146,11 @@ contract BondingCurve is IBondingCurve, OracleRef, PCVSplitter, Timed, Incentivi
_setDuration(_frequency);
}

/// @notice sets the mint cap for the bonding curve
function setMintCap(uint256 _mintCap) external override onlyGovernor {
_setMintCap(_mintCap);
}

/// @notice sets the allocation of incoming PCV
function setAllocation(
address[] calldata allocations,
Expand Down Expand Up @@ -176,6 +185,11 @@ contract BondingCurve is IBondingCurve, OracleRef, PCVSplitter, Timed, Incentivi
return totalPurchased >= scale;
}

/// @notice returns how close to the minting cap we are
function availableToMint() public view override returns (uint256) {
return mintCap - totalPurchased;
}

/// @notice return current instantaneous bonding curve price
/// @return price reported as FEI per USD
/// @dev Can be inaccurate if outdated, need to call `oracle().isOutdated()` to check
Expand Down Expand Up @@ -229,6 +243,8 @@ contract BondingCurve is IBondingCurve, OracleRef, PCVSplitter, Timed, Incentivi

amountOut = getAmountOut(amountIn);

require(availableToMint() >= amountOut, "BondingCurve: exceeds mint cap");

_incrementTotalPurchased(amountOut);
fei().mint(to, amountOut);

Expand All @@ -249,6 +265,15 @@ contract BondingCurve is IBondingCurve, OracleRef, PCVSplitter, Timed, Incentivi
emit ScaleUpdate(oldScale, newScale);
}

function _setMintCap(uint256 newMintCap) internal {
require(newMintCap != 0, "BondingCurve: zero mint cap");

uint256 oldMintCap = mintCap;
mintCap = newMintCap;

emit MintCapUpdate(oldMintCap, newMintCap);
}

/// @notice the bonding curve price multiplier used before Scale
function _getBondingCurvePriceMultiplier()
internal
Expand Down
7 changes: 7 additions & 0 deletions contracts/bondingcurve/IBondingCurve.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ interface IBondingCurve {

event ScaleUpdate(uint256 oldScale, uint256 newScale);

event MintCapUpdate(uint256 oldMint, uint256 newMint);

event BufferUpdate(uint256 oldBuffer, uint256 newBuffer);

event DiscountUpdate(uint256 oldDiscount, uint256 newDiscount);
Expand Down Expand Up @@ -45,6 +47,8 @@ interface IBondingCurve {

function setIncentiveFrequency(uint256 newFrequency) external;

function setMintCap(uint256 newMintCap) external;

// ----------- Getters -----------

function getCurrentPrice() external view returns (Decimal.D256 memory);
Expand All @@ -68,4 +72,7 @@ interface IBondingCurve {

function token() external view returns (IERC20);

function mintCap() external view returns (uint256);

function availableToMint() external view returns (uint256);
}
13 changes: 12 additions & 1 deletion test/bondingcurve/BondingCurve.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ describe('BondingCurve', function () {

await this.token.mint(userAddress, '1000000000000000000000000');
await this.core.grantMinter(this.bondingCurve.address, {from: governorAddress});

await this.bondingCurve.setMintCap(this.scale.mul(new BN('10')), {from: governorAddress});
});

describe('Init', function() {
Expand Down Expand Up @@ -270,7 +272,16 @@ describe('BondingCurve', function () {
});
});
});

describe('Exceeding cap', function() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't tell if there is a test in this file that buys under the mint cap to check that its still possible to buy even with the mint cap on

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To add to this point, I only see purchasing happening in the before each hook and we don't verify the fei they received. Recommend adding a test purchasing from the bonding curve that actually succeeds. After the success, we should verify FEI balances and that the bonding curve or receiving contract's eth balance incremented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are tests for the FEI being received higher up in the file

it('reverts', async function() {
this.purchaseAmount = new BN('4000000000');
await this.token.approve(this.bondingCurve.address, this.purchaseAmount, {from: userAddress});
await expectRevert(
this.bondingCurve.purchase(userAddress, this.purchaseAmount, {from: userAddress}),
'BondingCurve: exceeds mint cap'
);
});
});
describe('Crossing Scale', function() {
beforeEach(async function() {
this.purchaseAmount = new BN('400000000');
Expand Down
11 changes: 11 additions & 0 deletions test/bondingcurve/EthBondingCurve.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ describe('EthBondingCurve', function () {
scale: '100000000000', buffer: '100', discount: '100', duration: this.incentiveDuration.toString(), incentive: this.incentiveAmount.toString(), pcvDeposits: [this.pcvDeposit1.address, this.pcvDeposit2.address], ratios: [9000, 1000]
});
await this.core.grantMinter(this.bondingCurve.address, {from: governorAddress});

await this.bondingCurve.setMintCap(this.scale.mul(new BN('10')), {from: governorAddress});
});

describe('Init', function() {
Expand Down Expand Up @@ -260,6 +262,15 @@ describe('EthBondingCurve', function () {
});
});

describe('Exceeding cap', function() {
it('reverts', async function() {
this.purchaseAmount = new BN('4000000000');
await expectRevert(
this.bondingCurve.purchase(userAddress, this.purchaseAmount, {from: userAddress, value: this.purchaseAmount}),
'BondingCurve: exceeds mint cap'
);
});
});
describe('Crossing Scale', function() {
beforeEach(async function() {
this.purchaseAmount = new BN('200000000');
Expand Down