- 
                Notifications
    You must be signed in to change notification settings 
- Fork 2
[3.] New formula for Curve pricer #114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
cdb7a3b
              2432aa1
              189cedf
              7dfe0e2
              3e55ae1
              6c0b989
              fc27225
              224b79c
              f173ce5
              bbe2620
              b8b081e
              05d0581
              47fd38b
              d033996
              39c58d5
              e193c01
              63f4688
              df9e548
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -20,3 +20,4 @@ docker*.tgz | |
|  | ||
| # We don't ever use the generated manifests | ||
| .openzeppelin | ||
| /.vscode | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -12,10 +12,12 @@ import { ARegistryWired } from "../registry/ARegistryWired.sol"; | |
| * @title Implementation of the Curve Pricing, module that calculates the price of a domain | ||
| * based on its length and the rules set by Zero ADMIN. | ||
| * This module uses an asymptotic curve that starts from `maxPrice` for all domains <= `baseLength`. | ||
| * It then decreases in price, using the calculated price function below, until it reaches `minPrice` | ||
| * at `maxLength` length of the domain name. Price after `maxLength` is fixed and always equal to `minPrice`. | ||
| * It then decreases in price, using the calculated price function below. | ||
| * At `maxLength` length of the domain name. Price after `maxLength` is fixed | ||
|         
                  MichaelKorchagin marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| * and is determined using the formula where `length` = `maxLength`. | ||
| */ | ||
| contract ZNSCurvePricer is AAccessControlled, ARegistryWired, UUPSUpgradeable, IZNSCurvePricer { | ||
|  | ||
| using StringUtils for string; | ||
|  | ||
| /** | ||
|  | @@ -24,6 +26,12 @@ contract ZNSCurvePricer is AAccessControlled, ARegistryWired, UUPSUpgradeable, I | |
| */ | ||
| uint256 public constant PERCENTAGE_BASIS = 10000; | ||
|  | ||
| /** | ||
| * @notice Value used as a basis to multiply the multiplier, | ||
| * since Solidity does not support fractions. | ||
|         
                  MichaelKorchagin marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| */ | ||
| uint256 public constant MULTIPLIER_BASIS = 1000; | ||
|         
                  MichaelKorchagin marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
|  | ||
| /** | ||
| * @notice Mapping of domainHash to the price config for that domain set by the parent domain owner. | ||
| * @dev Zero, for pricing root domains, uses this mapping as well under 0x0 hash. | ||
|  | @@ -41,7 +49,6 @@ contract ZNSCurvePricer is AAccessControlled, ARegistryWired, UUPSUpgradeable, I | |
| * @dev > Note the for PriceConfig we set each value individually and calling | ||
| * 2 important functions that validate all of the config's values against the formula: | ||
| * - `setPrecisionMultiplier()` to validate precision multiplier | ||
| * - `_validateConfig()` to validate the whole config in order to avoid price spikes | ||
| * @param accessController_ the address of the ZNSAccessController contract. | ||
| * @param registry_ the address of the ZNSRegistry contract. | ||
| * @param zeroPriceConfig_ a number of variables that participate in the price calculation for subdomains. | ||
|  | @@ -122,9 +129,9 @@ contract ZNSCurvePricer is AAccessControlled, ARegistryWired, UUPSUpgradeable, I | |
|  | ||
| /** | ||
| * @notice Setter for `priceConfigs[domainHash]`. Only domain owner/operator can call this function. | ||
| * @dev Validates the value of the `precisionMultiplier` and the whole config in order to avoid price spikes, | ||
| * @dev Validates the value of the `precisionMultiplier`. | ||
| * fires `PriceConfigSet` event. | ||
| * Only the owner of the domain or an allowed operator can call this function | ||
| * Only the owner of the domain or an allowed operator can call this function. | ||
| * > This function should ALWAYS be used to set the config, since it's the only place where `isSet` is set to true. | ||
| * > Use the other individual setters to modify only, since they do not set this variable! | ||
| * @param domainHash The domain hash to set the price config for | ||
|  | @@ -137,7 +144,7 @@ contract ZNSCurvePricer is AAccessControlled, ARegistryWired, UUPSUpgradeable, I | |
| setPrecisionMultiplier(domainHash, priceConfig.precisionMultiplier); | ||
| priceConfigs[domainHash].baseLength = priceConfig.baseLength; | ||
| priceConfigs[domainHash].maxPrice = priceConfig.maxPrice; | ||
| priceConfigs[domainHash].minPrice = priceConfig.minPrice; | ||
| priceConfigs[domainHash].curveMultiplier = priceConfig.curveMultiplier; | ||
| priceConfigs[domainHash].maxLength = priceConfig.maxLength; | ||
| setFeePercentage(domainHash, priceConfig.feePercentage); | ||
|         
                  MichaelKorchagin marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| priceConfigs[domainHash].isSet = true; | ||
|  | @@ -147,7 +154,7 @@ contract ZNSCurvePricer is AAccessControlled, ARegistryWired, UUPSUpgradeable, I | |
| emit PriceConfigSet( | ||
| domainHash, | ||
| priceConfig.maxPrice, | ||
| priceConfig.minPrice, | ||
| priceConfig.curveMultiplier, | ||
| priceConfig.maxLength, | ||
| priceConfig.baseLength, | ||
| priceConfig.precisionMultiplier, | ||
|  | @@ -159,9 +166,9 @@ contract ZNSCurvePricer is AAccessControlled, ARegistryWired, UUPSUpgradeable, I | |
| * @notice Sets the max price for domains. Validates the config with the new price. | ||
| * Fires `MaxPriceSet` event. | ||
| * Only domain owner can call this function. | ||
| * > `maxPrice` can be set to 0 along with `baseLength` or `minPrice` to make all domains free! | ||
| * @dev We are checking here for possible price spike at `maxLength` if the `maxPrice` values is NOT 0. | ||
| * In the case of 0 we do not validate, since setting it to 0 will make all subdomains free. | ||
| * > `maxPrice` can be set to 0 along with `baseLength` to make all domains free! | ||
| * @dev In the case of 0 we do not validate, since setting it to 0 will make all subdomains free. | ||
| * @param domainHash The domain hash to set the `maxPrice` for it | ||
| * @param maxPrice The maximum price to set | ||
| */ | ||
| function setMaxPrice( | ||
|  | @@ -176,21 +183,24 @@ contract ZNSCurvePricer is AAccessControlled, ARegistryWired, UUPSUpgradeable, I | |
| } | ||
|  | ||
| /** | ||
| * @notice Sets the minimum price for domains. Validates the config with the new price. | ||
| * Fires `MinPriceSet` event. | ||
| * Only domain owner/operator can call this function. | ||
| * @param domainHash The domain hash to set the `minPrice` for | ||
| * @param minPrice The minimum price to set in $ZERO | ||
| * @notice Sets the multiplier for domains calculations | ||
| * to allow the hyperbolic price curve to be bent all the way to a straight line. | ||
| * Validates the config with the new multiplier in case where `baseLength` is 0 too. | ||
| * Fires `CurveMultiplier` event. | ||
| * Only domain owner can call this function. | ||
| * > `curveMultiplier` can be set to 0 to set a maximum price for all domains! | ||
|         
                  MichaelKorchagin marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| * @param domainHash The domain hash to set the price config for | ||
| * @param curveMultiplier Multiplier for bending the price function (graph) | ||
| */ | ||
| function setMinPrice( | ||
| function setCurveMultiplier( | ||
| bytes32 domainHash, | ||
| uint256 minPrice | ||
| uint256 curveMultiplier | ||
| ) external override onlyOwnerOrOperator(domainHash) { | ||
| priceConfigs[domainHash].minPrice = minPrice; | ||
| priceConfigs[domainHash].curveMultiplier = curveMultiplier; | ||
|          | ||
|  | ||
| _validateConfig(domainHash); | ||
|  | ||
| emit MinPriceSet(domainHash, minPrice); | ||
| emit CurveMultiplierSet(domainHash, curveMultiplier); | ||
| } | ||
|  | ||
| /** | ||
|  | @@ -218,12 +228,11 @@ contract ZNSCurvePricer is AAccessControlled, ARegistryWired, UUPSUpgradeable, I | |
|  | ||
| /** | ||
| * @notice Set the maximum length of a domain name to which price formula applies. | ||
| * All domain names (labels) that are longer than this value will cost the fixed price of `minPrice`, | ||
| * and the pricing formula will not apply to them. | ||
| * All domain names (labels) that are longer than this value will cost the lowest price at maxLength. | ||
| * Validates the config with the new length. | ||
| * Fires `MaxLengthSet` event. | ||
| * Only domain owner/operator can call this function. | ||
| * > `maxLength` can be set to 0 to make all domains cost `minPrice`! | ||
| * > `maxLength` can be set to 0! | ||
|         
                  MichaelKorchagin marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| * @param domainHash The domain hash to set the `maxLength` for | ||
| * @param length The maximum length to set | ||
| */ | ||
|  | @@ -233,6 +242,8 @@ contract ZNSCurvePricer is AAccessControlled, ARegistryWired, UUPSUpgradeable, I | |
| ) external override onlyOwnerOrOperator(domainHash) { | ||
| priceConfigs[domainHash].maxLength = length; | ||
|  | ||
| _validateConfig(domainHash); | ||
|         
                  MichaelKorchagin marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
|  | ||
| if (length != 0) _validateConfig(domainHash); | ||
|  | ||
| emit MaxLengthSet(domainHash, length); | ||
|  | @@ -247,14 +258,14 @@ contract ZNSCurvePricer is AAccessControlled, ARegistryWired, UUPSUpgradeable, I | |
| * Fires `PrecisionMultiplierSet` event. | ||
| * Only domain owner/operator can call this function. | ||
| * > Multiplier should be less or equal to 10^18 and greater than 0! | ||
| * @param domainHash The domain hash to set `PrecisionMultiplier` | ||
| * @param multiplier The multiplier to set | ||
| */ | ||
| function setPrecisionMultiplier( | ||
| bytes32 domainHash, | ||
| uint256 multiplier | ||
| ) public override onlyOwnerOrOperator(domainHash) { | ||
| if (multiplier == 0 || multiplier > 10**18) revert InvalidMultiplierPassed(multiplier); | ||
|  | ||
| priceConfigs[domainHash].precisionMultiplier = multiplier; | ||
|  | ||
| emit PrecisionMultiplierSet(domainHash, multiplier); | ||
|  | @@ -274,7 +285,6 @@ contract ZNSCurvePricer is AAccessControlled, ARegistryWired, UUPSUpgradeable, I | |
| onlyOwnerOrOperator(domainHash) { | ||
| if (feePercentage > PERCENTAGE_BASIS) | ||
| revert FeePercentageValueTooLarge(feePercentage, PERCENTAGE_BASIS); | ||
|  | ||
| priceConfigs[domainHash].feePercentage = feePercentage; | ||
| emit FeePercentageSet(domainHash, feePercentage); | ||
| } | ||
|  | @@ -290,17 +300,25 @@ contract ZNSCurvePricer is AAccessControlled, ARegistryWired, UUPSUpgradeable, I | |
| /** | ||
| * @notice Internal function to calculate price based on the config set, | ||
| * and the length of the domain label. | ||
| * @dev Before we calculate the price, 4 different cases are possible: | ||
| * @dev Before we calculate the price, 6 different cases are possible: | ||
| * 1. `maxPrice` is 0, which means all subdomains under this parent are free | ||
| * 2. `baseLength` is 0, which means we are returning `maxPrice` as a specific price for all domains | ||
| * 3. `length` is less than or equal to `baseLength`, which means a domain will cost `maxPrice` | ||
| * 4. `length` is greater than `maxLength`, which means a domain will cost `minPrice` | ||
| * 4. `length` is greater than `maxLength`, which means a domain will cost price by fomula at `maxLength` | ||
| * 5. The numerator can be less than the denominator, which is achieved by setting a huge value | ||
| * for `curveMultiplier` or by decreasing the `baseLength` and `maxPrice`, which means all domains | ||
| * which are longer than `baseLength` will be for free. | ||
|         
                  MichaelKorchagin marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| * 6. `curveMultiplier` is 0,which means all domains will cost `maxPrice`. | ||
| * | ||
| * The formula itself creates an asymptotic curve that decreases in pricing based on domain name length, | ||
| * base length and max price, the result is divided by the precision multiplier to remove numbers beyond | ||
| * The formula itself creates an hyperbolic curve that decreases in pricing based on domain name length, | ||
| * base length, max price and curve multiplier. | ||
| * `MULTIPLIER_BASIS` allows to perceive `curveMultiplier` as fraction number in regular formula, | ||
| * which helps to bend a curve of price chart. | ||
| * The result is divided by the precision multiplier to remove numbers beyond | ||
| * what we care about, then multiplied by the same precision multiplier to get the actual value | ||
| * with truncated values past precision. So having a value of `15.235234324234512365 * 10^18` | ||
| * with precision `2` would give us `15.230000000000000000 * 10^18` | ||
| * @param parentHash The parent hash | ||
| * @param length The length of the domain name | ||
| */ | ||
| function _getPrice( | ||
|  | @@ -317,26 +335,32 @@ contract ZNSCurvePricer is AAccessControlled, ARegistryWired, UUPSUpgradeable, I | |
| // e.g. promotions or sales | ||
| if (config.baseLength == 0) return config.maxPrice; | ||
| if (length <= config.baseLength) return config.maxPrice; | ||
| if (length > config.maxLength) return config.minPrice; | ||
|  | ||
| return (config.baseLength * config.maxPrice / length) | ||
| / config.precisionMultiplier * config.precisionMultiplier; | ||
| if (length > config.maxLength) length = config.maxLength; | ||
|  | ||
| // | ||
| return ((config.baseLength * config.maxPrice * MULTIPLIER_BASIS) / ( | ||
| config.baseLength * MULTIPLIER_BASIS + config.curveMultiplier * (length - config.baseLength) | ||
| )) | ||
| / config.precisionMultiplier * config.precisionMultiplier; | ||
|         
                  MichaelKorchagin marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| } | ||
|  | ||
| /** | ||
| * @notice Internal function called every time we set props of `priceConfigs[domainHash]` | ||
| * to make sure that values being set can not disrupt the price curve or zero out prices | ||
| * for domains. If this validation fails, the parent function will revert. | ||
| * @dev We are checking here for possible price spike at `maxLength` | ||
| * which can occur if some of the config values are not properly chosen and set. | ||
| * @dev We are checking here for possible incorrect passed values: `maxLength`, `baselength` or `curveMultiplier`. | ||
| * @param domainHash The domain hash to validate its config | ||
| */ | ||
| function _validateConfig(bytes32 domainHash) internal view { | ||
| uint256 prevToMinPrice = _getPrice(domainHash, priceConfigs[domainHash].maxLength); | ||
| if (priceConfigs[domainHash].minPrice > prevToMinPrice) | ||
| revert InvalidConfigCausingPriceSpikes( | ||
| domainHash, | ||
| priceConfigs[domainHash].minPrice, | ||
| prevToMinPrice | ||
| if (priceConfigs[domainHash].maxLength < priceConfigs[domainHash].baseLength) | ||
|         
                  MichaelKorchagin marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| revert InvalidBaseLengthOrMaxLength( | ||
| domainHash | ||
| ); | ||
|  | ||
| if (priceConfigs[domainHash].baseLength == 0 && priceConfigs[domainHash].curveMultiplier == 0) | ||
|         
                  MichaelKorchagin marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| revert DivisionByZero( | ||
| domainHash | ||
| ); | ||
| } | ||
|  | ||
|  | ||
Uh oh!
There was an error while loading. Please reload this page.