Skip to content

Commit 70cdc3b

Browse files
authored
add missing checks for transmuter's limiter (#554)
1 parent fcbf7b6 commit 70cdc3b

File tree

2 files changed

+95
-46
lines changed

2 files changed

+95
-46
lines changed

router/usecase/pools/routable_cw_alloy_transmuter_pool.go

+69-40
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package pools
33
import (
44
"context"
55
"fmt"
6-
"strings"
76

87
"cosmossdk.io/math"
98
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -29,10 +28,6 @@ type routableAlloyTransmuterPoolImpl struct {
2928
SpreadFactor osmomath.Dec "json:\"spread_factor\""
3029
}
3130

32-
const (
33-
alloyedLPShareDenomComponent = "all"
34-
)
35-
3631
// GetId implements domain.RoutablePool.
3732
func (r *routableAlloyTransmuterPoolImpl) GetId() uint64 {
3833
return r.ChainPool.PoolId
@@ -187,7 +182,6 @@ func (r *routableAlloyTransmuterPoolImpl) CalcTokenOutAmt(tokenIn sdk.Coin, toke
187182
}
188183

189184
// Check static upper rate limiter
190-
// We only need to check it for the token in coin since that is the only one that is increased by the current quote.
191185
if err := r.checkStaticRateLimiter(tokenIn); err != nil {
192186
return osmomath.BigDec{}, err
193187
}
@@ -202,9 +196,14 @@ func (r *routableAlloyTransmuterPoolImpl) CalcTokenOutAmt(tokenIn sdk.Coin, toke
202196
return tokenOutAmount, nil
203197
}
204198

205-
// checkStaticRateLimiter checks the static rate limiter for the token in coin.
199+
// checkStaticRateLimiter checks the static rate limiter.
200+
// If token in denom is not alloyed, we only need to validate the token in balance.
201+
// Since the token in balance is the only one that is increased by the current quote.
202+
//
203+
// If token in denom is alloyed, we need to validate all assets' balances except token out.
204+
// Since the token out composition is decreasing, other assets' weights are increasing.
205+
//
206206
// Note: static rate limit only has an upper limit.
207-
// Therefore, we only need to validate the token in balance.
208207
// No-op if the static rate limiter is not set.
209208
// Returns error if the token in weight is greater than the upper limit.
210209
// Returns nil if the token in weight is less than or equal to the upper limit.
@@ -214,12 +213,6 @@ func (r *routableAlloyTransmuterPoolImpl) checkStaticRateLimiter(tokenInCoin sdk
214213
return nil
215214
}
216215

217-
// Check if the static rate limiter exists for the token in denom updated balance.
218-
tokeInStaticLimiter, ok := r.AlloyTransmuterData.RateLimiterConfig.GetStaticLimiter(tokenInCoin.Denom)
219-
if !ok {
220-
return nil
221-
}
222-
223216
preComputedData := r.AlloyTransmuterData.PreComputedData
224217
normalizationFactors := preComputedData.NormalizationScalingFactors
225218

@@ -232,8 +225,8 @@ func (r *routableAlloyTransmuterPoolImpl) checkStaticRateLimiter(tokenInCoin sdk
232225
assetConfig := r.AlloyTransmuterData.AssetConfigs[i]
233226
assetDenom := assetConfig.Denom
234227

235-
// Skip if the asset is alloyed LP hsare
236-
if strings.Contains(assetDenom, alloyedLPShareDenomComponent) {
228+
// Skip if the asset is alloyed LP share
229+
if assetDenom == r.AlloyTransmuterData.AlloyedDenom {
237230
continue
238231
}
239232

@@ -244,6 +237,11 @@ func (r *routableAlloyTransmuterPoolImpl) checkStaticRateLimiter(tokenInCoin sdk
244237
assetBalance = assetBalance.Add(tokenInCoin.Amount)
245238
}
246239

240+
// Subtract the token out balance from the asset balance
241+
if assetDenom == r.TokenOutDenom {
242+
assetBalance = assetBalance.Sub(tokenInCoin.Amount)
243+
}
244+
247245
normalizationScalingFactor, ok := normalizationFactors[assetDenom]
248246
if !ok {
249247
return fmt.Errorf("normalization scaling factor not found for asset %s, pool id %d", assetDenom, r.GetId())
@@ -259,34 +257,65 @@ func (r *routableAlloyTransmuterPoolImpl) checkStaticRateLimiter(tokenInCoin sdk
259257
normalizeTotal = normalizeTotal.Add(normalizedBalance)
260258
}
261259

262-
// Calculate weights
263-
// Note: -1 for the alloyed LP share.
264-
weights := make(map[string]osmomath.Dec, len(r.AlloyTransmuterData.AssetConfigs)-1)
265-
for i := 0; i < len(r.AlloyTransmuterData.AssetConfigs); i++ {
266-
assetConfig := r.AlloyTransmuterData.AssetConfigs[i]
267-
assetDenom := assetConfig.Denom
268-
269-
// Skip if the asset is alloyed LP hsare
270-
if strings.Contains(assetDenom, alloyedLPShareDenomComponent) {
271-
continue
260+
// If token in denom is alloyed, we need to validate limiters for all assets' balances except token out.
261+
// Since the token out composition is decreasing, other assets' weights are increasing.
262+
// else, we only need to validate the token in denom limiter.
263+
if tokenInCoin.Denom == r.AlloyTransmuterData.AlloyedDenom {
264+
for i := 0; i < len(r.AlloyTransmuterData.AssetConfigs); i++ {
265+
assetConfig := r.AlloyTransmuterData.AssetConfigs[i]
266+
assetDenom := assetConfig.Denom
267+
268+
// Skip if the asset is alloyed LP share
269+
if assetDenom == r.AlloyTransmuterData.AlloyedDenom {
270+
continue
271+
}
272+
273+
// skip if the asset is token out, since its weight is decreasing, no need to check limiter
274+
if assetDenom == r.TokenOutDenom {
275+
continue
276+
}
277+
278+
// Check if the static rate limiter exists for the asset denom updated balance.
279+
// If not, continue to the next asset
280+
staticLimiter, ok := r.AlloyTransmuterData.RateLimiterConfig.GetStaticLimiter(assetDenom)
281+
if !ok {
282+
continue
283+
}
284+
285+
// Validate upper limit
286+
upperLimitInt := osmomath.MustNewDecFromStr(staticLimiter.UpperLimit)
287+
288+
// Asset weight
289+
assetWeight := normalizedBalances[assetDenom].ToLegacyDec().Quo(normalizeTotal.ToLegacyDec())
290+
291+
// Check the upper limit
292+
if assetWeight.GT(upperLimitInt) {
293+
return domain.StaticRateLimiterInvalidUpperLimitError{
294+
UpperLimit: staticLimiter.UpperLimit,
295+
Weight: assetWeight.String(),
296+
Denom: assetDenom,
297+
}
298+
}
299+
}
300+
} else {
301+
tokeInStaticLimiter, ok := r.AlloyTransmuterData.RateLimiterConfig.GetStaticLimiter(tokenInCoin.Denom)
302+
if !ok {
303+
return nil
272304
}
273305

274-
// Calculate weight
275-
weights[assetDenom] = normalizedBalances[assetDenom].ToLegacyDec().Quo(normalizeTotal.ToLegacyDec())
276-
}
277-
278-
// Validate upper limit
279-
upperLimitInt := osmomath.MustNewDecFromStr(tokeInStaticLimiter.UpperLimit)
306+
// Validate upper limit
307+
upperLimitInt := osmomath.MustNewDecFromStr(tokeInStaticLimiter.UpperLimit)
280308

281-
// Token in weight
282-
tokenInWeight := weights[tokenInCoin.Denom]
309+
// Token in weight
310+
tokenInWeight := normalizedBalances[tokenInCoin.Denom].ToLegacyDec().Quo(normalizeTotal.ToLegacyDec())
283311

284-
// Check the upper limit
285-
if tokenInWeight.GT(upperLimitInt) {
286-
return domain.StaticRateLimiterInvalidUpperLimitError{
287-
UpperLimit: tokeInStaticLimiter.UpperLimit,
288-
Weight: tokenInWeight.String(),
289-
Denom: tokenInCoin.Denom,
312+
// Check the upper limit
313+
if tokenInWeight.GT(upperLimitInt) {
314+
return domain.StaticRateLimiterInvalidUpperLimitError{
315+
UpperLimit: tokeInStaticLimiter.UpperLimit,
316+
Weight: tokenInWeight.String(),
317+
Denom: tokenInCoin.Denom,
318+
}
290319
}
291320
}
292321

router/usecase/pools/routable_cw_alloy_transmuter_pool_test.go

+26-6
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ func (s *RoutablePoolTestSuite) SetupRoutableAlloyTransmuterPoolCustom(tokenInDe
4141
AlloyTransmuter: &cosmwasmpool.AlloyTransmuterData{
4242
AlloyedDenom: ALLUSD,
4343
AssetConfigs: []cosmwasmpool.TransmuterAssetConfig{
44-
{Denom: USDC, NormalizationFactor: osmomath.NewInt(100)},
45-
{Denom: USDT, NormalizationFactor: osmomath.NewInt(1)},
4644
{Denom: OVERLY_PRECISE_USD, NormalizationFactor: veryBigNormalizationFactor},
4745
{Denom: NO_PRECISION_USD, NormalizationFactor: osmomath.ZeroInt()},
46+
{Denom: USDT, NormalizationFactor: osmomath.NewInt(1)},
47+
{Denom: USDC, NormalizationFactor: osmomath.NewInt(100)},
4848
{Denom: ALLUSD, NormalizationFactor: osmomath.NewInt(10)},
4949
},
5050

@@ -320,6 +320,7 @@ func (s *RoutablePoolTestSuite) TestCheckStaticRateLimiter() {
320320
USDT: osmomath.NewInt(1),
321321
OVERLY_PRECISE_USD: osmomath.NewInt(1),
322322
NO_PRECISION_USD: osmomath.NewInt(1),
323+
ALLUSD: osmomath.NewInt(1),
323324
}
324325

325326
oneInt := osmomath.NewInt(1)
@@ -337,6 +338,7 @@ func (s *RoutablePoolTestSuite) TestCheckStaticRateLimiter() {
337338

338339
tests := map[string]struct {
339340
tokenInCoin sdk.Coin
341+
tokenOutDenom string
340342
initialBalances sdk.Coins
341343
standardNormFactor osmomath.Int
342344
normalizationScalingFactors map[string]osmomath.Int
@@ -345,6 +347,7 @@ func (s *RoutablePoolTestSuite) TestCheckStaticRateLimiter() {
345347
}{
346348
"valid token in - below upper limit": {
347349
tokenInCoin: sdk.NewCoin(USDC, osmomath.NewInt(100_000)),
350+
tokenOutDenom: USDT,
348351
initialBalances: defaultInitialBalances,
349352
standardNormFactor: defaultStandardNormFactor,
350353
normalizationScalingFactors: defaultScalingFactors,
@@ -353,34 +356,51 @@ func (s *RoutablePoolTestSuite) TestCheckStaticRateLimiter() {
353356
},
354357
"invalid token in - exceeds upper limit": {
355358
tokenInCoin: sdk.NewCoin(USDC, osmomath.NewInt(2_000_000)),
359+
tokenOutDenom: USDT,
356360
initialBalances: defaultInitialBalances,
357361
standardNormFactor: defaultStandardNormFactor,
358362
normalizationScalingFactors: defaultScalingFactors,
359363
staticLimiterConfig: defaultStaticLimiterConfig,
360364
expectError: domain.StaticRateLimiterInvalidUpperLimitError{
361365
Denom: USDC,
362366
UpperLimit: "0.5",
363-
Weight: osmomath.MustNewDecFromStr("0.6").String(),
367+
Weight: osmomath.MustNewDecFromStr("1").String(),
364368
},
365369
},
366370
"no static limiter configured": {
367371
tokenInCoin: sdk.NewCoin(USDC, osmomath.NewInt(1_000_000)),
372+
tokenOutDenom: USDT,
368373
initialBalances: defaultInitialBalances,
369374
standardNormFactor: defaultStandardNormFactor,
370375
normalizationScalingFactors: defaultScalingFactors,
371376
staticLimiterConfig: map[string]cosmwasmpool.StaticLimiter{},
372377
expectError: nil,
373378
},
374379
"static limiter not set for token in denom": {
375-
tokenInCoin: sdk.NewCoin(USDC, osmomath.NewInt(1_000_000)),
380+
tokenInCoin: sdk.NewCoin(USDT, osmomath.NewInt(1_000_000)),
381+
tokenOutDenom: USDC,
376382
initialBalances: defaultInitialBalances,
377383
standardNormFactor: defaultStandardNormFactor,
378384
normalizationScalingFactors: defaultScalingFactors,
379385
staticLimiterConfig: defaultStaticLimiterConfig,
380386
expectError: nil,
381387
},
388+
"check all assets' static limiters when token in denom is alloyed": {
389+
tokenInCoin: sdk.NewCoin(ALLUSD, osmomath.NewInt(1_000_001)),
390+
tokenOutDenom: USDT,
391+
initialBalances: defaultInitialBalances,
392+
standardNormFactor: defaultStandardNormFactor,
393+
normalizationScalingFactors: defaultScalingFactors,
394+
staticLimiterConfig: defaultStaticLimiterConfig,
395+
expectError: domain.StaticRateLimiterInvalidUpperLimitError{
396+
Denom: USDC,
397+
UpperLimit: "0.5",
398+
Weight: osmomath.MustNewDecFromStr("0.500000250000125").String(),
399+
},
400+
},
382401
"different normalization factors": {
383-
tokenInCoin: sdk.NewCoin(USDC, osmomath.NewInt(500_000)),
402+
tokenInCoin: sdk.NewCoin(USDC, osmomath.NewInt(500_000)),
403+
tokenOutDenom: USDT,
384404
initialBalances: sdk.NewCoins(
385405
sdk.NewCoin(USDC, osmomath.NewInt(1_000_000)),
386406
sdk.NewCoin(USDT, osmomath.NewInt(2_000_000)),
@@ -402,7 +422,7 @@ func (s *RoutablePoolTestSuite) TestCheckStaticRateLimiter() {
402422
for name, tc := range tests {
403423
s.Run(name, func() {
404424
s.Setup()
405-
routablePool := s.SetupRoutableAlloyTransmuterPoolCustom(USDT, USDC, tc.initialBalances, osmomath.ZeroDec(), cosmwasmpool.AlloyedRateLimiter{
425+
routablePool := s.SetupRoutableAlloyTransmuterPoolCustom(tc.tokenInCoin.Denom, tc.tokenOutDenom, tc.initialBalances, osmomath.ZeroDec(), cosmwasmpool.AlloyedRateLimiter{
406426
StaticLimiterByDenomMap: tc.staticLimiterConfig,
407427
}, cosmwasmpool.PrecomputedData{
408428
StdNormFactor: tc.standardNormFactor,

0 commit comments

Comments
 (0)