PI-10: Fix Cultivation Gauge and Revise Parameters
Executed on June 19, 2025 at 03:57 UTC.
Summary
- Cultivation System: Upgrade the Cultivation System to handle two edge cases.
- Soil Sold Out Threshold: Upgrade the mechanism that determines if Soil sells out to be more manipulation resistant.
- Convert Down Penalty: Update the Convert Down Penalty Gauge to issue the penalty as a function of instead of .
- Batch Plot Transfers: Restore batch Plot transfers with correct allowance checks.
- Plot Listing Payments: Prevent Plot Listings from being filled with zero Pinto and correct the rounding boundary.
Links
- Implementation Commit: 04d7dba2e3094b65826a563ad1b4bf733f678e3d
Problems
1. Cultivation System
With the introduction of Tractor support for Sowing, there was an inefficiency discovered in how the Cultivation System worked.
In particular, the Cultivation Factor decreased when Soil did not sell out. In instances where there was a Tractor Requisition to Sow at the Maximum Temperature in a Season in which Soil sold out, and there were no Tractor Requisitions to Sow at lower Temperatures, the Cultivation Factor got "stuck" oscillating at the level it was prior to the Tractor Requisition being filled (typically the minimum Cultivation Factor). The result was that the available Soil never increased, even if there was demand to Sow in excess of the available Soil. In practice this had resulted in the Field never offering enough Soil to meaningfully affect the state of the system.
Additionally, there was no Steady case in which the Cultivation Factor stayed the same, which could lead to suboptimal numbers of Soil being issued in the instance where there was a consistent amount of demand for Soil across multiple Seasons.
2. Soil Sold Out Threshold
The threshold at which the protocol considered Soil to be sold out was not manipulation resistant.
3. Convert Down Penalty
The Convert Down Penalty was insufficiently aggressive in instances where the L2SR was Reasonably Low.
4. Batch Plot Transfers
A bug in the protocol allowed a malicious actor to bypass the approval process and transfer another user's Plots without their permission. The vulnerable functionality was removed in an Emergency PI leaving users unable to transfer multiple Plots in a gas efficient way.
5. Plot Listing Payments
A bug in the protocol allowed small Plot Listings (podAmount <= 1e6) with a certain Pod price to be purchased without Pinto.
Solutions
1. Cultivation System
Introduce a new threshold in which the protocol considers Soil mostly sold out.
Set the "Mostly Sold Out" threshold as a function of the Soil issued at the start of the Season, , and the Sold Out threshold (see below):
.
Introduce the Cultivation Temperature, which is set at the previous Season’s Maximum Temperature when Soil is Sold Out or Mostly Sold Out, and demand for Soil is steady or increasing.
Update the Cultivation Factor Gauge to increase, stay the same, or decrease based on the following logic: increase when Sold Out. Remain unchanged when Mostly Sold Out, or when demand is decreasing and the previous Season’s Maximum Temperature is below the Cultivation Temperature. Decrease otherwise. The Cultivation Factor remains between 1% and 100%.
2. Soil Sold Out Threshold
Update the "Sold Out" threshold to be a function of Soil issued at the start of the Season, , as:
3. Convert Down Penalty
Update the Convert Down Penalty such that the penalty is a function of , rather than . Here, denotes the previous Season’s L2SR, the lower bound (12%) replaces the optimal bound (40%), is the rolling counter after its current update, and is its cap of 12. The counter increases by 1 when time-weighted deltaB is positive and decreases by 1 otherwise, bounded between 0 and 12. At zero L2SR, the prior penalty ratio is retained instead of using this formula. The penalty is waived when the Pinto price in the conversion Well exceeds the Excessively High Price threshold. A minimum number of Grown Stalk is retained to keep the converted Deposit from germinating.
4. Batch Plot Transfers
Reintroduce the transferPlots function to properly decrement the allowance of a sender when sending another Farmer's Plot(s).
5. Plot Listing Payments
Update the fillPodListing function to revert when a user attempts to fill a Listing with 0 Pinto. Tighten the remaining-Pod rounding comparison to prevent an entire additional payment unit from being rounded away.
Rationale
1. Cultivation System
The Cultivation Factor getting stuck such that it never increases effectively cripples the efficacy of the Field. By tracking the Cultivation Temperature the protocol is able to prevent itself from getting stuck and reenables the Field to meaningfully contribute to peg maintenance.
Introducing a Steady case to the Cultivation Factor changes means that the protocol can more granularly adjust the number of available Soil, leading to even more efficient Soil issuance.
2. Soil Sold Out Threshold
Prior to this upgrade Soil was considered Sold Out if at most one Soil was not sold. Because the number was so low, there was the potential for manipulation that could prevent the protocol from logging Soil being Sold Out by someone Sowing all but just over 1 Soil.
When initial Soil is at least 50, less than 5 Soil remaining qualifies as Sold Out. With low gas costs, a Farmer with demand is likely to Sow the remaining Soil. When initial Soil is at least 500, the threshold is capped at 50 Soil. The cap was chosen to leave enough value that a Farmer with demand is expected to find Sowing worthwhile, while avoiding a threshold that grows indefinitely with Soil issuance.
3. Convert Down Penalty
By changing the Convert Down Penalty to be 100% when the counter is zero, the Pinto price in the conversion Well does not exceed the Excessively High Price threshold, and the L2SR is at least the Excessively Low L2SR threshold instead of the Optimal L2SR threshold, the protocol will have at least one Season where the penalty will be 100% in more instances when the Pinto price crosses the value target. This should cause TWA∆P > 0 and a mint to occur, which in instances where the L2SR is not Excessively Low would generally be healthy for the protocol. A 100% penalty does not burn all Grown Stalk. The minimum retained Grown Stalk keeps the converted Deposit from germinating.
4. Batch Plot Transfers
Correct allowance accounting restores batch transfers while requiring a delegated sender to have sufficient approval for the total number of Pods transferred.
5. Plot Listing Payments
Requiring a positive payment prevents a zero-Pinto fill. The stricter rounding boundary prevents an entire additional payment unit from being rounded away.
Contract Changes
Initialization
Executed InitPI10.init() at 0xcc7578d84785c30a45306c8afc75955ba1767709. Extended the Cultivation Gauge data from four to six uint256 values, preserving its existing adjustment and factor bounds and initializing cultivationTemp and prevSeasonTemp to zero. The stored Cultivation Factor and Convert Down Penalty value were unchanged by initialization. The new stored initialSoil field was not populated by the initializer. Both new threshold getters returned zero at the execution block, before the next Sunrise stored the new Season’s initial Soil.
Facet Changes
Replaced four facets and added five selectors: two Soil threshold getters, two Gauge-result getters, and the restored transferPlots function. Other selectors in these facets were replaced without implying that every function changed.
| Facet | Action | Previous Implementation | New Implementation |
GaugeFacet | Add / Replace | 0x42575b2f01d18cf3ed670c9fe82ee90332739d03 | 0x046c76aee5d109fba8708ee58a50d76eea1f98dc |
SeasonFacet | Replace | 0x5545a4ab5cd3f6e7db0883e73ea4805f6e1270a9 | 0x9c74f623d22c4132211c9a8c605de7d2cda01700 |
FieldFacet | Add / Replace | 0x0b5888a4c6fb90d26fb4d5f782d45c9e86dcc974 | 0x36e4dd6f2be854962174c3e398cd0e11d49e404c |
MarketplaceFacet | Add / Replace | 0x1f4504295c5bd57cdbd8caf4df9ab13381cb0ad4 | 0x5aeb7c0a256b167bae026056d32818539b1734b4 |
Other Contract / Parameter Changes
SeasonFacet linked the libraries listed below. GaugeFacet, FieldFacet and MarketplaceFacet have no externally linked libraries. LibEvaluate changed demand classification. LibWeather is a new external dependency containing the Temperature logic and the previous-Season Temperature update. The other six libraries have new deployment addresses but unchanged own-source files. Shared compiled dependencies may still differ.
| Library | Previous Implementation | New Implementation | Used By |
LibEvaluate | 0x311877f9ffbaf12b905df3e1f8c3b5f3c5527bd4 | 0x1687f3eccced54e4ea4d98ffa13e2108954501e5 | SeasonFacet |
LibGauge | 0x28d436b9bbbe755dea4db747483f693d71847984 | 0xfa27f1b255a02a929a7a4e668066ce76cadcc09c | SeasonFacet |
LibIncentive | 0x09240a6c83350caaf110d9de6e23a659625d5476 | 0xb3f71bf8362cbaafebb99e1e5fac02c85e1ecfcc | SeasonFacet |
LibShipping | 0x3e3442a75ed4dd47e64e4522da625701049d6477 | 0x166ab367e077e65f1a4f273f05d8c25a3c3ea398 | SeasonFacet |
LibWellMinting | 0x6dd836e7fe603bd73532bf6b5cbb188b7fcc7fa9 | 0xd76d2011647f57071cca0d182f0d524abeaee84e | SeasonFacet |
LibFlood | 0xff823fbb0ae114b8e41c0529c7d87702cc5d06a8 | 0x20a9aeac56fee169dec4d8a5c454e818b55fa8af | SeasonFacet |
LibGerminate | 0x74f0910564523a9a9a0b4db23505f653af20548c | 0xaedabcc13a7c44d612cb3776b6a2f3a81fd3d932 | SeasonFacet |
LibWeather | N/A — new dependency | 0x82e2cfe47cf2ddd23755f81abb1349bdd96b5bfe | SeasonFacet |
Changed the fixed 1 Soil sellout threshold to min(50 Soil, 10% of initial Soil), and added a Mostly Sold Out threshold equal to the sold-out threshold plus 20% of the remaining initial Soil. Integer calculations round down in 6-decimal Soil units. Changed the Convert Down Penalty scaling denominator from the optimal (40%) to the lower L2SR bound (12%). Added an L2SR input cap at the lower bound, while retaining the final 100% penalty cap. The counter rate of 1 per Season and cap of 12 were unchanged.
Function Changes
- Updated
cultivationFactor(bytes,bytes,bytes)to use six-field Gauge data and distinguish Sold Out, Mostly Sold Out and other demand conditions. It returns encoded value and data. Sold Out increases the factor. Mostly Sold Out preserves it. Otherwise, decreasing demand withprevSeasonTemp < cultivationTemppreserves it, and other cases decrease it. The existing zero-price return and factor bounds remain.
- Updated the Sunrise path through
gmandsunrise.LibWeather.updateTemperaturestores the previous Maximum Temperature before adjusting it, andSun.setSoilstoresinitialSoilfor the new Season.LibEvaluate.calcDeltaPodDemanddistinguishes the Mostly Sold Out sentinel from elapsed sellout time, so Mostly Sold Out alone does not trigger the sellout-time demand override.
- Added
getGaugeResult(Gauge,bytes)andgetGaugeIdResult(GaugeId,bytes), which return the computed encoded Gauge value and data without storing them. Both useLibGaugeHelpers.getGaugeResult, retaining its original value/data fallback for a missing selector or failed static call.
- Updated
sowandsowWithMinthroughLibDibbler._saveSowTimeto record Mostly Sold Out and Sold Out states. An initial observation exactly at the sold-out threshold records Mostly Sold Out. After that state is recorded, a subsequent qualifying Sow at or below the threshold records Sold Out.
- Added
getSoilSoldOutThreshold()andgetSoilMostlySoldOutThreshold(), each returning auint256threshold in 6-decimal Soil units calculated from storedinitialSoil. The existinginitialSoil()getter remains unchanged and returns remainings.sys.soil, not the new stored starting-Soil field.
- Updated
convertDownPenaltyGauge(bytes,bytes,bytes)to scale againstlpToSupplyRatioLowerBoundinstead oflpToSupplyRatioOptimal. The new input cap means L2SR above the lower bound cannot offset the counter’s penalty decay. The return remains encoded penalty ratio and rolling counter, plus Gauge data. The counter updates before the zero-L2SR fallback, which retains the prior penalty ratio. This change does not remove the existing downstream price bypass or minimum retained Grown Stalk.
- Re-added
transferPlotswith the correctedfieldIdandtotalAmountargument order when decrementing delegated Pod allowance. Updated shareddecrementAllowancePodsto reject zero allowance as well as insufficient allowance.transferPlotalso uses this helper. Owner transfers and unlimited approvals retain their existing exceptions.
- Updated
fillPodListingthrough_fillListingto requirebeanPayAmount > 0and changed the remaining-Pod rounding comparison from<=to<against1000000 / pricePerPod. A zero-payment fill now reverts withMarketplace: Zero payment.
Event Changes
- Added
EngagedData(uint8,bytes), emitted alongside the existingEngagedevent when a Gauge is engaged. AddedUpdatedGaugeData(uint8,bytes), emitted by the initializer and whenLibWeatherrecords the previous Season’s Maximum Temperature.TemperatureChangeandBeanToMaxLpGpPerBdvRatioChangemoved toLibWeatherwith unchanged event signatures.
- Added
SoilMostlySoldOut(uint256)andSoilSoldOut(uint256), emitting seconds since the Season started when the corresponding state is first recorded. A Sow directly below the sold-out threshold can emit onlySoilSoldOut.