Skip to content

Commit

Permalink
Add scaling factors (#1354)
Browse files Browse the repository at this point in the history
Closes: #1277

## What is the purpose of the change

This PR adds scaling factors to current stable swap implementation. 

For context on why scaling factors are needed: Suppose 1000 stablecoin1 equals 1 dollars, 100 stablecoin2 equals 2 dollars due to precision difference. Currently we compare each pool assets without considering these differences in precisions in `calcOutAmtGivenIn`, `calcInAmtGivenOut`, `SpotPrice`. 

This PR adds a field of scaling factors for each pool asset so that when internally calculating in `amm.go`, we compare two different assets upon same precision points. We do this by feeding the internal functions assets / scaling factors.

## Brief change log

- Adds scaling factor to stableswap
*(for example:)*
 
  - *The metadata is stored in the blob store on job creation time as a persistent artifact*
  - *Deployments RPC transmits only the blob storage reference*
  - *Daemons retrieve the RPC data from the blob cache*


## Testing and Verifying

This change is a trivial rework / code cleanup without any test coverage.


## Documentation and Release Note

  - Does this pull request introduce a new feature or user-facing behavior changes? yes
  - Is a relevant changelog entry added to the `Unreleased` section in `CHANGELOG.md`? no
  - How is the feature or change documented? not applicable
  • Loading branch information
mattverse authored and czarcas7ic committed May 5, 2022
1 parent fd7e073 commit 5e9feb5
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,9 @@ message Pool {
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
// for calculation amognst assets with different precisions
repeated uint64 scaling_factor = 7 [
(gogoproto.moretags) = "yaml:\"stableswap_scaling_factor\"",
(gogoproto.nullable) = false
];
}
12 changes: 7 additions & 5 deletions x/gamm/pool-models/stableswap/amm.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,28 +203,30 @@ func spotPrice(baseReserve, quoteReserve sdk.Dec) sdk.Dec {

// returns outAmt as a decimal
func (pa *Pool) calcOutAmtGivenIn(tokenIn sdk.Coin, tokenOutDenom string, swapFee sdk.Dec) (sdk.Dec, error) {
reserves, err := pa.getPoolAmts(tokenIn.Denom, tokenOutDenom)
reserves, err := pa.getScaledPoolAmts(tokenIn.Denom, tokenOutDenom)

if err != nil {
return sdk.Dec{}, err
}
tokenInSupply := reserves[0].ToDec()
tokenOutSupply := reserves[1].ToDec()
// We are solving for the amount of token out, hence x = tokenOutSupply, y = tokenInSupply
outAmt := solveCfmm(tokenOutSupply, tokenInSupply, tokenIn.Amount.ToDec())
cfmmOut := solveCfmm(tokenOutSupply, tokenInSupply, tokenIn.Amount.ToDec())
outAmt := pa.getDescaledPoolAmt(tokenOutDenom, cfmmOut)
return outAmt, nil
}

// returns inAmt as a decimal
func (pa *Pool) calcInAmtGivenOut(tokenOut sdk.Coin, tokenInDenom string, swapFee sdk.Dec) (sdk.Dec, error) {
reserves, err := pa.getPoolAmts(tokenInDenom, tokenOut.Denom)
reserves, err := pa.getScaledPoolAmts(tokenInDenom, tokenOut.Denom)
if err != nil {
return sdk.Dec{}, err
}
tokenInSupply := reserves[0].ToDec()
tokenOutSupply := reserves[1].ToDec()
// We are solving for the amount of token in, cfmm(x,y) = cfmm(x + x_in, y - y_out)
// x = tokenInSupply, y = tokenOutSupply, yIn = -tokenOutAmount
inAmtRaw := solveCfmm(tokenInSupply, tokenOutSupply, tokenOut.Amount.ToDec().Neg())
inAmt := inAmtRaw.NegMut()
cfmmIn := solveCfmm(tokenInSupply, tokenOutSupply, tokenOut.Amount.ToDec().Neg())
inAmt := pa.getDescaledPoolAmt(tokenInDenom, cfmmIn.NegMut())
return inAmt, nil
}
55 changes: 52 additions & 3 deletions x/gamm/pool-models/stableswap/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ func (pa Pool) GetTotalShares() sdk.Int {
return pa.TotalShares.Amount
}

func (pa Pool) GetScalingFactors() []uint64 {
return pa.ScalingFactor
}

// CONTRACT: scaling factors follow the same index with pool liquidity denoms
func (pa Pool) GetScalingFactorByLiquidityIndex(liquidityIndex int) uint64 {
return pa.ScalingFactor[liquidityIndex]
}

// returns pool liquidity of the provided denoms, in the same order the denoms were provided in
func (pa Pool) getPoolAmts(denoms ...string) ([]sdk.Int, error) {
result := make([]sdk.Int, len(denoms))
Expand All @@ -69,6 +78,44 @@ func (pa Pool) getPoolAmts(denoms ...string) ([]sdk.Int, error) {
return result, nil
}

// getScaledPoolAmts returns scaled amount of pool liquidity based on each asset's precisions
func (pa Pool) getScaledPoolAmts(denoms ...string) ([]sdk.Int, error) {
result := make([]sdk.Int, len(denoms))
poolLiquidity := pa.PoolLiquidity
liquidityIndexes := pa.getLiquidityIndexMap()

for i, denom := range denoms {
liquidityIndex := liquidityIndexes[denom]

amt := poolLiquidity.AmountOf(denom)
if amt.IsZero() {
return []sdk.Int{}, fmt.Errorf("denom %s does not exist in pool", denom)
}
scalingFactor := pa.GetScalingFactorByLiquidityIndex(liquidityIndex)
result[i] = amt.QuoRaw(int64(scalingFactor))
}
return result, nil
}

// getDescaledPoolAmts gets descaled amount of given denom and amount
func (pa Pool) getDescaledPoolAmt(denom string, amount sdk.Dec) sdk.Dec {
liquidityIndexes := pa.getLiquidityIndexMap()
liquidityIndex := liquidityIndexes[denom]

scalingFactor := pa.GetScalingFactorByLiquidityIndex(liquidityIndex)
return amount.MulInt64(int64(scalingFactor))
}

// getLiquidityIndexMap creates a map of denoms to its index in pool liquidity
func (pa Pool) getLiquidityIndexMap() map[string]int {
poolLiquidity := pa.PoolLiquidity
liquidityIndexMap := make(map[string]int, poolLiquidity.Len())
for i, coin := range poolLiquidity {
liquidityIndexMap[coin.Denom] = i
}
return liquidityIndexMap
}

// updatePoolLiquidityForSwap updates the pool liquidity.
// It requires caller to validate that tokensIn and tokensOut only consist of
// denominations in the pool.
Expand Down Expand Up @@ -144,12 +191,14 @@ func (pa *Pool) SwapInAmtGivenOut(ctx sdk.Context, tokenOut sdk.Coins, tokenInDe
}

func (pa Pool) SpotPrice(ctx sdk.Context, baseAssetDenom string, quoteAssetDenom string) (sdk.Dec, error) {
reserves, err := pa.getPoolAmts(baseAssetDenom, quoteAssetDenom)
reserves, err := pa.getScaledPoolAmts(baseAssetDenom, quoteAssetDenom)
if err != nil {
return sdk.Dec{}, err
}
// TODO: apply scaling factors here
return spotPrice(reserves[0].ToDec(), reserves[1].ToDec()), nil
scaledSpotPrice := spotPrice(reserves[0].ToDec(), reserves[1].ToDec())
spotPrice := pa.getDescaledPoolAmt(baseAssetDenom, scaledSpotPrice)

return spotPrice, nil
}

func (pa Pool) CalcJoinPoolShares(ctx sdk.Context, tokensIn sdk.Coins, swapFee sdk.Dec) (numShares sdk.Int, newLiquidity sdk.Coins, err error) {
Expand Down
177 changes: 141 additions & 36 deletions x/gamm/pool-models/stableswap/stableswap_pool.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 5e9feb5

Please sign in to comment.