Skip to content

Commit 7035d04

Browse files
committed
refactor: accounting for PR comments
1 parent bb35f9a commit 7035d04

File tree

4 files changed

+99
-126
lines changed

4 files changed

+99
-126
lines changed

contracts/BondCalculator.sol

+86-17
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.25;
33

4-
import {GenericBondCalculator} from "./GenericBondCalculator.sol";
4+
import {mulDiv} from "@prb/math/src/Common.sol";
55
import {IVotingEscrow} from "./interfaces/IVotingEscrow.sol";
6+
import "./interfaces/IUniswapV2Pair.sol";
67

78
interface ITokenomics {
89
/// @dev Gets number of new units that were donated in the last epoch.
@@ -63,12 +64,16 @@ struct Product {
6364
/// @author Aleksandr Kuperman - <[email protected]>
6465
/// @author Andrey Lebedev - <[email protected]>
6566
/// @author Mariapia Moscatiello - <[email protected]>
66-
contract BondCalculator is GenericBondCalculator {
67+
contract BondCalculator {
6768
event OwnerUpdated(address indexed owner);
6869
event DiscountParamsUpdated(DiscountParams newDiscountParams);
6970

7071
// Maximum sum of discount factor weights
7172
uint256 public constant MAX_SUM_WEIGHTS = 10_000;
73+
// OLAS contract address
74+
address public immutable olas;
75+
// Tokenomics contract address
76+
address public immutable tokenomics;
7277
// veOLAS contract address
7378
address public immutable ve;
7479

@@ -83,14 +88,14 @@ contract BondCalculator is GenericBondCalculator {
8388
/// @param _tokenomics Tokenomics contract address.
8489
/// @param _ve veOLAS contract address.
8590
/// @param _discountParams Discount factor parameters.
86-
constructor(address _olas, address _tokenomics, address _ve, DiscountParams memory _discountParams)
87-
GenericBondCalculator(_olas, _tokenomics)
88-
{
89-
// Check for zero address
90-
if (_ve == address(0)) {
91+
constructor(address _olas, address _tokenomics, address _ve, DiscountParams memory _discountParams) {
92+
// Check for at least one zero contract address
93+
if (_olas == address(0) || _tokenomics == address(0) || _ve == address(0)) {
9194
revert ZeroAddress();
9295
}
9396

97+
olas = _olas;
98+
tokenomics = _tokenomics;
9499
ve = _ve;
95100
owner = msg.sender;
96101

@@ -153,17 +158,14 @@ contract BondCalculator is GenericBondCalculator {
153158
}
154159

155160
/// @dev Calculated inverse discount factor based on bonding and account parameters.
156-
/// @param data Custom data that is used to calculate the IDF:
157-
/// - account Account address.
158-
/// - bondVestingTime Bond vesting time.
159-
/// - productMaxVestingTime Product max vesting time.
160-
/// - productSupply Current product supply.
161-
/// - productPayout Current product payout.
161+
/// @param account Account address.
162+
/// @param bondVestingTime Bond vesting time.
163+
/// @param productMaxVestingTime Product max vesting time.
164+
/// @param productSupply Current product supply.
165+
/// @param productPayout Current product payout.
162166
/// @return idf Inverse discount factor in 18 decimals format.
163-
function calculateIDF(bytes memory data) public view override returns (uint256 idf) {
164-
// Decode the required data
165-
(address account, uint256 bondVestingTime, uint256 productMaxVestingTime, uint256 productSupply,
166-
uint256 productPayout) = abi.decode(data, (address, uint256, uint256, uint256, uint256));
167+
function calculateIDF(address account, uint256 bondVestingTime, uint256 productMaxVestingTime, uint256 productSupply,
168+
uint256 productPayout) public view returns (uint256 idf) {
167169

168170
// Get the copy of the discount params
169171
DiscountParams memory localParams = discountParams;
@@ -226,6 +228,73 @@ contract BondCalculator is GenericBondCalculator {
226228
idf = 1e18 + discountBooster;
227229
}
228230

231+
/// @dev Calculates the amount of OLAS tokens based on the bonding calculator mechanism accounting for dynamic IDF.
232+
/// @param account Account address.
233+
/// @param tokenAmount LP token amount.
234+
/// @param priceLP LP token price.
235+
/// @param bondVestingTime Bond vesting time.
236+
/// @param productMaxVestingTime Product max vesting time.
237+
/// @param productSupply Current product supply.
238+
/// @param productPayout Current product payout.
239+
/// @return amountOLAS Resulting amount of OLAS tokens.
240+
function calculatePayoutOLAS(
241+
address account,
242+
uint256 tokenAmount,
243+
uint256 priceLP,
244+
uint256 bondVestingTime,
245+
uint256 productMaxVestingTime,
246+
uint256 productSupply,
247+
uint256 productPayout
248+
) external view returns (uint256 amountOLAS) {
249+
// The result is divided by additional 1e18, since it was multiplied by in the current LP price calculation
250+
// The resulting amountDF can not overflow by the following calculations: idf = 64 bits;
251+
// priceLP = 2 * r0/L * 10^18 = 2*r0*10^18/sqrt(r0*r1) ~= 61 + 96 - sqrt(96 * 112) ~= 53 bits (if LP is balanced)
252+
// or 2* r0/sqrt(r0) * 10^18 => 87 bits + 60 bits = 147 bits (if LP is unbalanced);
253+
// tokenAmount is of the order of sqrt(r0*r1) ~ 104 bits (if balanced) or sqrt(96) ~ 10 bits (if max unbalanced);
254+
// overall: 64 + 53 + 104 = 221 < 256 - regular case if LP is balanced, and 64 + 147 + 10 = 221 < 256 if unbalanced
255+
// mulDiv will correctly fit the total amount up to the value of max uint256, i.e., max of priceLP and max of tokenAmount,
256+
// however their multiplication can not be bigger than the max of uint192
257+
uint256 totalTokenValue = mulDiv(priceLP, tokenAmount, 1);
258+
// Check for the cumulative LP tokens value limit
259+
if (totalTokenValue > type(uint192).max) {
260+
revert Overflow(totalTokenValue, type(uint192).max);
261+
}
262+
263+
// Calculate the dynamic inverse discount factor
264+
uint256 idf = calculateIDF(account, bondVestingTime, productMaxVestingTime, productSupply, productPayout);
265+
266+
// Amount with the discount factor is IDF * priceLP * tokenAmount / 1e36
267+
// At this point of time IDF is bound by the max of uint64, and totalTokenValue is no bigger than the max of uint192
268+
amountOLAS = (idf * totalTokenValue) / 1e36;
269+
}
270+
271+
/// @dev Gets current reserves of OLAS / totalSupply of Uniswap V2-like LP tokens.
272+
/// @notice The price LP calculation is based on the UniswapV2Pair contract.
273+
/// @param token Token address.
274+
/// @return priceLP Resulting reserveX / totalSupply ratio with 18 decimals.
275+
function getCurrentPriceLP(address token) external view returns (uint256 priceLP) {
276+
IUniswapV2Pair pair = IUniswapV2Pair(token);
277+
uint256 totalSupply = pair.totalSupply();
278+
if (totalSupply > 0) {
279+
address token0 = pair.token0();
280+
address token1 = pair.token1();
281+
uint256 reserve0;
282+
uint256 reserve1;
283+
// requires low gas
284+
(reserve0, reserve1, ) = pair.getReserves();
285+
// token0 != olas && token1 != olas, this should never happen
286+
if (token0 == olas || token1 == olas) {
287+
// If OLAS is in token0, assign its reserve to reserve1, otherwise the reserve1 is already correct
288+
if (token0 == olas) {
289+
reserve1 = reserve0;
290+
}
291+
// Calculate the LP price based on reserves and totalSupply ratio multiplied by 1e18
292+
// Inspired by: https://github.com/curvefi/curve-contract/blob/master/contracts/pool-templates/base/SwapTemplateBase.vy#L262
293+
priceLP = (reserve1 * 1e18) / totalSupply;
294+
}
295+
}
296+
}
297+
229298
function getDiscountParams() external view returns (DiscountParams memory) {
230299
return discountParams;
231300
}

contracts/Depository.sol

+13-6
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,22 @@ import {ITreasury} from "./interfaces/ITreasury.sol";
99

1010
interface IBondCalculator {
1111
/// @dev Calculates the amount of OLAS tokens based on the bonding calculator mechanism accounting for dynamic IDF.
12+
/// @param account Account address.
1213
/// @param tokenAmount LP token amount.
1314
/// @param priceLP LP token price.
14-
/// @param data Custom data that is used to calculate the IDF.
15+
/// @param bondVestingTime Bond vesting time.
16+
/// @param productMaxVestingTime Product max vesting time.
17+
/// @param productSupply Current product supply.
18+
/// @param productPayout Current product payout.
1519
/// @return amountOLAS Resulting amount of OLAS tokens.
1620
function calculatePayoutOLAS(
21+
address account,
1722
uint256 tokenAmount,
1823
uint256 priceLP,
19-
bytes memory data
24+
uint256 bondVestingTime,
25+
uint256 productMaxVestingTime,
26+
uint256 productSupply,
27+
uint256 productPayout
2028
) external view returns (uint256 amountOLAS);
2129

2230
/// @dev Gets current reserves of OLAS / totalSupply of Uniswap V2-like LP tokens.
@@ -114,7 +122,7 @@ contract Depository is ERC721, IErrorsTokenomics {
114122

115123
// OLAS token address
116124
address public immutable olas;
117-
// Tkenomics contract address
125+
// Tokenomics contract address
118126
address public tokenomics;
119127
// Treasury contract address
120128
address public treasury;
@@ -370,9 +378,8 @@ contract Depository is ERC721, IErrorsTokenomics {
370378

371379
// Calculate the payout in OLAS tokens based on the LP pair with the inverse discount factor (IDF) calculation
372380
// Note that payout cannot be zero since the price LP is non-zero, otherwise the product would not be created
373-
payout = IBondCalculator(bondCalculator).calculatePayoutOLAS(tokenAmount, product.priceLP,
374-
// Encode parameters required for the IDF calculation
375-
abi.encode(msg.sender, bondVestingTime, productMaxVestingTime, supply, product.payout));
381+
payout = IBondCalculator(bondCalculator).calculatePayoutOLAS(msg.sender, tokenAmount, product.priceLP,
382+
bondVestingTime, productMaxVestingTime, supply, product.payout);
376383

377384
// Check for the sufficient supply
378385
if (payout > supply) {

contracts/GenericBondCalculator.sol

-103
This file was deleted.
File renamed without changes.

0 commit comments

Comments
 (0)