From 888e3d1bb860b9cb1d4d2e6cb16774fbfc5a0893 Mon Sep 17 00:00:00 2001 From: DedicatedDev Date: Sat, 23 Mar 2024 14:16:38 -0500 Subject: [PATCH] chore: remove map in pool data --- go.mod | 1 + go.sum | 2 + proto/side/gmm/pool.proto | 2 +- x/gmm/keeper/msg_server_create_pool_test.go | 4 +- x/gmm/keeper/msg_server_swap_test.go | 8 +- x/gmm/keeper/pool_apr_test.go | 10 +- x/gmm/keeper/volume_test.go | 10 +- x/gmm/types/approx_power.go | 145 ++++++++++++++ x/gmm/types/message_create_pool.go | 29 ++- x/gmm/types/pool.go | 26 ++- x/gmm/types/pool.pb.go | 206 +++++--------------- x/gmm/types/poolI.go | 6 +- x/gmm/types/pool_apr.go | 6 +- x/gmm/types/pool_stable.go | 40 ++-- x/gmm/types/pool_weight.go | 32 ++- x/gmm/types/pool_weight_test.go | 35 ++++ 16 files changed, 336 insertions(+), 226 deletions(-) create mode 100644 x/gmm/types/approx_power.go create mode 100644 x/gmm/types/pool_weight_test.go diff --git a/go.mod b/go.mod index 86cdd8e4..b528a6dd 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 0bfe1322..157c4932 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/proto/side/gmm/pool.proto b/proto/side/gmm/pool.proto index 81a4de5e..a0a7d3e2 100644 --- a/proto/side/gmm/pool.proto +++ b/proto/side/gmm/pool.proto @@ -12,7 +12,7 @@ message Pool { string pool_id = 1; string sender = 2; PoolParams poolParams = 3 [(gogoproto.nullable) = false]; - map 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\"", diff --git a/x/gmm/keeper/msg_server_create_pool_test.go b/x/gmm/keeper/msg_server_create_pool_test.go index e32875dc..ec24afe2 100644 --- a/x/gmm/keeper/msg_server_create_pool_test.go +++ b/x/gmm/keeper/msg_server_create_pool_test.go @@ -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), }, diff --git a/x/gmm/keeper/msg_server_swap_test.go b/x/gmm/keeper/msg_server_swap_test.go index c30e3b7f..52675850 100644 --- a/x/gmm/keeper/msg_server_swap_test.go +++ b/x/gmm/keeper/msg_server_swap_test.go @@ -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)) }, }, @@ -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 @@ -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) }) diff --git a/x/gmm/keeper/pool_apr_test.go b/x/gmm/keeper/pool_apr_test.go index 35333f07..f87bd6d6 100644 --- a/x/gmm/keeper/pool_apr_test.go +++ b/x/gmm/keeper/pool_apr_test.go @@ -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", diff --git a/x/gmm/keeper/volume_test.go b/x/gmm/keeper/volume_test.go index ab2bf336..3e092763 100644 --- a/x/gmm/keeper/volume_test.go +++ b/x/gmm/keeper/volume_test.go @@ -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", diff --git a/x/gmm/types/approx_power.go b/x/gmm/types/approx_power.go new file mode 100644 index 00000000..d36baa18 --- /dev/null +++ b/x/gmm/types/approx_power.go @@ -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. +} diff --git a/x/gmm/types/message_create_pool.go b/x/gmm/types/message_create_pool.go index c4df422f..6f2e4865 100644 --- a/x/gmm/types/message_create_pool.go +++ b/x/gmm/types/message_create_pool.go @@ -1,6 +1,8 @@ package types import ( + "sort" + sdkerrors "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" @@ -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 } diff --git a/x/gmm/types/pool.go b/x/gmm/types/pool.go index 6f9e6ff0..5694eb2c 100644 --- a/x/gmm/types/pool.go +++ b/x/gmm/types/pool.go @@ -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 @@ -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 { @@ -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 +} diff --git a/x/gmm/types/pool.pb.go b/x/gmm/types/pool.pb.go index 40059c8c..ddebb078 100644 --- a/x/gmm/types/pool.pb.go +++ b/x/gmm/types/pool.pb.go @@ -27,10 +27,10 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Pool struct { //option (cosmos_proto.implements_interface) = "PoolI"; - PoolId string `protobuf:"bytes,1,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty"` - Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"` - PoolParams PoolParams `protobuf:"bytes,3,opt,name=poolParams,proto3" json:"poolParams"` - Assets map[string]PoolAsset `protobuf:"bytes,4,rep,name=assets,proto3" json:"assets" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + PoolId string `protobuf:"bytes,1,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty"` + Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"` + PoolParams PoolParams `protobuf:"bytes,3,opt,name=poolParams,proto3" json:"poolParams"` + Assets []PoolAsset `protobuf:"bytes,4,rep,name=assets,proto3" json:"assets"` // sum of all LP tokens sent out TotalShares types.Coin `protobuf:"bytes,5,opt,name=total_shares,json=totalShares,proto3" json:"total_shares" yaml:"total_shares"` } @@ -89,7 +89,7 @@ func (m *Pool) GetPoolParams() PoolParams { return PoolParams{} } -func (m *Pool) GetAssets() map[string]PoolAsset { +func (m *Pool) GetAssets() []PoolAsset { if m != nil { return m.Assets } @@ -250,7 +250,6 @@ func (m *PoolWasmAsset) GetDecimal() uint32 { func init() { proto.RegisterType((*Pool)(nil), "side.gmm.Pool") - proto.RegisterMapType((map[string]PoolAsset)(nil), "side.gmm.Pool.AssetsEntry") proto.RegisterType((*PoolI)(nil), "side.gmm.PoolI") proto.RegisterType((*PoolWasmAsset)(nil), "side.gmm.PoolWasmAsset") } @@ -258,43 +257,40 @@ func init() { func init() { proto.RegisterFile("side/gmm/pool.proto", fileDescriptor_539300364e2e3e52) } var fileDescriptor_539300364e2e3e52 = []byte{ - // 572 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x53, 0xd1, 0x8a, 0xda, 0x4c, - 0x14, 0x36, 0xd1, 0x8d, 0xeb, 0xf8, 0x2b, 0x3f, 0xe3, 0xd2, 0x8d, 0x16, 0xa2, 0x08, 0x2d, 0x69, - 0xa1, 0x19, 0x74, 0x6f, 0x16, 0xe9, 0x4d, 0x5d, 0x5a, 0xf0, 0xa6, 0x48, 0x5a, 0x28, 0xed, 0x8d, - 0x8c, 0xc9, 0x34, 0x86, 0xcd, 0x64, 0x42, 0x66, 0x74, 0x9b, 0xb7, 0xe8, 0x83, 0xf4, 0x41, 0xf6, - 0x72, 0x2f, 0x97, 0x5e, 0x48, 0xd1, 0x37, 0xe8, 0x13, 0x94, 0x99, 0x44, 0x31, 0x37, 0xed, 0x55, - 0xce, 0x77, 0xbe, 0xef, 0xe4, 0x9c, 0xf3, 0x71, 0x06, 0x74, 0x78, 0xe8, 0x13, 0x14, 0x50, 0x8a, - 0x12, 0xc6, 0x22, 0x27, 0x49, 0x99, 0x60, 0xf0, 0x5c, 0x26, 0x9d, 0x80, 0xd2, 0x5e, 0xaf, 0x44, - 0x2f, 0x12, 0x9c, 0x62, 0xca, 0x73, 0x55, 0xaf, 0x5b, 0xe6, 0x30, 0xe7, 0x44, 0x14, 0xd4, 0x45, - 0xc0, 0x02, 0xa6, 0x42, 0x24, 0xa3, 0x22, 0x6b, 0x79, 0x8c, 0x53, 0xc6, 0xd1, 0x12, 0x73, 0x82, - 0x36, 0xa3, 0x25, 0x11, 0x78, 0x84, 0x3c, 0x16, 0xc6, 0x39, 0x3f, 0x7c, 0xd4, 0x41, 0x6d, 0xce, - 0x58, 0x04, 0x2f, 0x41, 0x5d, 0xfd, 0x32, 0xf4, 0x4d, 0x6d, 0xa0, 0xd9, 0x0d, 0xd7, 0x90, 0x70, - 0xe6, 0xc3, 0x27, 0xc0, 0xe0, 0x24, 0xf6, 0x49, 0x6a, 0xea, 0x79, 0x3e, 0x47, 0x70, 0x02, 0x80, - 0x54, 0xcc, 0xd5, 0x78, 0x66, 0x75, 0xa0, 0xd9, 0xcd, 0xf1, 0x85, 0x73, 0xd8, 0xc2, 0x99, 0x1f, - 0xb9, 0x69, 0xed, 0x7e, 0xdb, 0xaf, 0xb8, 0x27, 0x6a, 0x78, 0x0d, 0x0c, 0x35, 0x3a, 0x37, 0x6b, - 0x83, 0xaa, 0xdd, 0x1c, 0xf7, 0xca, 0x75, 0xce, 0x1b, 0x45, 0xbe, 0x8d, 0x45, 0x9a, 0x15, 0xd5, - 0x85, 0x1e, 0x7e, 0x06, 0xff, 0x09, 0x26, 0x70, 0xb4, 0xe0, 0x2b, 0x9c, 0x12, 0x6e, 0x9e, 0xa9, - 0xbe, 0x5d, 0x27, 0x5f, 0xd3, 0x91, 0x6b, 0x3a, 0xc5, 0x9a, 0xce, 0x0d, 0x0b, 0xe3, 0xe9, 0x53, - 0x59, 0xfe, 0x7b, 0xdb, 0xef, 0x64, 0x98, 0x46, 0x93, 0xe1, 0x69, 0xf1, 0xd0, 0x6d, 0x2a, 0xf8, - 0x41, 0xa1, 0xde, 0x7b, 0xd0, 0x3c, 0xe9, 0x0b, 0xff, 0x07, 0xd5, 0x5b, 0x92, 0x15, 0x66, 0xc8, - 0x10, 0xbe, 0x00, 0x67, 0x1b, 0x1c, 0xad, 0x89, 0x32, 0xa2, 0x39, 0xee, 0x94, 0x87, 0x56, 0xb5, - 0x6e, 0xae, 0x98, 0xe8, 0xd7, 0xda, 0xf0, 0x87, 0x0e, 0xce, 0x24, 0x31, 0x83, 0x6d, 0xa0, 0x1f, - 0x6d, 0xd5, 0x43, 0x1f, 0x3e, 0x03, 0x6d, 0xce, 0xd6, 0xa9, 0x47, 0x16, 0x5e, 0x4a, 0xb0, 0x60, - 0x07, 0x6b, 0x5b, 0x79, 0xf6, 0x26, 0x4f, 0x42, 0x74, 0x74, 0xa9, 0xaa, 0x5c, 0xba, 0x2c, 0x37, - 0xfc, 0x84, 0x39, 0xcd, 0x9b, 0x1e, 0xcc, 0xe9, 0x82, 0x73, 0x7e, 0x87, 0x93, 0xc5, 0x57, 0x42, - 0xcc, 0xda, 0x40, 0xb3, 0x5b, 0x6e, 0x5d, 0xe2, 0x77, 0x84, 0xc0, 0xd7, 0xa0, 0x8a, 0x69, 0xa2, - 0xec, 0x6a, 0x4c, 0x5f, 0xfe, 0xdc, 0xf6, 0x9f, 0x07, 0xa1, 0x58, 0xad, 0x97, 0x8e, 0xc7, 0x28, - 0x2a, 0x6e, 0x24, 0xff, 0xbc, 0xe2, 0xfe, 0x2d, 0x12, 0x59, 0x42, 0xb8, 0x33, 0x8b, 0x85, 0x2b, - 0xcb, 0xe0, 0x08, 0x18, 0x7c, 0x9d, 0x24, 0x51, 0x66, 0x1a, 0xff, 0xf0, 0xdb, 0x2d, 0x84, 0x10, - 0x81, 0x86, 0xba, 0x27, 0xf9, 0x27, 0xb3, 0x3e, 0xd0, 0xec, 0xf6, 0x18, 0x96, 0xe7, 0xff, 0x98, - 0x25, 0xc4, 0x3d, 0x4f, 0x8a, 0x68, 0xb8, 0x01, 0xad, 0xd2, 0x56, 0xf0, 0x0a, 0xd4, 0x97, 0x38, - 0xc2, 0xb1, 0x47, 0x94, 0x75, 0x7f, 0xed, 0x7a, 0x50, 0xca, 0x6b, 0xbd, 0x23, 0x61, 0xb0, 0x12, - 0xca, 0xd2, 0x96, 0x5b, 0x20, 0x68, 0x82, 0xba, 0x4f, 0xbc, 0x90, 0xe2, 0x48, 0x9d, 0x6a, 0xcb, - 0x3d, 0xc0, 0xe9, 0xf4, 0x7e, 0x67, 0x69, 0x0f, 0x3b, 0x4b, 0xfb, 0xb5, 0xb3, 0xb4, 0xef, 0x7b, - 0xab, 0xf2, 0xb0, 0xb7, 0x2a, 0x8f, 0x7b, 0xab, 0xf2, 0xc5, 0x3e, 0xb1, 0x48, 0x4e, 0xae, 0x5e, - 0x8c, 0xc7, 0x22, 0x05, 0xd0, 0x37, 0xf5, 0x0c, 0x95, 0x51, 0x4b, 0x43, 0x51, 0x57, 0x7f, 0x02, - 0x00, 0x00, 0xff, 0xff, 0x05, 0x24, 0x0e, 0xf0, 0xda, 0x03, 0x00, 0x00, + // 527 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x53, 0x4d, 0x8e, 0xda, 0x30, + 0x18, 0x25, 0xc0, 0xf0, 0x63, 0x0a, 0x0b, 0x33, 0xea, 0x04, 0x2a, 0x05, 0x84, 0xd4, 0x2a, 0xaa, + 0x54, 0x5b, 0x30, 0xbb, 0x51, 0x37, 0x65, 0xa4, 0x4a, 0xec, 0x50, 0x5a, 0xa9, 0x6a, 0x37, 0xc8, + 0x49, 0xdc, 0x10, 0x35, 0x8e, 0xad, 0xd8, 0xcc, 0x94, 0x65, 0x6f, 0xd0, 0x83, 0xf4, 0x20, 0xb3, + 0x9c, 0x65, 0xd5, 0x05, 0xaa, 0xe0, 0x06, 0x3d, 0x41, 0x65, 0x27, 0x20, 0xb2, 0x69, 0x57, 0xf1, + 0xfb, 0xde, 0xfb, 0xec, 0xef, 0x3d, 0x3b, 0xa0, 0x2f, 0xe3, 0x90, 0xe2, 0x88, 0x31, 0x2c, 0x38, + 0x4f, 0x90, 0xc8, 0xb8, 0xe2, 0xb0, 0xa5, 0x8b, 0x28, 0x62, 0x6c, 0x38, 0x2c, 0xd1, 0x2b, 0x41, + 0x32, 0xc2, 0x64, 0xae, 0x1a, 0x0e, 0xca, 0x1c, 0x91, 0x92, 0xaa, 0x82, 0xba, 0x8c, 0x78, 0xc4, + 0xcd, 0x12, 0xeb, 0x55, 0x51, 0x75, 0x02, 0x2e, 0x19, 0x97, 0xd8, 0x27, 0x92, 0xe2, 0xbb, 0xa9, + 0x4f, 0x15, 0x99, 0xe2, 0x80, 0xc7, 0x69, 0xce, 0x4f, 0xbe, 0x55, 0x41, 0x7d, 0xc9, 0x79, 0x02, + 0xaf, 0x40, 0xd3, 0x6c, 0x19, 0x87, 0xb6, 0x35, 0xb6, 0xdc, 0xb6, 0xd7, 0xd0, 0x70, 0x11, 0xc2, + 0xa7, 0xa0, 0x21, 0x69, 0x1a, 0xd2, 0xcc, 0xae, 0xe6, 0xf5, 0x1c, 0xc1, 0x1b, 0x00, 0xb4, 0x62, + 0x69, 0xc6, 0xb3, 0x6b, 0x63, 0xcb, 0xed, 0xcc, 0x2e, 0xd1, 0xd1, 0x05, 0x5a, 0x9e, 0xb8, 0x79, + 0xfd, 0x61, 0x37, 0xaa, 0x78, 0x67, 0x6a, 0x38, 0x05, 0x0d, 0x33, 0xba, 0xb4, 0xeb, 0xe3, 0x9a, + 0xdb, 0x99, 0xf5, 0xcb, 0x7d, 0x6f, 0x34, 0x57, 0xb4, 0x15, 0x42, 0xf8, 0x11, 0x3c, 0x51, 0x5c, + 0x91, 0x64, 0x25, 0xd7, 0x24, 0xa3, 0xd2, 0xbe, 0x30, 0x07, 0x0e, 0x50, 0xee, 0x0f, 0x69, 0x7f, + 0xa8, 0xf0, 0x87, 0x6e, 0x79, 0x9c, 0xce, 0x9f, 0xe9, 0xf6, 0x3f, 0xbb, 0x51, 0x7f, 0x4b, 0x58, + 0x72, 0x33, 0x39, 0x6f, 0x9e, 0x78, 0x1d, 0x03, 0xdf, 0xe5, 0xe8, 0x47, 0x15, 0x5c, 0xe8, 0x63, + 0x17, 0xb0, 0x07, 0xaa, 0x27, 0xff, 0xd5, 0x38, 0x84, 0xcf, 0x41, 0x4f, 0xf2, 0x4d, 0x16, 0xd0, + 0x55, 0x90, 0x51, 0xa2, 0xf8, 0x31, 0x83, 0x6e, 0x5e, 0xbd, 0xcd, 0x8b, 0x10, 0x9f, 0xec, 0xd4, + 0x8c, 0x9d, 0xab, 0xb2, 0x9d, 0x0f, 0x44, 0x32, 0x63, 0xe9, 0x64, 0x66, 0x00, 0x5a, 0xf2, 0x9e, + 0x88, 0xd5, 0x67, 0x4a, 0xed, 0xfa, 0xd8, 0x72, 0xbb, 0x5e, 0x53, 0xe3, 0xb7, 0x94, 0xc2, 0xd7, + 0xa0, 0x46, 0x98, 0x30, 0xf6, 0xda, 0xf3, 0x97, 0xbf, 0x76, 0xa3, 0x17, 0x51, 0xac, 0xd6, 0x1b, + 0x1f, 0x05, 0x9c, 0xe1, 0xe2, 0x32, 0xf3, 0xcf, 0x2b, 0x19, 0x7e, 0xc1, 0x6a, 0x2b, 0xa8, 0x44, + 0x8b, 0x54, 0x79, 0xba, 0x4d, 0x07, 0x2b, 0x37, 0x42, 0x24, 0x5b, 0xbb, 0xf1, 0x9f, 0x7c, 0xbc, + 0x42, 0x08, 0x31, 0x68, 0x9b, 0x8b, 0xd7, 0x3b, 0xd9, 0xcd, 0xb1, 0xe5, 0xf6, 0x66, 0xb0, 0x3c, + 0xff, 0xfb, 0xad, 0xa0, 0x5e, 0x4b, 0x14, 0xab, 0xc9, 0x1d, 0xe8, 0x96, 0x5c, 0xc1, 0x6b, 0xd0, + 0xf4, 0x49, 0x42, 0xd2, 0x80, 0x9a, 0xe8, 0xfe, 0x79, 0xea, 0x51, 0xa9, 0x9f, 0xd5, 0x3d, 0x8d, + 0xa3, 0xb5, 0x32, 0x91, 0x76, 0xbd, 0x02, 0x41, 0x1b, 0x34, 0x43, 0x1a, 0xc4, 0x8c, 0x24, 0xe6, + 0x4d, 0x75, 0xbd, 0x23, 0x9c, 0xcf, 0x1f, 0xf6, 0x8e, 0xf5, 0xb8, 0x77, 0xac, 0xdf, 0x7b, 0xc7, + 0xfa, 0x7e, 0x70, 0x2a, 0x8f, 0x07, 0xa7, 0xf2, 0xf3, 0xe0, 0x54, 0x3e, 0xb9, 0x67, 0x11, 0xe9, + 0xc9, 0xcd, 0xd3, 0x0e, 0x78, 0x62, 0x00, 0xfe, 0x6a, 0xfe, 0x17, 0x13, 0x94, 0xdf, 0x30, 0xd4, + 0xf5, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x79, 0x9d, 0x01, 0x78, 0x83, 0x03, 0x00, 0x00, } func (m *Pool) Marshal() (dAtA []byte, err error) { @@ -328,11 +324,9 @@ func (m *Pool) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x2a if len(m.Assets) > 0 { - for k := range m.Assets { - v := m.Assets[k] - baseI := i + for iNdEx := len(m.Assets) - 1; iNdEx >= 0; iNdEx-- { { - size, err := (&v).MarshalToSizedBuffer(dAtA[:i]) + size, err := m.Assets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -340,14 +334,6 @@ func (m *Pool) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintPool(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x12 - i -= len(k) - copy(dAtA[i:], k) - i = encodeVarintPool(dAtA, i, uint64(len(k))) - i-- - dAtA[i] = 0xa - i = encodeVarintPool(dAtA, i, uint64(baseI-i)) - i-- dAtA[i] = 0x22 } } @@ -536,12 +522,9 @@ func (m *Pool) Size() (n int) { l = m.PoolParams.Size() n += 1 + l + sovPool(uint64(l)) if len(m.Assets) > 0 { - for k, v := range m.Assets { - _ = k - _ = v - l = v.Size() - mapEntrySize := 1 + len(k) + sovPool(uint64(len(k))) + 1 + l + sovPool(uint64(l)) - n += mapEntrySize + 1 + sovPool(uint64(mapEntrySize)) + for _, e := range m.Assets { + l = e.Size() + n += 1 + l + sovPool(uint64(l)) } } l = m.TotalShares.Size() @@ -766,105 +749,10 @@ func (m *Pool) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Assets == nil { - m.Assets = make(map[string]PoolAsset) - } - var mapkey string - mapvalue := &PoolAsset{} - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPool - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPool - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthPool - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey < 0 { - return ErrInvalidLengthPool - } - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var mapmsglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPool - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - mapmsglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if mapmsglen < 0 { - return ErrInvalidLengthPool - } - postmsgIndex := iNdEx + mapmsglen - if postmsgIndex < 0 { - return ErrInvalidLengthPool - } - if postmsgIndex > l { - return io.ErrUnexpectedEOF - } - mapvalue = &PoolAsset{} - if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { - return err - } - iNdEx = postmsgIndex - } else { - iNdEx = entryPreIndex - skippy, err := skipPool(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthPool - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } + m.Assets = append(m.Assets, PoolAsset{}) + if err := m.Assets[len(m.Assets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } - m.Assets[mapkey] = *mapvalue iNdEx = postIndex case 5: if wireType != 2 { diff --git a/x/gmm/types/poolI.go b/x/gmm/types/poolI.go index e3e18bd0..f33167db 100644 --- a/x/gmm/types/poolI.go +++ b/x/gmm/types/poolI.go @@ -5,14 +5,14 @@ import ( ) func (p *PoolI) ToPool() Pool { - assets := make(map[string]PoolAsset) + assets := []PoolAsset{} //make(map[PoolAsset]) for _, asset := range p.Assets { weight := sdkmath.NewIntFromUint64(uint64(asset.Weight)) - assets[asset.Balance.Denom] = PoolAsset{ + assets = append(assets, PoolAsset{ Token: *asset.Balance, Weight: &weight, Decimal: sdkmath.NewIntFromUint64(uint64(asset.Decimal)), - } + }) } return Pool{ PoolId: p.Id, diff --git a/x/gmm/types/pool_apr.go b/x/gmm/types/pool_apr.go index 2a094490..927da399 100644 --- a/x/gmm/types/pool_apr.go +++ b/x/gmm/types/pool_apr.go @@ -32,18 +32,18 @@ func NewPoolAPR(ctx sdk.Context) *PoolAPR { } // Please multiply market price of every assets after getting to display as USD. -func (pa *PoolAPR) CalcAPR(ctx sdk.Context, tvl map[string]PoolAsset) sdk.Coins { +func (pa *PoolAPR) CalcAPR(ctx sdk.Context, tvl []PoolAsset) sdk.Coins { oneYearAsSec := int64(60 * 60 * 24 * 365) var apr sdk.Coins for _, coin := range pa.Fees { interval := ctx.BlockTime().Unix() - pa.CreatedAt - if _, found := tvl[coin.Denom]; found && !tvl[coin.Denom].Token.Amount.IsZero() { + if asset, found := FindAsset(tvl, coin.Denom); found && !asset.Token.Amount.IsZero() { if interval <= 0 { apr = apr.Add(sdk.NewCoin(coin.Denom, sdk.NewInt(0))) continue } avg := coin.Amount.Mul(sdkmath.NewInt(oneYearAsSec)).Mul(sdk.NewInt(1e10)).Quo(sdkmath.NewInt(interval)) - avg = avg.Quo(tvl[coin.Denom].Token.Amount) + avg = avg.Quo(asset.Token.Amount) apr = apr.Add(sdk.NewCoin(coin.Denom, avg)) } } diff --git a/x/gmm/types/pool_stable.go b/x/gmm/types/pool_stable.go index 5e800e6b..d530c4bf 100644 --- a/x/gmm/types/pool_stable.go +++ b/x/gmm/types/pool_stable.go @@ -20,8 +20,12 @@ func (p *Pool) estimateShareInStablePool(coins sdk.Coins) (sdk.Coin, error) { for _, asset := range coins { currentWeight := sdkmath.LegacyDec(asset.Amount).Quo(sdkmath.LegacyNewDecFromInt(sum)) - balanceRatiosWithFee[asset.Denom] = sdkmath.LegacyDec(asset.Amount.Add(p.Assets[asset.Denom].Token.Amount)).Quo( - sdkmath.LegacyNewDecFromInt(p.Assets[asset.Denom].Token.Amount), + assetInPool, _, exist := p.GetAssetByDenom(asset.Denom) + if !exist { + continue + } + balanceRatiosWithFee[asset.Denom] = sdkmath.LegacyDec(asset.Amount.Add(assetInPool.Token.Amount)).Quo( + sdkmath.LegacyNewDecFromInt(assetInPool.Token.Amount), ) invariantRatioWithFees = invariantRatioWithFees.Add( @@ -33,7 +37,10 @@ func (p *Pool) estimateShareInStablePool(coins sdk.Coins) (sdk.Coin, error) { newBalances := sdk.NewCoins() for _, amountIn := range coins { - asset := p.Assets[amountIn.Denom] + asset, _, exist := p.GetAssetByDenom(amountIn.Denom) + if !exist { + continue + } var amountInWithoutFee sdkmath.Int // Check if the balance ratio is greater than the ideal ratio to charge fees or not if balanceRatiosWithFee[asset.Token.Denom].GT(invariantRatioWithFees) { @@ -89,18 +96,21 @@ func (p *Pool) estimateSwapInStablePool(tokenIn sdk.Coin, denomOut string) (sdk. inv := calculateInvariantInStablePool(*p.PoolParams.Amp, p.GetLiquidity()) assets := p.Assets - - balance := assets[tokenIn.Denom].Token.Amount.Add(tokenInDec.RoundInt()) - assets[tokenIn.Denom] = PoolAsset{ + asset, index, exist := p.GetAssetByDenom(tokenIn.Denom) + if !exist { + return sdk.Coin{}, ErrNotFoundAssetInPool + } + balance := asset.Token.Amount.Add(tokenInDec.RoundInt()) + assets[index] = PoolAsset{ Token: sdk.NewCoin(tokenIn.Denom, balance), - Weight: assets[tokenIn.Denom].Weight, - Decimal: assets[tokenIn.Denom].Decimal, + Weight: assets[index].Weight, + Decimal: assets[index].Decimal, } finalBalanceOut, err := getTokenBalanceGivenInvariantAndAllOtherBalances( - *p.PoolParams.Amp, inv, assets, tokenIn.Denom, + *p.PoolParams.Amp, inv, assets, tokenIn.Amount, ) - out := p.Assets[denomOut].Token.Amount.Sub(finalBalanceOut).Sub(sdkmath.OneInt()) + out := p.Assets[index].Token.Amount.Sub(finalBalanceOut).Sub(sdkmath.OneInt()) return sdk.NewCoin(denomOut, out), err } @@ -191,25 +201,25 @@ func calculateInvariantInStablePool( func getTokenBalanceGivenInvariantAndAllOtherBalances( amp sdkmath.Int, inv sdkmath.Int, - assets map[string]PoolAsset, - tokenInDenom string, + assets []PoolAsset, + tokenInAmount sdkmath.Int, ) (sdkmath.Int, error) { numTokens := sdkmath.NewInt(int64(len(assets))) ampTimeTotal := amp.Mul(numTokens) sum := sdkmath.NewInt(0) - PD := numTokens.Mul(assets[tokenInDenom].Token.Amount) + PD := numTokens.Mul(tokenInAmount) for _, asset := range assets { PD = PD.Mul(asset.Token.Amount).Mul(numTokens).Quo(inv) sum = sum.Add(asset.Token.Amount) } - sum = sum.Sub(assets[tokenInDenom].Token.Amount) + sum = sum.Sub(tokenInAmount) inv2 := inv.Mul(inv) - c := inv2.Quo(ampTimeTotal.Mul(PD)).Mul(AmpPrecision).Mul(assets[tokenInDenom].Token.Amount) + c := inv2.Quo(ampTimeTotal.Mul(PD)).Mul(AmpPrecision).Mul(tokenInAmount) b := sum.Add(inv.Quo(ampTimeTotal).Mul(AmpPrecision)) tokenBalance := (inv2.Add(c)).Quo(inv.Add(b)) diff --git a/x/gmm/types/pool_weight.go b/x/gmm/types/pool_weight.go index 0cdd26ae..e7b25083 100644 --- a/x/gmm/types/pool_weight.go +++ b/x/gmm/types/pool_weight.go @@ -2,6 +2,7 @@ package types import ( fmt "fmt" + "math/big" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -28,9 +29,13 @@ func (p *Pool) estimateShareWithSingleLiquidityInWeightPool(coin sdk.Coin) (sdk. decAsset := sdk.NewDecCoinFromCoin(asset.Token) weight := sdk.NewDecFromInt(*asset.Weight).Quo(sdk.NewDec(100)) // divide by 100 ratio := decToken.Amount.Quo(decAsset.Amount).Add(sdk.NewDec(1)) - - factor := (ApproximatePow(ratio, weight, 100).Sub(sdk.OneDec())) - issueAmount := p.TotalShares.Amount.Mul(factor.RoundInt()).Quo(sdk.NewInt(1e10)) + precision := big.NewInt(1) //sdk.MustNewDecFromStr("0.00000001") + _ = weight + _ = ratio + _ = precision + factor := sdk.NewInt(1) + //factor := (ApproximatePow(ratio.BigInt(), weight.BigInt(), precision).Sub(sdk.OneDec())) + issueAmount := p.TotalShares.Amount.Mul(factor).Quo(sdk.NewInt(1e10)) outputToken := sdk.Coin{ Amount: issueAmount, Denom: p.TotalShares.Denom, @@ -99,24 +104,15 @@ func (p *Pool) estimateSwapInWeightPool(amountIn sdk.Coin, denomOut string) (sdk balanceInPlusAmount := balanceIn.Add(amount) ratio := balanceIn.Quo(balanceInPlusAmount) oneMinusRatio := sdk.NewDec(1).Sub(ratio) - power := weightIn.Quo(weightOut) - factor := ApproximatePow(oneMinusRatio, power, 100) // 100 iterations for example - amountOut := balanceOut.Mul(factor) + precision := "0.00000001" //sdk.MustNewDecFromStr("0.00000001") + factor, err := ApproximatePow(oneMinusRatio.String(), power.String(), precision) // 100 iterations for example + if err != nil { + return sdk.Coin{}, err + } + amountOut := balanceOut.Mul(sdk.MustNewDecFromStr(factor.String())) return sdk.Coin{ Amount: amountOut.RoundInt(), Denom: denomOut, }, nil } - -// ApproximatePow approximates (base ^ exponent) using a series expansion. -// Here's a simple approximation; you might need a more accurate one depending on your needs. -func ApproximatePow(base sdk.Dec, exponent sdk.Dec, iterations int) sdk.Dec { - result := sdk.OneDec() // Start with 1 as the initial result - term := sdk.OneDec() // The current term starts at 1 (base^0) - for n := 1; n <= iterations; n++ { - term = term.Mul(base).Mul(exponent).QuoInt64(int64(n)) // term *= base * exponent / n - result = result.Sub(term) - } - return result -} diff --git a/x/gmm/types/pool_weight_test.go b/x/gmm/types/pool_weight_test.go new file mode 100644 index 00000000..29933de0 --- /dev/null +++ b/x/gmm/types/pool_weight_test.go @@ -0,0 +1,35 @@ +package types + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +func TestApproximatePow(t *testing.T) { + // Set a common precision for the tests + precision := "0.000000001" + + // Test cases + tests := []struct { + base string + exponent string + expected string + }{ + {"1.45", "1.5", "1.746031213"}, + {"1.05", "3.5", "1.186212638"}, + {"0.91", "1.85", "0.839898055"}, + } + + for _, tt := range tests { + base := types.MustNewDecFromStr(tt.base) + exponent := types.MustNewDecFromStr(tt.exponent) + expected := types.MustNewDecFromStr(tt.expected) + + result, err := ApproximatePow(base.String(), exponent.String(), precision) + require.NoError(t, err) + resultAsDec := types.MustNewDecFromStr(result.String()) + require.Equal(t, expected, resultAsDec) + } +}