enforce lower bound on bond price
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
parent
45e730a8ce
commit
40c203a6b0
@ -150,7 +150,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function create(
|
function create(
|
||||||
uint256[3] calldata _market,
|
uint256[4] calldata _market,
|
||||||
uint256[2] calldata _terms,
|
uint256[2] calldata _terms,
|
||||||
address _quoteToken,
|
address _quoteToken,
|
||||||
uint32[2] calldata _intervals,
|
uint32[2] calldata _intervals,
|
||||||
@ -169,19 +169,20 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
|
|||||||
|
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
// forge-lint: disable-next-line(unsafe-typecast)
|
||||||
uint64 maxPayout = uint64(FullMath.mulDiv(targetDebt, _intervals[0], secondsToConclusion));
|
uint64 maxPayout = uint64(FullMath.mulDiv(targetDebt, _intervals[0], secondsToConclusion));
|
||||||
uint256 maxDebt = targetDebt + FullMath.mulDiv(targetDebt, _market[2], 1e5);
|
uint256 maxDebt = targetDebt + FullMath.mulDiv(targetDebt, _market[3], 1e5);
|
||||||
uint256 controlVariable = FullMath.mulDiv(_market[1], ITreasury(TREASURY).baseSupply(), targetDebt);
|
uint256 controlVariable = FullMath.mulDiv(_market[1], ITreasury(TREASURY).baseSupply(), targetDebt);
|
||||||
|
|
||||||
id = markets.length;
|
id = markets.length;
|
||||||
|
|
||||||
markets.push(
|
markets.push(
|
||||||
Market({
|
Market({
|
||||||
|
capacity: _market[0],
|
||||||
|
purchased: 0,
|
||||||
|
minimumPrice: _market[2],
|
||||||
quoteToken: _quoteToken,
|
quoteToken: _quoteToken,
|
||||||
capacityInQuote: _booleans[0],
|
capacityInQuote: _booleans[0],
|
||||||
capacity: _market[0],
|
|
||||||
totalDebt: targetDebt,
|
totalDebt: targetDebt,
|
||||||
maxPayout: maxPayout,
|
maxPayout: maxPayout,
|
||||||
purchased: 0,
|
|
||||||
sold: 0
|
sold: 0
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -225,7 +226,9 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function marketPrice(uint256 id) public view override returns (uint256) {
|
function marketPrice(uint256 id) public view override returns (uint256) {
|
||||||
return FullMath.mulDiv(currentControlVariable(id), debtRatio(id), 10**metadatas[id].quoteDecimals);
|
uint256 minimumPrice = markets[id].minimumPrice;
|
||||||
|
uint256 currentPrice = FullMath.mulDiv(currentControlVariable(id), debtRatio(id), 10**metadatas[id].quoteDecimals);
|
||||||
|
return currentPrice > minimumPrice ? currentPrice : minimumPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
function payoutFor(
|
function payoutFor(
|
||||||
@ -311,7 +314,9 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _marketPrice(uint256 _id) internal view returns (uint256) {
|
function _marketPrice(uint256 _id) internal view returns (uint256) {
|
||||||
return FullMath.mulDiv(terms[_id].controlVariable, _debtRatio(_id), 10**metadatas[_id].quoteDecimals);
|
uint256 minimumPrice = markets[_id].minimumPrice;
|
||||||
|
uint256 currentPrice = FullMath.mulDiv(terms[_id].controlVariable, _debtRatio(_id), 10**metadatas[_id].quoteDecimals);
|
||||||
|
return currentPrice > minimumPrice ? currentPrice : minimumPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _debtRatio(uint256 id) internal view returns (uint256) {
|
function _debtRatio(uint256 id) internal view returns (uint256) {
|
||||||
|
|||||||
@ -18,12 +18,13 @@ interface IBondDepository {
|
|||||||
|
|
||||||
struct Market {
|
struct Market {
|
||||||
uint256 capacity;
|
uint256 capacity;
|
||||||
|
uint256 purchased;
|
||||||
|
uint256 minimumPrice;
|
||||||
address quoteToken;
|
address quoteToken;
|
||||||
bool capacityInQuote;
|
bool capacityInQuote;
|
||||||
uint64 totalDebt;
|
uint64 totalDebt;
|
||||||
uint64 maxPayout;
|
uint64 maxPayout;
|
||||||
uint64 sold;
|
uint64 sold;
|
||||||
uint256 purchased;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Term {
|
struct Term {
|
||||||
@ -65,7 +66,7 @@ interface IBondDepository {
|
|||||||
);
|
);
|
||||||
|
|
||||||
function create(
|
function create(
|
||||||
uint256[3] memory _market,
|
uint256[4] memory _market,
|
||||||
uint256[2] memory _terms,
|
uint256[2] memory _terms,
|
||||||
address _quoteToken,
|
address _quoteToken,
|
||||||
uint32[2] memory _intervals,
|
uint32[2] memory _intervals,
|
||||||
|
|||||||
@ -121,7 +121,7 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
|
|
||||||
vm.prank(POLICY);
|
vm.prank(POLICY);
|
||||||
depository.create(
|
depository.create(
|
||||||
[CAPACITY, INITIAL_PRICE, BUFFER],
|
[CAPACITY, INITIAL_PRICE, 0, BUFFER],
|
||||||
[VESTING, conclusion],
|
[VESTING, conclusion],
|
||||||
address(reserve),
|
address(reserve),
|
||||||
[uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)], // forge-lint: disable-line(unsafe-typecast)
|
[uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)], // forge-lint: disable-line(unsafe-typecast)
|
||||||
@ -144,7 +144,7 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
|
|
||||||
vm.prank(POLICY);
|
vm.prank(POLICY);
|
||||||
depository.create(
|
depository.create(
|
||||||
[CAPACITY, INITIAL_PRICE, BUFFER],
|
[CAPACITY, INITIAL_PRICE, 0, BUFFER],
|
||||||
[VESTING, conclusion],
|
[VESTING, conclusion],
|
||||||
address(weth),
|
address(weth),
|
||||||
[uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)], // forge-lint: disable-line(unsafe-typecast)
|
[uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)], // forge-lint: disable-line(unsafe-typecast)
|
||||||
@ -166,14 +166,14 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test_shouldSetMaxPayoutToCorrectPercentageOfCapacity() public view {
|
function test_shouldSetMaxPayoutToCorrectPercentageOfCapacity() public view {
|
||||||
(, , , , uint256 maxPayout, ,) = depository.markets(0);
|
(, , , , , , uint256 maxPayout,) = depository.markets(0);
|
||||||
assertEq(maxPayout, CAPACITY / 6);
|
assertEq(maxPayout, CAPACITY / 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_shouldReturnIdsOfAllMarkets() public {
|
function test_shouldReturnIdsOfAllMarkets() public {
|
||||||
vm.prank(POLICY);
|
vm.prank(POLICY);
|
||||||
depository.create(
|
depository.create(
|
||||||
[CAPACITY, INITIAL_PRICE, BUFFER],
|
[CAPACITY, INITIAL_PRICE, 0, BUFFER],
|
||||||
[VESTING, conclusion],
|
[VESTING, conclusion],
|
||||||
address(reserve),
|
address(reserve),
|
||||||
[uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)], // forge-lint: disable-line(unsafe-typecast)
|
[uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)], // forge-lint: disable-line(unsafe-typecast)
|
||||||
@ -186,10 +186,31 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
assertEq(liveMarkets[1], 1);
|
assertEq(liveMarkets[1], 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function test_marketPriceShouldNeverFallBelowMinimum() public {
|
||||||
|
uint256 initialPrice = 420 * 1e9;
|
||||||
|
uint256 minPrice = 69 * 1e9;
|
||||||
|
|
||||||
|
vm.prank(POLICY);
|
||||||
|
uint256 marketId = depository.create(
|
||||||
|
[CAPACITY, initialPrice, minPrice, BUFFER],
|
||||||
|
[VESTING, TIME_TO_CONCLUSION],
|
||||||
|
address(reserve),
|
||||||
|
[uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)],
|
||||||
|
[false, true]
|
||||||
|
);
|
||||||
|
|
||||||
|
uint256 currentPrice = depository.marketPrice(marketId);
|
||||||
|
assertEq(currentPrice, initialPrice);
|
||||||
|
|
||||||
|
vm.warp(block.timestamp + TIME_TO_CONCLUSION - 420);
|
||||||
|
uint256 priceAfterDecay = depository.marketPrice(marketId);
|
||||||
|
assertEq(priceAfterDecay, minPrice);
|
||||||
|
}
|
||||||
|
|
||||||
function test_shouldUpdateIdsOfMarkets() public {
|
function test_shouldUpdateIdsOfMarkets() public {
|
||||||
vm.startPrank(POLICY);
|
vm.startPrank(POLICY);
|
||||||
depository.create(
|
depository.create(
|
||||||
[CAPACITY, INITIAL_PRICE, BUFFER],
|
[CAPACITY, INITIAL_PRICE, 0, BUFFER],
|
||||||
[VESTING, conclusion],
|
[VESTING, conclusion],
|
||||||
address(reserve),
|
address(reserve),
|
||||||
[uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)], // forge-lint: disable-line(unsafe-typecast)
|
[uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)], // forge-lint: disable-line(unsafe-typecast)
|
||||||
@ -221,13 +242,13 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test_shouldDecayDebt() public {
|
function test_shouldDecayDebt() public {
|
||||||
(, , , uint256 totalDebt, , ,) = depository.markets(0);
|
(, , , , , uint256 totalDebt, ,) = depository.markets(0);
|
||||||
|
|
||||||
skip(DEPOSIT_INTERVAL);
|
skip(DEPOSIT_INTERVAL);
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
depository.deposit(0, 0, INITIAL_PRICE, ALICE);
|
depository.deposit(0, 0, INITIAL_PRICE, ALICE);
|
||||||
|
|
||||||
(, , , uint256 newTotalDebt, , ,) = depository.markets(0);
|
(, , , , , uint256 newTotalDebt, ,) = depository.markets(0);
|
||||||
assertEq(totalDebt > newTotalDebt, true);
|
assertEq(totalDebt > newTotalDebt, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,7 +453,7 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test_shouldDecayMaxPayoutInTargetDepositInterval() public {
|
function test_shouldDecayMaxPayoutInTargetDepositInterval() public {
|
||||||
(, , , , uint64 maxPayout, ,) = depository.markets(0);
|
(, , , , , , uint64 maxPayout,) = depository.markets(0);
|
||||||
uint256 price = depository.marketPrice(0);
|
uint256 price = depository.marketPrice(0);
|
||||||
uint256 amount = maxPayout * price;
|
uint256 amount = maxPayout * price;
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
@ -443,11 +464,11 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test_shouldCloseMarket() public {
|
function test_shouldCloseMarket() public {
|
||||||
(uint256 cap, , , , , ,) = depository.markets(0);
|
(uint256 cap, , , , , , ,) = depository.markets(0);
|
||||||
assertEq(cap > 0, true);
|
assertEq(cap > 0, true);
|
||||||
vm.prank(POLICY);
|
vm.prank(POLICY);
|
||||||
depository.close(0);
|
depository.close(0);
|
||||||
(uint256 newCap, , , , , ,) = depository.markets(0);
|
(uint256 newCap, , , , , , ,) = depository.markets(0);
|
||||||
assertEq(newCap, 0);
|
assertEq(newCap, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -72,7 +72,7 @@ contract MockGovernance is Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function create(
|
function create(
|
||||||
uint256[3] calldata market,
|
uint256[4] calldata market,
|
||||||
uint256[2] calldata terms,
|
uint256[2] calldata terms,
|
||||||
address quoteToken,
|
address quoteToken,
|
||||||
uint32[2] calldata intervals,
|
uint32[2] calldata intervals,
|
||||||
@ -83,6 +83,7 @@ contract MockGovernance is Test {
|
|||||||
assertEq(market[0], 69);
|
assertEq(market[0], 69);
|
||||||
assertEq(market[1], 420);
|
assertEq(market[1], 420);
|
||||||
assertEq(market[2], 1337);
|
assertEq(market[2], 1337);
|
||||||
|
assertEq(market[3], 0);
|
||||||
|
|
||||||
assertEq(terms[0], 420);
|
assertEq(terms[0], 420);
|
||||||
assertEq(terms[1], 1337);
|
assertEq(terms[1], 1337);
|
||||||
@ -468,12 +469,12 @@ contract GatekeeperTest is Test {
|
|||||||
0x10121ffe0cc95cf3cbb4e92d72d61feec3e94045185cd7f3c5ce0d01e3a644ea
|
0x10121ffe0cc95cf3cbb4e92d72d61feec3e94045185cd7f3c5ce0d01e3a644ea
|
||||||
);
|
);
|
||||||
|
|
||||||
// execute close, exodusSession: 10
|
// execute create, exodusSession: 10
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
gatekeeper.verify(
|
gatekeeper.verify(
|
||||||
hex"31371426000000000000000000000000000000000000000000000000000000000000000a0000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000144a0139f41000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000053900000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000000005390000000000000000000000000101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000",
|
hex"31371426000000000000000000000000000000000000000000000000000000000000000a0000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000164206eb0f8000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000000000000000000000000001a40000000000000000000000000000000000000000000000000000000000000539000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000000005390000000000000000000000000101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000",
|
||||||
0xb354049d0012ef7ec3edac6aba453daf4bdc2d28fe1d88138f94459799e2343f,
|
0x18c5c0b98ac8221b75b1f7bf3e36fc0d820d5866b23010f4861d8602066f93b0,
|
||||||
0x13a6f8b10007b41fa08c8efee85501fac2554571af99fe587b534fe28fdb82a3
|
0x45db6a091ddc9d19156c6d2f1d3aca7541733faeebdeed7b8572192d050e4e06
|
||||||
);
|
);
|
||||||
|
|
||||||
// execute enable, exodusSession: 13
|
// execute enable, exodusSession: 13
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user