Skip to content

Commit 2022b44

Browse files
authored
feat: align pricing, funding, and termination
1 parent fddde24 commit 2022b44

64 files changed

Lines changed: 3648 additions & 2504 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

costs/calculate.go

Lines changed: 145 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@ import (
44
"math/big"
55

66
"github.com/strahe/synapse-go/chain"
7+
"github.com/strahe/synapse-go/pdp"
78
"github.com/strahe/synapse-go/warmstorage"
89
)
910

1011
// CalculateEffectiveRate computes the storage rate for the given total data size.
1112
// Integer division is used to match on-chain Solidity truncation.
1213
// If epochsPerMonth is zero or negative, chain.EpochsPerMonth is used as a safe default.
13-
// Nil sizeBytes, pricePerTiBPerMonth, or minMonthlyRate are treated as zero.
14+
// Nil sizeBytes, pricePerTiBPerMonth, or datasetFeePerMonth are treated as zero.
1415
func CalculateEffectiveRate(
1516
sizeBytes *big.Int,
1617
pricePerTiBPerMonth *big.Int,
17-
minMonthlyRate *big.Int,
18+
datasetFeePerMonth *big.Int,
1819
epochsPerMonth int64,
1920
) EffectiveRate {
2021
if epochsPerMonth <= 0 {
@@ -28,43 +29,69 @@ func CalculateEffectiveRate(
2829
if pricePerTiBPerMonth == nil {
2930
pricePerTiBPerMonth = new(big.Int)
3031
}
31-
if minMonthlyRate == nil {
32-
minMonthlyRate = new(big.Int)
32+
if datasetFeePerMonth == nil {
33+
datasetFeePerMonth = new(big.Int)
34+
}
35+
36+
if sizeBytes.Sign() == 0 {
37+
return EffectiveRate{
38+
RatePerEpoch: new(big.Int),
39+
RatePerMonth: new(big.Int),
40+
}
3341
}
3442

3543
ratePerMonth := new(big.Int).Mul(pricePerTiBPerMonth, sizeBytes)
3644
ratePerMonth.Div(ratePerMonth, bigTiB)
37-
if ratePerMonth.Cmp(minMonthlyRate) < 0 {
38-
ratePerMonth.Set(minMonthlyRate)
39-
}
45+
ratePerMonth.Add(ratePerMonth, datasetFeePerMonth)
4046

4147
ratePerEpoch := new(big.Int).Mul(pricePerTiBPerMonth, sizeBytes)
4248
divisor := new(big.Int).Mul(bigTiB, epm)
4349
ratePerEpoch.Div(ratePerEpoch, divisor)
44-
45-
minEpochRate := new(big.Int).Div(minMonthlyRate, epm)
46-
if minEpochRate.Cmp(bigOne) < 0 {
47-
minEpochRate.Set(bigOne)
48-
}
49-
if ratePerEpoch.Cmp(minEpochRate) < 0 {
50-
ratePerEpoch.Set(minEpochRate)
51-
}
50+
ratePerEpoch.Add(ratePerEpoch, new(big.Int).Div(datasetFeePerMonth, epm))
5251

5352
return EffectiveRate{
5453
RatePerEpoch: ratePerEpoch,
5554
RatePerMonth: ratePerMonth,
5655
}
5756
}
5857

58+
// CalculateUploadFees computes one-time upload fees from the price list.
59+
func CalculateUploadFees(priceList *warmstorage.PriceList, isNewDataSet bool, pieceCount *big.Int) UploadFees {
60+
if priceList == nil {
61+
priceList = &warmstorage.PriceList{}
62+
}
63+
pieces := copyBigOrDefault(pieceCount, bigOne)
64+
if pieces.Sign() <= 0 {
65+
pieces.SetInt64(1)
66+
}
67+
68+
maxBatch := big.NewInt(pdp.MaxAddPiecesBatchSize)
69+
addPiecesOperationCount := new(big.Int).Add(pieces, new(big.Int).Sub(maxBatch, bigOne))
70+
addPiecesOperationCount.Div(addPiecesOperationCount, maxBatch)
71+
72+
createDataSetFee := new(big.Int)
73+
if isNewDataSet {
74+
createDataSetFee.Set(zeroBig(priceList.Fees.CreateDataSetFee))
75+
}
76+
addPiecesFee := new(big.Int).Mul(zeroBig(priceList.Fees.AddPiecesBaseFee), addPiecesOperationCount)
77+
addPiecesFee.Add(addPiecesFee, new(big.Int).Mul(zeroBig(priceList.Fees.AddPiecesPerPieceFee), pieces))
78+
total := new(big.Int).Add(createDataSetFee, addPiecesFee)
79+
80+
return UploadFees{
81+
CreateDataSetFee: createDataSetFee,
82+
AddPiecesFee: addPiecesFee,
83+
Total: total,
84+
}
85+
}
86+
5987
// CalculateAdditionalLockupRequired returns the incremental lockup needed to
6088
// store uploadSizeBytes into a dataset that currently holds currentDataSetSizeBytes.
61-
// Nil dataSizeBytes, currentDataSetSizeBytes, pricing, and usdfcSybilFee use zero-value defaults.
89+
// Nil dataSizeBytes, currentDataSetSizeBytes, and priceList use zero-value defaults.
6290
func CalculateAdditionalLockupRequired(
6391
dataSizeBytes *big.Int,
6492
currentDataSetSizeBytes *big.Int,
65-
pricing *warmstorage.ServicePrice,
66-
lockupPeriod int64,
67-
usdfcSybilFee *big.Int,
93+
priceList *warmstorage.PriceList,
94+
lockupPeriod *big.Int,
6895
isNewDataSet bool,
6996
enableCDN bool,
7097
) AdditionalLockup {
@@ -74,28 +101,24 @@ func CalculateAdditionalLockupRequired(
74101
if currentDataSetSizeBytes == nil {
75102
currentDataSetSizeBytes = new(big.Int)
76103
}
77-
if pricing == nil {
78-
pricing = &warmstorage.ServicePrice{}
79-
}
80-
var epm int64
81-
if pricing.EpochsPerMonth != nil && pricing.EpochsPerMonth.Sign() > 0 {
82-
epm = pricing.EpochsPerMonth.Int64()
104+
if priceList == nil {
105+
priceList = &warmstorage.PriceList{}
83106
}
84107

85108
var rateDelta *big.Int
86109
if currentDataSetSizeBytes.Sign() > 0 && !isNewDataSet {
87110
newTotalSize := new(big.Int).Add(currentDataSetSizeBytes, dataSizeBytes)
88111
newRate := CalculateEffectiveRate(
89112
newTotalSize,
90-
pricing.PricePerTiBPerMonthNoCDN,
91-
pricing.MinimumPricePerMonth,
92-
epm,
113+
priceList.Rates.StoragePerTiBPerMonth,
114+
priceList.Rates.DatasetFeePerMonth,
115+
chain.EpochsPerMonth,
93116
)
94117
currentRate := CalculateEffectiveRate(
95118
currentDataSetSizeBytes,
96-
pricing.PricePerTiBPerMonthNoCDN,
97-
pricing.MinimumPricePerMonth,
98-
epm,
119+
priceList.Rates.StoragePerTiBPerMonth,
120+
priceList.Rates.DatasetFeePerMonth,
121+
chain.EpochsPerMonth,
99122
)
100123
rateDelta = new(big.Int).Sub(newRate.RatePerEpoch, currentRate.RatePerEpoch)
101124
if rateDelta.Sign() < 0 {
@@ -104,37 +127,48 @@ func CalculateAdditionalLockupRequired(
104127
} else {
105128
newRate := CalculateEffectiveRate(
106129
dataSizeBytes,
107-
pricing.PricePerTiBPerMonthNoCDN,
108-
pricing.MinimumPricePerMonth,
109-
epm,
130+
priceList.Rates.StoragePerTiBPerMonth,
131+
priceList.Rates.DatasetFeePerMonth,
132+
chain.EpochsPerMonth,
110133
)
111134
rateDelta = new(big.Int).Set(newRate.RatePerEpoch)
112135
}
113136

114-
if lockupPeriod <= 0 {
115-
lockupPeriod = DefaultLockupPeriod
137+
effectiveLockupPeriod := copyBigOrDefault(lockupPeriod, priceList.Lockups.DefaultLockupPeriod)
138+
if effectiveLockupPeriod.Sign() <= 0 {
139+
effectiveLockupPeriod.SetInt64(DefaultLockupPeriod)
116140
}
117-
rateLockup := new(big.Int).Mul(rateDelta, big.NewInt(lockupPeriod))
141+
streamingLockup := new(big.Int).Mul(rateDelta, effectiveLockupPeriod)
118142

119-
cdnLockup := new(big.Int)
120-
if isNewDataSet && enableCDN {
121-
cdnLockup.Set(cdnFixedLockup)
143+
lifecycleLockup := new(big.Int)
144+
if isNewDataSet {
145+
lifecycleLockup.Set(zeroBig(priceList.Lockups.LifecycleReserveTarget))
122146
}
123147

124-
sybilFee := new(big.Int)
125-
if isNewDataSet && usdfcSybilFee != nil {
126-
sybilFee.Set(usdfcSybilFee)
148+
cdnLockup := new(big.Int)
149+
cacheMissLockup := new(big.Int)
150+
if isNewDataSet && enableCDN {
151+
cdnLockup.Set(zeroBig(priceList.Lockups.CDNLockupAmount))
152+
cacheMissLockup.Set(zeroBig(priceList.Lockups.CacheMissLockupAmount))
127153
}
128154

129-
totalLockup := new(big.Int).Add(rateLockup, cdnLockup)
130-
totalLockup.Add(totalLockup, sybilFee)
155+
totalLockup := new(big.Int).Add(streamingLockup, lifecycleLockup)
156+
totalLockup.Add(totalLockup, cdnLockup)
157+
totalLockup.Add(totalLockup, cacheMissLockup)
158+
cdnFixedAlias := new(big.Int).Add(cdnLockup, cacheMissLockup)
131159

132160
return AdditionalLockup{
133-
RateDelta: rateDelta,
134-
RateLockup: rateLockup,
135-
CDNFixedLockup: cdnLockup,
136-
SybilFee: sybilFee,
137-
TotalLockup: totalLockup,
161+
RateDeltaPerEpoch: rateDelta,
162+
StreamingLockup: streamingLockup,
163+
LifecycleLockup: lifecycleLockup,
164+
CDNLockup: cdnLockup,
165+
CacheMissLockup: cacheMissLockup,
166+
Total: totalLockup,
167+
RateDelta: copyBigOrDefault(rateDelta, nil),
168+
RateLockup: copyBigOrDefault(streamingLockup, nil),
169+
CDNFixedLockup: cdnFixedAlias,
170+
SybilFee: copyBigOrDefault(lifecycleLockup, nil),
171+
TotalLockup: copyBigOrDefault(totalLockup, nil),
138172
}
139173
}
140174

@@ -148,10 +182,12 @@ func CalculateAdditionalLockupRequired(
148182
// Nil *big.Int fields are treated as zero. Negative epoch counts are clamped to zero.
149183
func CalculateDepositNeeded(calc DepositCalculation) *big.Int {
150184
additionalLockup := zeroBig(calc.AdditionalLockup)
185+
fees := zeroBig(calc.Fees)
151186
rateDelta := zeroBig(calc.RateDelta)
152187
currentLockupRate := zeroBig(calc.CurrentLockupRate)
153188
debt := zeroBig(calc.Debt)
154189
availableFunds := zeroBig(calc.AvailableFunds)
190+
runwayInEpochs := zeroBig(calc.RunwayInEpochs)
155191
runwayEpochs := calc.ExtraRunwayEpochs
156192
if runwayEpochs < 0 {
157193
runwayEpochs = 0
@@ -165,25 +201,26 @@ func CalculateDepositNeeded(calc DepositCalculation) *big.Int {
165201
runway := new(big.Int).Mul(combinedRate, big.NewInt(runwayEpochs))
166202

167203
raw := new(big.Int).Add(additionalLockup, runway)
204+
raw.Add(raw, fees)
168205
raw.Sub(raw, availableFunds)
169206
raw.Add(raw, debt)
170207

171-
if currentLockupRate.Sign() == 0 && calc.IsNewDataSet {
172-
if raw.Sign() < 0 {
173-
return new(big.Int)
208+
skipBuffer := currentLockupRate.Sign() == 0 && calc.IsNewDataSet
209+
buffer := new(big.Int)
210+
if !skipBuffer {
211+
if raw.Sign() > 0 {
212+
buffer.Mul(combinedRate, bufferEpochsBig)
213+
} else if runwayInEpochs.Cmp(bufferEpochsBig) <= 0 {
214+
buffer.Mul(combinedRate, bufferEpochsBig)
215+
buffer.Sub(buffer, availableFunds)
216+
if buffer.Sign() < 0 {
217+
buffer.SetInt64(0)
218+
}
174219
}
175-
return raw
176220
}
177221

178-
bufferCost := new(big.Int).Mul(combinedRate, bufferEpochsBig)
179222
if raw.Sign() > 0 {
180-
return raw.Add(raw, bufferCost)
181-
}
182-
183-
remainingAfterRequirements := new(big.Int).Neg(raw)
184-
buffer := new(big.Int).Sub(bufferCost, remainingAfterRequirements)
185-
if buffer.Sign() < 0 {
186-
return new(big.Int)
223+
return raw.Add(raw, buffer)
187224
}
188225
return buffer
189226
}
@@ -197,7 +234,7 @@ func zeroBig(v *big.Int) *big.Int {
197234

198235
// isFWSSMaxApproved returns true when all FWSS approval conditions are met.
199236
// Nil *big.Int fields are treated as zero (not approved).
200-
func isFWSSMaxApproved(approved bool, rateAllowance, lockAllowance, maxLockPeriod *big.Int) bool {
237+
func isFWSSMaxApproved(approved bool, rateAllowance, lockAllowance, maxLockPeriod, requiredLockupPeriod *big.Int) bool {
201238
if !approved {
202239
return false
203240
}
@@ -208,8 +245,52 @@ func isFWSSMaxApproved(approved bool, rateAllowance, lockAllowance, maxLockPerio
208245
if lockAllowance == nil || lockAllowance.Cmp(halfMaxUint256) < 0 {
209246
return false
210247
}
211-
if maxLockPeriod == nil || maxLockPeriod.Cmp(big.NewInt(DefaultLockupPeriod)) < 0 {
248+
required := copyBigOrDefault(requiredLockupPeriod, big.NewInt(DefaultLockupPeriod))
249+
if required.Sign() <= 0 {
250+
required.SetInt64(DefaultLockupPeriod)
251+
}
252+
if maxLockPeriod == nil || maxLockPeriod.Cmp(required) < 0 {
212253
return false
213254
}
214255
return true
215256
}
257+
258+
func copyBigOrDefault(v, def *big.Int) *big.Int {
259+
if v != nil {
260+
return new(big.Int).Set(v)
261+
}
262+
if def != nil {
263+
return new(big.Int).Set(def)
264+
}
265+
return new(big.Int)
266+
}
267+
268+
func requiredLockupPeriod(priceList *warmstorage.PriceList) *big.Int {
269+
if priceList != nil && priceList.Lockups.DefaultLockupPeriod != nil && priceList.Lockups.DefaultLockupPeriod.Sign() > 0 {
270+
return new(big.Int).Set(priceList.Lockups.DefaultLockupPeriod)
271+
}
272+
return big.NewInt(DefaultLockupPeriod)
273+
}
274+
275+
func aggregateLockup(rateDelta, streaming, lifecycle, cdn, cacheMiss, total *big.Int) AdditionalLockup {
276+
rateDeltaOut := copyBigOrDefault(rateDelta, nil)
277+
streamingOut := copyBigOrDefault(streaming, nil)
278+
lifecycleOut := copyBigOrDefault(lifecycle, nil)
279+
cdnOut := copyBigOrDefault(cdn, nil)
280+
cacheMissOut := copyBigOrDefault(cacheMiss, nil)
281+
totalOut := copyBigOrDefault(total, nil)
282+
cdnFixedAlias := new(big.Int).Add(cdnOut, cacheMissOut)
283+
return AdditionalLockup{
284+
RateDeltaPerEpoch: rateDeltaOut,
285+
StreamingLockup: streamingOut,
286+
LifecycleLockup: lifecycleOut,
287+
CDNLockup: cdnOut,
288+
CacheMissLockup: cacheMissOut,
289+
Total: totalOut,
290+
RateDelta: copyBigOrDefault(rateDeltaOut, nil),
291+
RateLockup: copyBigOrDefault(streamingOut, nil),
292+
CDNFixedLockup: cdnFixedAlias,
293+
SybilFee: copyBigOrDefault(lifecycleOut, nil),
294+
TotalLockup: copyBigOrDefault(totalOut, nil),
295+
}
296+
}

0 commit comments

Comments
 (0)