Skip to content

Commit

Permalink
chore: remove map in pool data
Browse files Browse the repository at this point in the history
  • Loading branch information
soring323 committed Mar 23, 2024
1 parent dfa8be4 commit 888e3d1
Show file tree
Hide file tree
Showing 16 changed files with 336 additions and 226 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ require (
github.com/cosmos/cosmos-proto v1.0.0-beta.4
github.com/cosmos/ics23/go v0.10.0
github.com/prometheus/client_golang v1.16.0
github.com/shopspring/decimal v1.3.1
golang.org/x/tools v0.12.0
google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0
google.golang.org/grpc v1.60.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,8 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0
github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sideprotocol/cosmos-sdk v0.47.111 h1:BaNMkp918+nAhxDP37+slIGEJlJiRxAJFAiciPgwi9I=
github.com/sideprotocol/cosmos-sdk v0.47.111/go.mod h1:F3+fgTq4W3kYIw+ETBaKaXi/t3eUPVKCDvKYqcei5WQ=
Expand Down
2 changes: 1 addition & 1 deletion proto/side/gmm/pool.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ message Pool {
string pool_id = 1;
string sender = 2;
PoolParams poolParams = 3 [(gogoproto.nullable) = false];
map<string, PoolAsset> assets = 4 [(gogoproto.nullable) = false];
repeated PoolAsset assets = 4 [(gogoproto.nullable) = false];
// sum of all LP tokens sent out
cosmos.base.v1beta1.Coin total_shares = 5 [
(gogoproto.moretags) = "yaml:\"total_shares\"",
Expand Down
4 changes: 2 additions & 2 deletions x/gmm/keeper/msg_server_create_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,12 @@ func (suite *KeeperTestSuite) createNewStablePool() string {
},
[]types.PoolAsset{
{
Token: sdk.NewCoin(simapp.WDAI, sdkmath.NewInt(100)),
Token: sdk.NewCoin(simapp.WDAI, sdkmath.NewInt(100000)),
Weight: &weight,
Decimal: sdk.NewInt(6),
},
{
Token: sdk.NewCoin(simapp.WUSDT, sdkmath.NewInt(100)),
Token: sdk.NewCoin(simapp.WUSDT, sdkmath.NewInt(100000)),
Weight: &weight,
Decimal: sdk.NewInt(6),
},
Expand Down
8 changes: 5 additions & 3 deletions x/gmm/keeper/msg_server_swap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (suite *KeeperTestSuite) TestMsgSwap() {
"swap in stable pool",
types.PoolType_STABLE,
func(msg *types.MsgSwap, poolID string) {
msg.TokenIn = sdk.NewCoin(simapp.WDAI, sdk.NewInt(100))
msg.TokenIn = sdk.NewCoin(simapp.WDAI, sdk.NewInt(50))
msg.TokenOut = sdk.NewCoin(simapp.WUSDT, sdk.NewInt(0))
},
},
Expand Down Expand Up @@ -57,7 +57,8 @@ func (suite *KeeperTestSuite) TestMsgSwap() {
suite.Require().NoError(err)

pool := queryResBeforeSwap.Pool.ToPool()
outAssetBeforeSwap := pool.Assets[msg.TokenOut.Denom]
outAssetBeforeSwap, _, exist := pool.GetAssetByDenom(msg.TokenOut.Denom)
suite.Require().Equal(exist, true)
estimatedOut, err := pool.EstimateSwap(msg.TokenIn, msg.TokenOut.Denom)
suite.Require().NoError(err)
msg.TokenOut = estimatedOut
Expand All @@ -75,7 +76,8 @@ func (suite *KeeperTestSuite) TestMsgSwap() {
})
suite.Require().NoError(err)
pool = queryResAfterSwap.Pool.ToPool()
outAssetAfterSwap := pool.Assets[msg.TokenOut.Denom]
outAssetAfterSwap, _, exist := pool.GetAssetByDenom(msg.TokenOut.Denom)
suite.Require().Equal(exist, true)
out := outAssetBeforeSwap.Token.Sub(outAssetAfterSwap.Token)
suite.Require().Equal(out, estimatedOut)
})
Expand Down
10 changes: 5 additions & 5 deletions x/gmm/keeper/pool_apr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ func TestAPRCalculation(t *testing.T) {
keeper, ctx := testkeeper.GmmKeeper(t)
amp := sdkmath.NewInt(100)
//params := types.DefaultParams()
mockAssets := make(map[string]types.PoolAsset)
mockAssets := []types.PoolAsset{}
weight := sdkmath.NewInt(6)
tokenIn := sdk.NewCoin("usdt", sdk.NewInt(100))
//tokenOut := sdk.NewCoin("usdc", sdk.NewInt(80))
mockAssets["usdt"] = types.PoolAsset{
mockAssets = append(mockAssets, types.PoolAsset{
Decimal: sdkmath.NewInt(6),
Weight: &weight,
Token: sdk.NewCoin("usdt", sdk.NewInt(1000000)),
}
mockAssets["usdc"] = types.PoolAsset{
})
mockAssets = append(mockAssets, types.PoolAsset{
Decimal: sdkmath.NewInt(6),
Weight: &weight,
Token: sdk.NewCoin("usdc", sdk.NewInt(1000000)),
}
})

pool := types.Pool{
PoolId: "test",
Expand Down
10 changes: 5 additions & 5 deletions x/gmm/keeper/volume_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ func TestVolumeQuery(t *testing.T) {
keeper, ctx := testkeeper.GmmKeeper(t)
amp := sdkmath.NewInt(100)
//params := types.DefaultParams()
mockAssets := make(map[string]types.PoolAsset)
mockAssets := []types.PoolAsset{}
weight := sdkmath.NewInt(6)
tokenIn := sdk.NewCoin("usdt", sdk.NewInt(100))
tokenOut := sdk.NewCoin("usdc", sdk.NewInt(80))
mockAssets["usdt"] = types.PoolAsset{
mockAssets = append(mockAssets, types.PoolAsset{
Decimal: sdkmath.NewInt(6),
Weight: &weight,
Token: sdk.NewCoin("usdt", sdk.NewInt(1000000)),
}
mockAssets["usdc"] = types.PoolAsset{
})
mockAssets = append(mockAssets, types.PoolAsset{
Decimal: sdkmath.NewInt(6),
Weight: &weight,
Token: sdk.NewCoin("usdc", sdk.NewInt(1000000)),
}
})

pool := types.Pool{
PoolId: "test",
Expand Down
145 changes: 145 additions & 0 deletions x/gmm/types/approx_power.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package types

import (
"errors"
"math/big"
"strings"

"github.com/shopspring/decimal"
)

// DecimalFractional constant for decimal calculations
var DecimalFractional = decimal.NewFromBigInt(big.NewInt(1_000_000_000_000_000_000), 0)

// SubSign returns mod subtraction and boolean indicating if the result is negative
func SubSign(a, b decimal.Decimal) (decimal.Decimal, bool) {
if a.GreaterThanOrEqual(b) {
return a.Sub(b), false
}
return b.Sub(a), true
}

// Sqrt computes the square root of a decimal number using Newton's method.
func Sqrt(value decimal.Decimal, precision decimal.Decimal) (decimal.Decimal, error) {
if value.LessThan(decimal.Zero) {
return decimal.Decimal{}, errors.New("square root of negative number")
}

x := value.DivRound(decimal.NewFromInt(2), precision.Exponent())
lastX := decimal.Zero

for x.Sub(lastX).Abs().GreaterThan(precision) {
lastX = x
x = decimal.Avg(x, value.Div(x))
}

return x, nil
}

// CalculatePow computes base^(exp) using an approximation algorithm
func ApproximatePow(baseS, expS string, precisionS string) (decimal.Decimal, error) {
// Convert string to Decimal
base, err := decimal.NewFromString(baseS)
if err != nil {
return decimal.Decimal{}, err
}

exp, err := decimal.NewFromString(expS)
if err != nil {
return decimal.Decimal{}, err
}

precision, err := decimal.NewFromString(precisionS)
if err != nil {
return decimal.Decimal{}, err
}

if base.IsZero() && !exp.IsZero() {
return base, nil
}

if base.GreaterThan(decimal.NewFromInt(2)) {
return decimal.Decimal{}, errors.New("calculatePow: base must be less than 2")
}

integer := exp.Div(DecimalFractional).Floor()
fractional := exp.Mod(DecimalFractional)
integerPow := base.Pow(integer)

if fractional.IsZero() {
return integerPow, nil
}

fractionalPow, err := PowApprox(base, fractional, precision)
if err != nil {
return decimal.Decimal{}, err
}

result := integerPow.Mul(fractionalPow)
return result, nil
}

// PowApprox approximates power for fractional exponents
func PowApprox(base, exp, precision decimal.Decimal) (decimal.Decimal, error) {
if exp.Equals(decimal.NewFromInt(1).Div(decimal.NewFromInt(2))) {
sqrtBase, err := Sqrt(base, precision)
if err != nil {
return decimal.Decimal{}, err
}
return sqrtBase, nil
}

x, xNeg := SubSign(base, decimal.NewFromInt(1))
term := decimal.NewFromInt(1)
sum := decimal.NewFromInt(1)
negative := false

a := exp
bigK := decimal.Zero
i := decimal.NewFromInt(1)

for term.GreaterThanOrEqual(precision) {
c, cNeg := SubSign(a, bigK)
bigK = i

newTerm := term.Mul(c).Mul(x).Div(bigK)
term = newTerm

if term.IsZero() {
break
}

if xNeg {
negative = !negative
}

if cNeg {
negative = !negative
}

if negative {
sum = sum.Sub(term)
} else {
sum = sum.Add(term)
}

i = i.Add(decimal.NewFromInt(1))
}
fixedPoint := DecimalPlacesFromPrecision(precision)
return sum.RoundDown(fixedPoint), nil
}

// DecimalPlacesFromPrecision calculates the number of decimal places based on the precision decimal.
// DecimalPlacesFromPrecision calculates the number of decimal places based on the precision decimal.
func DecimalPlacesFromPrecision(precision decimal.Decimal) int32 {
// Convert precision to string using no fixed decimal places to avoid trailing zeros.
str := precision.String()
dotIndex := strings.IndexRune(str, '.')
if dotIndex != -1 {
// Remove trailing zeros to get the correct count of significant decimal places.
str = strings.TrimRight(str, "0")
// Count the number of characters after the decimal point to determine the scale.
return int32(len(str) - dotIndex - 1)
}
return 0 // Return 0 if there is no fractional part.
}
29 changes: 21 additions & 8 deletions x/gmm/types/message_create_pool.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package types

import (
"sort"

sdkerrors "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -101,27 +103,38 @@ func (msg *MsgCreatePool) GetAssetDenoms() []string {
return denoms
}

// Return denom list of liquidity
func (msg *MsgCreatePool) CreatePool() Pool {
// Extract denom list from Liquidity
denoms := msg.GetAssetDenoms()

assets := make(map[string]PoolAsset)
// Sort denoms in alphabetical order
sort.Strings(denoms) // This ensures the denoms are in a deterministic order

assets := make([]PoolAsset, len(msg.Liquidity)) // Change this to a slice to maintain order
totalShares := sdk.NewInt(0)
for _, liquidity := range msg.Liquidity {
assets[liquidity.Token.Denom] = liquidity
totalShares = totalShares.Add(liquidity.Token.Amount)

// Fill the assets slice in a deterministic manner
for i, denom := range denoms {
for _, liquidity := range msg.Liquidity {
if liquidity.Token.Denom == denom {
assets[i] = liquidity // Assigning by sorted index
totalShares = totalShares.Add(liquidity.Token.Amount)
break // Move to next denom after finding and assigning
}
}
}

// Generate new PoolId
newPoolID := GetPoolID(denoms)
// Generate new PoolId using sorted denoms
newPoolID := GetPoolID(denoms) // Assuming this is already implemented to be deterministic
poolShareBaseDenom := GetPoolShareDenom(newPoolID)

pool := Pool{
PoolId: newPoolID,
Sender: msg.Sender,
PoolParams: *msg.Params,
Assets: assets,
Assets: assets, // Now a slice, maintaining the order
TotalShares: sdk.NewCoin(poolShareBaseDenom, totalShares),
}

return pool
}
26 changes: 22 additions & 4 deletions x/gmm/types/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,13 @@ func (p *Pool) DecreaseShare(amt sdkmath.Int) {
// IncreaseLiquidity adds xx amount liquidity to assets in pool
func (p *Pool) IncreaseLiquidity(coins []sdk.Coin) error {
for _, coin := range coins {
asset, exists := p.Assets[coin.Denom]
asset, index, exists := p.GetAssetByDenom(coin.Denom) //Assets[coin.Denom]
if !exists {
return ErrNotFoundAssetInPool
}
// Add liquidity logic here
asset.Token.Amount = asset.Token.Amount.Add(coin.Amount)
p.Assets[coin.Denom] = asset
p.Assets[index] = asset
}
// Update TotalShares or other fields if necessary
return nil
Expand All @@ -97,18 +97,27 @@ func (p *Pool) IncreaseLiquidity(coins []sdk.Coin) error {
// DecreaseLiquidity subtracts xx amount liquidity from assets in pool
func (p *Pool) DecreaseLiquidity(coins []sdk.Coin) error {
for _, coin := range coins {
asset, exists := p.Assets[coin.Denom]
asset, index, exists := p.GetAssetByDenom(coin.Denom)
if !exists {
return ErrNotFoundAssetInPool
}
// Add liquidity logic here
asset.Token.Amount = asset.Token.Amount.Sub(coin.Amount)
p.Assets[coin.Denom] = asset
p.Assets[index] = asset
}
// Update TotalShares or other fields if necessary
return nil
}

func (p *Pool) GetAssetByDenom(denom string) (PoolAsset, int, bool) {
for index, asset := range p.Assets {
if asset.Token.Denom == denom {
return asset, index, true
}
}
return PoolAsset{}, 0, false
}

// findAssetByDenom finds pool asset by denom
func (p *Pool) findAssetByDenom(denom string) (PoolAsset, error) {
for _, asset := range p.Assets {
Expand Down Expand Up @@ -162,3 +171,12 @@ func (p *Pool) Sum() sdkmath.Int {
}
return sdk.ZeroInt()
}

func FindAsset(assets []PoolAsset, denom string) (PoolAsset, bool) {
for _, asset := range assets {
if asset.Token.Denom == denom {
return asset, true
}
}
return PoolAsset{}, false
}
Loading

0 comments on commit 888e3d1

Please sign in to comment.