Skip to content

Commit

Permalink
refactor: resolve goroutine leak stemming from creating a grpc connec…
Browse files Browse the repository at this point in the history
…tion for every cosmwasm pool in route (#394)

* refactor: resolve goroutine leak stemming from creating a grpc connection for every cosmwasm pool in route. Share one connection.

* lint
  • Loading branch information
p0mvn authored Jul 18, 2024
1 parent 6489a1b commit 055c719
Show file tree
Hide file tree
Showing 19 changed files with 145 additions and 84 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ Ref: https://keepachangelog.com/en/1.0.0/

# Changelog

## v25.4.1

- Resolve goroutine leak stemming from creating a grpc connection for every cosmwasm pool in route. Share one connection.

## v25.4.0

- Remove spread factor from fee as it is included in the price impact
Expand Down
5 changes: 4 additions & 1 deletion app/sidecar_query_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,10 @@ func NewSideCarQueryServer(appCodec codec.Codec, config domain.Config, logger lo
}

// Initialize pools repository, usecase and HTTP handler
poolsUseCase := poolsUseCase.NewPoolsUsecase(config.Pools, config.ChainGRPCGatewayEndpoint, routerRepository, tokensUseCase.GetChainScalingFactorByDenomMut, logger)
poolsUseCase, err := poolsUseCase.NewPoolsUsecase(config.Pools, config.ChainGRPCGatewayEndpoint, routerRepository, tokensUseCase.GetChainScalingFactorByDenomMut, logger)
if err != nil {
return nil, err
}

// Initialize candidate route searcher
candidateRouteSearcher := routerUseCase.NewCandidateRouteFinder(routerRepository, logger)
Expand Down
4 changes: 3 additions & 1 deletion domain/mocks/pools_usecase_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ func (pm *PoolsUsecaseMock) GetRoutesFromCandidates(candidateRoutes sqsdomain.Ca
}

// TODO: note that taker fee is force set to zero
routablePool, err := pools.NewRoutablePool(foundPool, candidatePool.TokenOutDenom, osmomath.ZeroDec(), domain.CosmWasmPoolRouterConfig{}, domain.UnsetScalingFactorGetterCb)
routablePool, err := pools.NewRoutablePool(foundPool, candidatePool.TokenOutDenom, osmomath.ZeroDec(), pools.CosmWasmPoolsParams{
ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb,
})
if err != nil {
return nil, err
}
Expand Down
65 changes: 47 additions & 18 deletions pools/usecase/pools_usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ import (
"sync"

"cosmossdk.io/math"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
"github.com/osmosis-labs/sqs/log"
"github.com/osmosis-labs/sqs/sqsdomain"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"

"github.com/osmosis-labs/sqs/domain"
"github.com/osmosis-labs/sqs/domain/mvc"
Expand All @@ -32,11 +36,10 @@ type orderBookEntry struct {
type poolsUseCase struct {
pools sync.Map
routerRepository routerrepo.RouterRepository
cosmWasmConfig domain.CosmWasmPoolRouterConfig

canonicalOrderBookForBaseQuoteDenom sync.Map

scalingFactorGetterCb domain.ScalingFactorGetterCb
cosmWasmPoolsParams pools.CosmWasmPoolsParams

logger log.Logger
}
Expand All @@ -49,7 +52,7 @@ const (
)

// NewPoolsUsecase will create a new pools use case object
func NewPoolsUsecase(poolsConfig *domain.PoolsConfig, chainGRPCGatewayEndpoint string, routerRepository routerrepo.RouterRepository, scalingFactorGetterCb domain.ScalingFactorGetterCb, logger log.Logger) *poolsUseCase {
func NewPoolsUsecase(poolsConfig *domain.PoolsConfig, chainGRPCGatewayEndpoint string, routerRepository routerrepo.RouterRepository, scalingFactorGetterCb domain.ScalingFactorGetterCb, logger log.Logger) (*poolsUseCase, error) {
transmuterCodeIDsMap := make(map[uint64]struct{}, len(poolsConfig.TransmuterCodeIDs))
for _, codeId := range poolsConfig.TransmuterCodeIDs {
transmuterCodeIDsMap[codeId] = struct{}{}
Expand All @@ -70,21 +73,31 @@ func NewPoolsUsecase(poolsConfig *domain.PoolsConfig, chainGRPCGatewayEndpoint s
generalizedCosmWasmCodeIDsMap[codeId] = struct{}{}
}

wasmClient, err := initializeWasmClient(chainGRPCGatewayEndpoint)
if err != nil {
return nil, err
}

return &poolsUseCase{
cosmWasmConfig: domain.CosmWasmPoolRouterConfig{
TransmuterCodeIDs: transmuterCodeIDsMap,
AlloyedTransmuterCodeIDs: alloyedTransmuterCodeIDsMap,
OrderbookCodeIDs: orderbookCodeIDsMap,
GeneralCosmWasmCodeIDs: generalizedCosmWasmCodeIDsMap,
ChainGRPCGatewayEndpoint: chainGRPCGatewayEndpoint,
},
pools: sync.Map{},
routerRepository: routerRepository,

pools: sync.Map{},
routerRepository: routerRepository,
scalingFactorGetterCb: scalingFactorGetterCb,
cosmWasmPoolsParams: pools.CosmWasmPoolsParams{
Config: domain.CosmWasmPoolRouterConfig{
TransmuterCodeIDs: transmuterCodeIDsMap,
AlloyedTransmuterCodeIDs: alloyedTransmuterCodeIDsMap,
OrderbookCodeIDs: orderbookCodeIDsMap,
GeneralCosmWasmCodeIDs: generalizedCosmWasmCodeIDsMap,
ChainGRPCGatewayEndpoint: chainGRPCGatewayEndpoint,
},

WasmClient: wasmClient,

ScalingFactorGetterCb: scalingFactorGetterCb,
},

logger: logger,
}
}, nil
}

// GetAllPools returns all pools from the repository.
Expand Down Expand Up @@ -134,7 +147,7 @@ func (p *poolsUseCase) GetRoutesFromCandidates(candidateRoutes sqsdomain.Candida
takerFee = sqsdomain.DefaultTakerFee
}

routablePool, err := pools.NewRoutablePool(pool, candidatePool.TokenOutDenom, takerFee, p.cosmWasmConfig, p.scalingFactorGetterCb)
routablePool, err := pools.NewRoutablePool(pool, candidatePool.TokenOutDenom, takerFee, p.cosmWasmPoolsParams)
if err != nil {
skipErrorRoute = true
break
Expand Down Expand Up @@ -218,7 +231,7 @@ func (p *poolsUseCase) GetPoolSpotPrice(ctx context.Context, poolID uint64, take

// N.B.: Empty string for token out denom because it is irrelevant for calculating spot price.
// It is only relevant in the context of routing
routablePool, err := pools.NewRoutablePool(pool, "", takerFee, p.cosmWasmConfig, p.scalingFactorGetterCb)
routablePool, err := pools.NewRoutablePool(pool, "", takerFee, p.cosmWasmPoolsParams)
if err != nil {
return osmomath.BigDec{}, err
}
Expand All @@ -228,7 +241,7 @@ func (p *poolsUseCase) GetPoolSpotPrice(ctx context.Context, poolID uint64, take

// IsGeneralCosmWasmCodeID implements mvc.PoolsUsecase.
func (p *poolsUseCase) IsGeneralCosmWasmCodeID(codeId uint64) bool {
_, isGenneralCosmWasmCodeID := p.cosmWasmConfig.GeneralCosmWasmCodeIDs[codeId]
_, isGenneralCosmWasmCodeID := p.cosmWasmPoolsParams.Config.GeneralCosmWasmCodeIDs[codeId]
return isGenneralCosmWasmCodeID
}

Expand Down Expand Up @@ -439,10 +452,26 @@ func (p *poolsUseCase) GetAllCanonicalOrderbookPoolIDs() ([]domain.CanonicalOrde

// GetCosmWasmPoolConfig implements mvc.PoolsUsecase.
func (p *poolsUseCase) GetCosmWasmPoolConfig() domain.CosmWasmPoolRouterConfig {
return p.cosmWasmConfig
return p.cosmWasmPoolsParams.Config
}

// formatBaseQuoteDenom formats the base and quote denom into a single string with a separator.
func formatBaseQuoteDenom(baseDenom, quoteDenom string) string {
return baseDenom + baseQuoteKeySeparator + quoteDenom
}

// initializeWasmClient initializes the wasm client given the node URI
// Returns error if fails to initialize the client
func initializeWasmClient(grpcGatewayEndpoint string) (wasmtypes.QueryClient, error) {
grpcClient, err := grpc.NewClient(grpcGatewayEndpoint,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
)
if err != nil {
return nil, err
}

wasmClient := wasmtypes.NewQueryClient(grpcClient)

return wasmClient, nil
}
28 changes: 19 additions & 9 deletions pools/usecase/pools_usecase_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ func (s *PoolsUsecaseTestSuite) TestGetRoutesFromCandidates() {
// to the wrong type. Note that the default is balancer.
brokenChainPool := *defaultPool
brokenChainPool.PoolType = poolmanagertypes.CosmWasm
_, err = pools.NewRoutablePool(&brokenChainPool, denomTwo, defaultTakerFee, domain.CosmWasmPoolRouterConfig{}, nil)

cosmWasmPoolsParams := pools.CosmWasmPoolsParams{
ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb,
}
_, err = pools.NewRoutablePool(&brokenChainPool, denomTwo, defaultTakerFee, cosmWasmPoolsParams)
// Validate that it is indeed broken.
s.Require().Error(err)

Expand Down Expand Up @@ -201,7 +205,8 @@ func (s *PoolsUsecaseTestSuite) TestGetRoutesFromCandidates() {
routerRepo.SetTakerFees(tc.takerFeeMap)

// Create pools use case
poolsUsecase := usecase.NewPoolsUsecase(&domain.PoolsConfig{}, "node-uri-placeholder", routerRepo, domain.UnsetScalingFactorGetterCb, &log.NoOpLogger{})
poolsUsecase, err := usecase.NewPoolsUsecase(&domain.PoolsConfig{}, "node-uri-placeholder", routerRepo, domain.UnsetScalingFactorGetterCb, &log.NoOpLogger{})
s.Require().NoError(err)

poolsUsecase.StorePools(tc.pools)

Expand Down Expand Up @@ -320,7 +325,7 @@ func (s *PoolsUsecaseTestSuite) TestProcessOrderbookPoolIDForBaseQuote() {
tc := tc
s.Run(tc.name, func() {

poolsUsecase := newDefaultPoolsUseCase()
poolsUsecase := s.newDefaultPoolsUseCase()

// Pre-set invalid data for the base/quote
if tc.preStoreInvalidEntry {
Expand Down Expand Up @@ -428,7 +433,7 @@ func (s *PoolsUsecaseTestSuite) TestStorePools() {
}
)

poolsUsecase := newDefaultPoolsUseCase()
poolsUsecase := s.newDefaultPoolsUseCase()

// Pre-set invalid data for the base/quote
poolsUsecase.StoreInvalidOrderBookEntry(invalidBaseDenom, orderBookQuoteDenom)
Expand Down Expand Up @@ -465,7 +470,7 @@ func (s *PoolsUsecaseTestSuite) TestStorePools() {
// by the StorePools and ProcessOrderbookPoolIDForBaseQuote tests.
func (s *PoolsUsecaseTestSuite) TestGetAllCanonicalOrderbooks_HappyPath() {

poolsUseCase := newDefaultPoolsUseCase()
poolsUseCase := s.newDefaultPoolsUseCase()

// Denom one and denom two
poolsUseCase.StoreValidOrdeBookEntry(denomOne, denomTwo, defaultPoolID, defaultPoolLiquidityCap)
Expand Down Expand Up @@ -500,14 +505,19 @@ func (s *PoolsUsecaseTestSuite) TestGetAllCanonicalOrderbooks_HappyPath() {

}

func (s *PoolsUsecaseTestSuite) newRoutablePool(pool sqsdomain.PoolI, tokenOutDenom string, takerFee osmomath.Dec, cosmWasmPoolIDs domain.CosmWasmPoolRouterConfig) domain.RoutablePool {
routablePool, err := pools.NewRoutablePool(pool, tokenOutDenom, takerFee, cosmWasmPoolIDs, domain.UnsetScalingFactorGetterCb)
func (s *PoolsUsecaseTestSuite) newRoutablePool(pool sqsdomain.PoolI, tokenOutDenom string, takerFee osmomath.Dec, cosmWasmConfig domain.CosmWasmPoolRouterConfig) domain.RoutablePool {
cosmWasmPoolsParams := pools.CosmWasmPoolsParams{
Config: cosmWasmConfig,
ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb,
}
routablePool, err := pools.NewRoutablePool(pool, tokenOutDenom, takerFee, cosmWasmPoolsParams)
s.Require().NoError(err)
return routablePool
}

func newDefaultPoolsUseCase() *usecase.PoolsUsecase {
func (s *PoolsUsecaseTestSuite) newDefaultPoolsUseCase() *usecase.PoolsUsecase {
routerRepo := routerrepo.New(&log.NoOpLogger{})
poolsUsecase := usecase.NewPoolsUsecase(&domain.PoolsConfig{}, "node-uri-placeholder", routerRepo, domain.UnsetScalingFactorGetterCb, &log.NoOpLogger{})
poolsUsecase, err := usecase.NewPoolsUsecase(&domain.PoolsConfig{}, "node-uri-placeholder", routerRepo, domain.UnsetScalingFactorGetterCb, &log.NoOpLogger{})
s.Require().NoError(err)
return poolsUsecase
}
3 changes: 2 additions & 1 deletion router/usecase/optimized_routes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,8 @@ func (s *RouterTestSuite) TestGetCustomQuote_GetCustomDirectQuote_Mainnet_UOSMOU
tokensRepositoryMock.SetTakerFees(mainnetState.TakerFeeMap)

// Setup pools usecase mock.
poolsUsecase := poolsusecase.NewPoolsUsecase(&domain.PoolsConfig{}, "node-uri-placeholder", tokensRepositoryMock, domain.UnsetScalingFactorGetterCb, &log.NoOpLogger{})
poolsUsecase, err := poolsusecase.NewPoolsUsecase(&domain.PoolsConfig{}, "node-uri-placeholder", tokensRepositoryMock, domain.UnsetScalingFactorGetterCb, &log.NoOpLogger{})
s.Require().NoError(err)
poolsUsecase.StorePools(mainnetState.Pools)

tokenMetaDataHolderMock := &mocks.TokenMetadataHolderMock{}
Expand Down
21 changes: 5 additions & 16 deletions router/usecase/pools/cosmwasm_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,16 @@ import (
"context"

wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
"github.com/osmosis-labs/sqs/domain"
"github.com/osmosis-labs/sqs/sqsdomain/json"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
)

// initializeWasmClient initializes the wasm client given the node URI
// Returns error if fails to initialize the client
func initializeWasmClient(grpcGatewayEndpoint string) (wasmtypes.QueryClient, error) {
grpcClient, err := grpc.NewClient(grpcGatewayEndpoint,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
)
if err != nil {
return nil, err
}

wasmClient := wasmtypes.NewQueryClient(grpcClient)

return wasmClient, nil
type CosmWasmPoolsParams struct {
Config domain.CosmWasmPoolRouterConfig
WasmClient wasmtypes.QueryClient
ScalingFactorGetterCb domain.ScalingFactorGetterCb
}

// queryCosmwasmContract queries the cosmwasm contract given the contract address, request and response
Expand Down
4 changes: 2 additions & 2 deletions router/usecase/pools/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ type (
func NewRoutableCosmWasmPoolWithCustomModel(
pool sqsdomain.PoolI,
cosmwasmPool *cwpoolmodel.CosmWasmPool,
cosmWasmConfig domain.CosmWasmPoolRouterConfig,
cosmWasmPoolsParams CosmWasmPoolsParams,
tokenOutDenom string,
takerFee osmomath.Dec,
) (domain.RoutablePool, error) {
return newRoutableCosmWasmPoolWithCustomModel(pool, cosmwasmPool, cosmWasmConfig, tokenOutDenom, takerFee)
return newRoutableCosmWasmPoolWithCustomModel(pool, cosmwasmPool, cosmWasmPoolsParams, tokenOutDenom, takerFee)
}
25 changes: 10 additions & 15 deletions router/usecase/pools/pool_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

// NewRoutablePool creates a new RoutablePool.
// Panics if pool is of invalid type or if does not contain tick data when a concentrated pool.
func NewRoutablePool(pool sqsdomain.PoolI, tokenOutDenom string, takerFee osmomath.Dec, cosmWasmConfig domain.CosmWasmPoolRouterConfig, scalingFactorGetterCb domain.ScalingFactorGetterCb) (domain.RoutablePool, error) {
func NewRoutablePool(pool sqsdomain.PoolI, tokenOutDenom string, takerFee osmomath.Dec, cosmWasmPoolsParams CosmWasmPoolsParams) (domain.RoutablePool, error) {
poolType := pool.GetType()
chainPool := pool.GetUnderlyingPool()
if poolType == poolmanagertypes.Concentrated {
Expand Down Expand Up @@ -84,12 +84,12 @@ func NewRoutablePool(pool sqsdomain.PoolI, tokenOutDenom string, takerFee osmoma
}, nil
}

return newRoutableCosmWasmPool(pool, cosmWasmConfig, tokenOutDenom, takerFee, scalingFactorGetterCb)
return newRoutableCosmWasmPool(pool, tokenOutDenom, takerFee, cosmWasmPoolsParams)
}

// newRoutableCosmWasmPool creates a new RoutablePool for CosmWasm pools.
// Panics if the given pool is not a cosmwasm pool or if the
func newRoutableCosmWasmPool(pool sqsdomain.PoolI, cosmWasmConfig domain.CosmWasmPoolRouterConfig, tokenOutDenom string, takerFee osmomath.Dec, scalingFactorGetterCb domain.ScalingFactorGetterCb) (domain.RoutablePool, error) {
func newRoutableCosmWasmPool(pool sqsdomain.PoolI, tokenOutDenom string, takerFee osmomath.Dec, cosmWasmPoolsParams CosmWasmPoolsParams) (domain.RoutablePool, error) {
chainPool := pool.GetUnderlyingPool()
poolType := pool.GetType()

Expand All @@ -104,7 +104,7 @@ func newRoutableCosmWasmPool(pool sqsdomain.PoolI, cosmWasmConfig domain.CosmWas
balances := pool.GetSQSPoolModel().Balances

// Check if the pool is a transmuter pool
_, isTransmuter := cosmWasmConfig.TransmuterCodeIDs[cosmwasmPool.CodeId]
_, isTransmuter := cosmWasmPoolsParams.Config.TransmuterCodeIDs[cosmwasmPool.CodeId]
if isTransmuter {
spreadFactor := pool.GetSQSPoolModel().SpreadFactor

Expand All @@ -118,21 +118,16 @@ func newRoutableCosmWasmPool(pool sqsdomain.PoolI, cosmWasmConfig domain.CosmWas
}, nil
}

_, isGeneralizedCosmWasmPool := cosmWasmConfig.GeneralCosmWasmCodeIDs[cosmwasmPool.CodeId]
_, isGeneralizedCosmWasmPool := cosmWasmPoolsParams.Config.GeneralCosmWasmCodeIDs[cosmwasmPool.CodeId]
if isGeneralizedCosmWasmPool {
wasmClient, err := initializeWasmClient(cosmWasmConfig.ChainGRPCGatewayEndpoint)
if err != nil {
return nil, err
}

spreadFactor := pool.GetSQSPoolModel().SpreadFactor

// for most other CosmWasm pools, interaction with the chain will
// be required. As a result, we have a custom implementation.
return NewRoutableCosmWasmPool(cosmwasmPool, balances, tokenOutDenom, takerFee, spreadFactor, wasmClient, scalingFactorGetterCb), nil
return NewRoutableCosmWasmPool(cosmwasmPool, balances, tokenOutDenom, takerFee, spreadFactor, cosmWasmPoolsParams), nil
}

return newRoutableCosmWasmPoolWithCustomModel(pool, cosmwasmPool, cosmWasmConfig, tokenOutDenom, takerFee)
return newRoutableCosmWasmPoolWithCustomModel(pool, cosmwasmPool, cosmWasmPoolsParams, tokenOutDenom, takerFee)
}

// newRoutableCosmWasmPoolWithCustomModel creates a new RoutablePool for CosmWasm pools that require a custom CosmWasmPoolModel.
Expand All @@ -143,7 +138,7 @@ func newRoutableCosmWasmPool(pool sqsdomain.PoolI, cosmWasmConfig domain.CosmWas
func newRoutableCosmWasmPoolWithCustomModel(
pool sqsdomain.PoolI,
cosmwasmPool *cwpoolmodel.CosmWasmPool,
cosmWasmConfig domain.CosmWasmPoolRouterConfig,
cosmWasmPoolsParams CosmWasmPoolsParams,
tokenOutDenom string,
takerFee osmomath.Dec,
) (domain.RoutablePool, error) {
Expand All @@ -157,7 +152,7 @@ func newRoutableCosmWasmPoolWithCustomModel(
// since v2, we introduce concept of alloyed assets but not yet actively used
// since v3, we introduce concept of normalization factor
// `routableAlloyTransmuterPoolImpl` is v3 compatible
_, isAlloyedTransmuterCodeId := cosmWasmConfig.AlloyedTransmuterCodeIDs[cosmwasmPool.CodeId]
_, isAlloyedTransmuterCodeId := cosmWasmPoolsParams.Config.AlloyedTransmuterCodeIDs[cosmwasmPool.CodeId]
if isAlloyedTransmuterCodeId && model.IsAlloyTransmuter() {
if model.Data.AlloyTransmuter == nil {
return nil, domain.CosmWasmPoolDataMissingError{
Expand All @@ -176,7 +171,7 @@ func newRoutableCosmWasmPoolWithCustomModel(
}, nil
}

_, isOrderbookCodeId := cosmWasmConfig.OrderbookCodeIDs[cosmwasmPool.CodeId]
_, isOrderbookCodeId := cosmWasmPoolsParams.Config.OrderbookCodeIDs[cosmwasmPool.CodeId]
if isOrderbookCodeId && model.IsOrderbook() {
if model.Data.Orderbook == nil {
return nil, domain.CosmWasmPoolDataMissingError{
Expand Down
5 changes: 4 additions & 1 deletion router/usecase/pools/pool_factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,10 @@ func TestNewRoutableCosmWasmPoolWithCustomModel(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
routablePool, err := pools.NewRoutableCosmWasmPoolWithCustomModel(tt.pool, tt.cosmwasmPool, tt.cosmWasmConfig, tt.tokenOutDenom, tt.takerFee)
cosmWasmPoolsParams := pools.CosmWasmPoolsParams{
Config: tt.cosmWasmConfig,
}
routablePool, err := pools.NewRoutableCosmWasmPoolWithCustomModel(tt.pool, tt.cosmwasmPool, cosmWasmPoolsParams, tt.tokenOutDenom, tt.takerFee)

if tt.expectedError != nil {
require.Equal(t, tt.expectedError, err)
Expand Down
Loading

0 comments on commit 055c719

Please sign in to comment.