Repository: osmosis-labs/osmosis
Pull request #3055 (2022-10-19 18:22:31)

[stableswap]: Cap number of assets and post-scaled asset amounts to ensure pools never overflow
<!-- < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < ☺
v                               ✰  Thanks for creating a PR! ✰    
v    Before smashing the submit button please review the checkboxes.
v    If a checkbox is n/a - please still include it but + a little note why
v    If your PR doesn't close an issue, that's OK!  Just remove the Closes: #XXX line!
☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >  -->

Closes: #2827

## What is the purpose of the change

This PR adds guardrails to stableswap pool assets that ensures they can never overflow.

## Brief Changelog

- Added maximum *post-scaled* amount of `10_000_000_000` (this allows for 10B * 2^63 tokens per asset using scaling factors)
- Moved pool liquidity checks to `validatePoolLiquidity` function and added to pool creation, joins, and swaps
- Added basic unit tests to ensure expected bounds hold

## Testing and Verifying

Tests can be found in `amm_test.go`, `pool_test.go`, and `msgs_test.go`

## Documentation and Release Note

  - Does this pull request introduce a new feature or user-facing behavior changes? (no)
  - Is a relevant changelog entry added to the `Unreleased` section in `CHANGELOG.md`? (no)
  - How is the feature or change documented? (not documented)


Patch:
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 901271a09..cfc7a7c9d 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -20,6 +20,8 @@
   - app/wasm/**/*
 "C:x/claim":
   - x/claim/**/*
+"C:x/concentrated-liquidity":
+  - x/concentrated-liquidity/**/*
 "C:x/epochs":
   - x/epochs/**/*
 "C:x/gamm":
@@ -34,6 +36,8 @@
   - x/pool-incentives/**/*
 "C:x/superfluid":
   - x/superfluid/**/*
+"C:x/swaprouter":
+  - x/swaprouter/**/*
 "C:x/txfees":
   - x/txfees/**/*
 "C:x/tokenfactory":
@@ -52,7 +56,7 @@
 "C:CLI":
   - client/**/*
   - x/*/client/**/*
-# Add "C:docs" label to documented related files and directories. 
+# Add "C:docs" label to documented related files and directories.
 "C:docs":
   - bug.md
   - issue.md
@@ -61,4 +65,3 @@
   - x/**/*.md
   - x/**/*/*.md
   - tests/**/*.md
-  
diff --git a/.github/mergify.yml b/.github/mergify.yml
index 818cb1198..02bd5b149 100644
--- a/.github/mergify.yml
+++ b/.github/mergify.yml
@@ -110,3 +110,11 @@ pull_request_rules:
       backport:
         branches:
           - v13.x
+  - name: backport patches to concentrated-liquidity-main
+    conditions:
+      - base=main
+      - label=A:backport/cl
+    actions:
+      backport:
+        branches:
+          - concentrated-liquidity-main
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index c468161ab..4baa5ff16 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -4,6 +4,7 @@ on:
   push:
     branches:
       - main
+      - concentrated-liquidity-main
 
 jobs:
   golangci:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7eed7859..ee35bcc74 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -44,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Features
 
+* [#2788](https://github.com/osmosis-labs/osmosis/pull/2788) Add logarithm base 2 implementation.
 * [#2739](https://github.com/osmosis-labs/osmosis/pull/2739) Add pool type query
 
 ### Bug fixes
@@ -55,6 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Misc Improvements
 
 * [#2804](https://github.com/osmosis-labs/osmosis/pull/2804) Improve error handling and messages when parsing pool assets.
+* [#3035](https://github.com/osmosis-labs/osmosis/pull/3035) Remove `PokePool` from `PoolI` interface. Define on a new WeightedPoolExtension` instead.
 
 ## v12.0.0
 
diff --git a/chain.schema.json b/chain.schema.json
index 731bad99c..e367fa009 100644
--- a/chain.schema.json
+++ b/chain.schema.json
@@ -2,8 +2,10 @@
   "$schema": "http://json-schema.org/draft-07/schema#",
   "codebase":{
     "git_repo": "https://github.com/osmosis-labs/osmosis",
-    "recommended_version": "12.1.0",
-    "compatible_versions": ["12.1.0"],
+    "recommended_version": "12.2.0",
+    "compatible_versions": [
+      "12.2.0"
+    ],
     "binaries": {
       "linux/amd64": "https://github.com/osmosis-labs/osmosis/releases/download/v12.1.0/osmosisd-12.1.0-linux-amd64?checksum=sha256:44433f93946338b8cb167d9030ebbcfe924294d95d745026ada5dbe8f50d5010",
       "linux/arm64": "https://github.com/osmosis-labs/osmosis/releases/download/v12.1.0/osmosisd-12.1.0-linux-arm64?checksum=sha256:ef2c3d60156be5481534ecb33f9d94d73afa38a1b016e7e1c6d3fe10e3e69b3a"
@@ -12,8 +14,12 @@
     "tendermint_version": "0.34",
     "cosmwasm_version": "0.28",
     "cosmwasm_enabled": true,
+    "ibc_go_version": "3.3.0",
+    "ics_enabled": [
+      "ics20-1"
+    ],
     "genesis": {
-      "name": "v3.1.0",
+      "name": "v3",
       "genesis_url": "https://github.com/osmosis-labs/networks/raw/main/osmosis-1/genesis.json"
     },
     "versions": [
diff --git a/go.mod b/go.mod
index 12162fb08..4a45a7406 100644
--- a/go.mod
+++ b/go.mod
@@ -12,7 +12,7 @@ require (
 	github.com/gogo/protobuf v1.3.3
 	github.com/golang/mock v1.6.0
 	github.com/golang/protobuf v1.5.2
-	github.com/golangci/golangci-lint v1.50.0
+	github.com/golangci/golangci-lint v1.50.1
 	github.com/gorilla/mux v1.8.0
 	github.com/grpc-ecosystem/grpc-gateway v1.16.0
 	github.com/mattn/go-sqlite3 v1.14.15
@@ -24,13 +24,13 @@ require (
 	github.com/spf13/cobra v1.6.0
 	github.com/spf13/pflag v1.0.5
 	github.com/spf13/viper v1.13.0
-	github.com/stretchr/testify v1.8.0
-	github.com/tendermint/tendermint v0.34.21
+	github.com/stretchr/testify v1.8.1
+	github.com/tendermint/tendermint v0.34.22
 	github.com/tendermint/tm-db v0.6.8-0.20220506192307-f628bb5dc95b
 	go.uber.org/multierr v1.8.0
 	golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e
 	google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b
-	google.golang.org/grpc v1.49.0
+	google.golang.org/grpc v1.50.0
 	gopkg.in/yaml.v2 v2.4.0
 	mvdan.cc/gofumpt v0.4.0
 )
@@ -43,7 +43,7 @@ require (
 	github.com/curioswitch/go-reassign v0.2.0 // indirect
 	github.com/felixge/httpsnoop v1.0.1 // indirect
 	github.com/google/btree v1.0.0 // indirect
-	github.com/kkHAIKE/contextcheck v1.1.2 // indirect
+	github.com/kkHAIKE/contextcheck v1.1.3 // indirect
 	github.com/maratori/testableexamples v1.0.0 // indirect
 	github.com/regen-network/cosmos-proto v0.3.1 // indirect
 	github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
@@ -51,8 +51,8 @@ require (
 	github.com/sivchari/nosnakecase v1.7.0 // indirect
 	github.com/timonwong/loggercheck v0.9.3 // indirect
 	github.com/zimmski/go-mutesting v0.0.0-20210610104036-6d9217011a00 // indirect
-	go.uber.org/atomic v1.9.0 // indirect
-	go.uber.org/zap v1.21.0 // indirect
+	go.uber.org/atomic v1.10.0 // indirect
+	go.uber.org/zap v1.22.0 // indirect
 )
 
 require (
@@ -63,7 +63,7 @@ require (
 	github.com/Antonboom/errname v0.1.7 // indirect
 	github.com/Antonboom/nilnil v0.1.1 // indirect
 	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
-	github.com/BurntSushi/toml v1.2.0 // indirect
+	github.com/BurntSushi/toml v1.2.1 // indirect
 	github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect
 	github.com/CosmWasm/wasmvm v1.0.0
 	github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 // indirect
@@ -97,7 +97,7 @@ require (
 	github.com/cosmos/gorocksdb v1.2.0 // indirect
 	github.com/cosmos/ledger-cosmos-go v0.11.1 // indirect
 	github.com/cosmos/ledger-go v0.9.3 // indirect
-	github.com/daixiang0/gci v0.8.0 // indirect
+	github.com/daixiang0/gci v0.8.1 // indirect
 	github.com/danieljoos/wincred v1.1.2 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/denis-tingaikin/go-header v0.4.3 // indirect
@@ -199,11 +199,11 @@ require (
 	github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
 	github.com/mbilski/exhaustivestruct v1.2.0 // indirect
 	github.com/mgechev/revive v1.2.4 // indirect
-	github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect
+	github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect
 	github.com/minio/highwayhash v1.0.2 // indirect
 	github.com/mitchellh/go-homedir v1.1.0 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
-	github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
+	github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect
 	github.com/moricho/tparallel v0.2.1 // indirect
 	github.com/mtibben/percent v0.2.1 // indirect
 	github.com/nakabonne/nestif v0.3.1 // indirect
@@ -228,14 +228,14 @@ require (
 	github.com/quasilyte/gogrep v0.0.0-20220828223005-86e4605de09f // indirect
 	github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 // indirect
 	github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
-	github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
+	github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
 	github.com/rivo/uniseg v0.2.0 // indirect
 	github.com/rs/cors v1.8.2 // indirect
 	github.com/rs/zerolog v1.27.0 // indirect
 	github.com/ryancurrah/gomodguard v1.2.4 // indirect
 	github.com/ryanrolds/sqlclosecheck v0.3.0 // indirect
 	github.com/sanposhiho/wastedassign/v2 v2.0.6 // indirect
-	github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa // indirect
+	github.com/sasha-s/go-deadlock v0.3.1 // indirect
 	github.com/securego/gosec/v2 v2.13.1 // indirect
 	github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect
 	github.com/sirupsen/logrus v1.9.0 // indirect
@@ -247,7 +247,7 @@ require (
 	github.com/spf13/jwalterweatherman v1.1.0 // indirect
 	github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
 	github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect
-	github.com/stretchr/objx v0.4.0 // indirect
+	github.com/stretchr/objx v0.5.0 // indirect
 	github.com/subosito/gotenv v1.4.1 // indirect
 	github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca
 	github.com/tdakkota/asciicheck v0.1.1 // indirect
@@ -256,8 +256,8 @@ require (
 	github.com/tendermint/go-amino v0.16.0 // indirect
 	github.com/tetafro/godot v1.4.11 // indirect
 	github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144 // indirect
-	github.com/tomarrell/wrapcheck/v2 v2.6.2 // indirect
-	github.com/tommy-muehle/go-mnd/v2 v2.5.0 // indirect
+	github.com/tomarrell/wrapcheck/v2 v2.7.0 // indirect
+	github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
 	github.com/ultraware/funlen v0.0.3 // indirect
 	github.com/ultraware/whitespace v0.0.5 // indirect
 	github.com/uudashr/gocognit v1.0.6 // indirect
@@ -272,15 +272,15 @@ require (
 	gitlab.com/bosi/decorder v0.2.3 // indirect
 	go.etcd.io/bbolt v1.3.6 // indirect
 	go.opencensus.io v0.23.0 // indirect
-	golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
+	golang.org/x/crypto v0.1.0 // indirect
 	golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91 // indirect
-	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
-	golang.org/x/net v0.0.0-20220726230323-06994584191e // indirect
+	golang.org/x/mod v0.6.0 // indirect
+	golang.org/x/net v0.1.0 // indirect
 	golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect
-	golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41 // indirect
-	golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect
-	golang.org/x/text v0.3.7 // indirect
-	golang.org/x/tools v0.1.12 // indirect
+	golang.org/x/sys v0.1.0 // indirect
+	golang.org/x/term v0.1.0 // indirect
+	golang.org/x/text v0.4.0 // indirect
+	golang.org/x/tools v0.2.0 // indirect
 	google.golang.org/protobuf v1.28.1
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
diff --git a/go.sum b/go.sum
index 8c21314c7..11450d968 100644
--- a/go.sum
+++ b/go.sum
@@ -67,8 +67,8 @@ github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN
 github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
 github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
-github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
+github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg=
 github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4=
@@ -138,7 +138,6 @@ github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi
 github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
 github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
-github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -265,8 +264,8 @@ github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDU
 github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc=
 github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
 github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
-github.com/daixiang0/gci v0.8.0 h1:DzWYUm4+bc+taVUtuq1tsIMb/QFMMYgDIiykSoO98ZU=
-github.com/daixiang0/gci v0.8.0/go.mod h1:EpVfrztufwVgQRXjnX4zuNinEpLj5OmMjtu/+MB0V0c=
+github.com/daixiang0/gci v0.8.1 h1:T4xpSC+hmsi4CSyuYfIJdMZAr9o7xZmHpQVygMghGZ4=
+github.com/daixiang0/gci v0.8.1/go.mod h1:EpVfrztufwVgQRXjnX4zuNinEpLj5OmMjtu/+MB0V0c=
 github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
 github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
 github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -473,8 +472,8 @@ github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe h1:6RGUuS7EGotKx6
 github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ=
 github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 h1:amWTbTGqOZ71ruzrdA+Nx5WA3tV1N0goTspwmKCQvBY=
 github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2/go.mod h1:9wOXstvyDRshQ9LggQuzBCGysxs3b6Uo/1MvYCR2NMs=
-github.com/golangci/golangci-lint v1.50.0 h1:+Xmyt8rKLauNLp2gzcxKMN8VNGqGc5Avc2ZLTwIOpEA=
-github.com/golangci/golangci-lint v1.50.0/go.mod h1:UqtDvK24R9OizqRF06foPX8opRMzQB0HQK90uI2JgKc=
+github.com/golangci/golangci-lint v1.50.1 h1:C829clMcZXEORakZlwpk7M4iDw2XiwxxKaG504SZ9zY=
+github.com/golangci/golangci-lint v1.50.1/go.mod h1:AQjHBopYS//oB8xs0y0M/dtxdKHkdhl0RvmjUct0/4w=
 github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA=
 github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
 github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA=
@@ -501,7 +500,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -685,8 +683,8 @@ github.com/kisielk/errcheck v1.6.2 h1:uGQ9xI8/pgc9iOoCe7kWQgRE6SBTrCGmTSf0LrEtY7
 github.com/kisielk/errcheck v1.6.2/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw=
 github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/kkHAIKE/contextcheck v1.1.2 h1:BYUSG/GhMhqVz//yjl8IkBDlMEws+9DtCmkz18QO1gg=
-github.com/kkHAIKE/contextcheck v1.1.2/go.mod h1:PG/cwd6c0705/LM0KTr1acO2gORUxkSVWyLJOFW5qoo=
+github.com/kkHAIKE/contextcheck v1.1.3 h1:l4pNvrb8JSwRd51ojtcOxOeHJzHek+MtOyXbaR0uvmw=
+github.com/kkHAIKE/contextcheck v1.1.3/go.mod h1:PG/cwd6c0705/LM0KTr1acO2gORUxkSVWyLJOFW5qoo=
 github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
 github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
@@ -778,8 +776,9 @@ github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aks
 github.com/mgechev/revive v1.2.4 h1:+2Hd/S8oO2H0Ikq2+egtNwQsVhAeELHjxjIUFX5ajLI=
 github.com/mgechev/revive v1.2.4/go.mod h1:iAWlQishqCuj4yhV24FTnKSXGpbAA+0SckXB8GQMX/Q=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0=
 github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
+github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94=
+github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
 github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
 github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
 github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
@@ -797,8 +796,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
 github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
 github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
-github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
-github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
+github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI=
+github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -991,8 +990,9 @@ github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8
 github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
 github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
-github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
 github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
+github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 github.com/regen-network/cosmos-proto v0.3.1 h1:rV7iM4SSFAagvy8RiyhiACbWEGotmqzywPxOvwMdxcg=
 github.com/regen-network/cosmos-proto v0.3.1/go.mod h1:jO0sVX6a1B36nmE8C9xBFXpNwWejXC7QqCOnH3O0+YM=
 github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4=
@@ -1024,8 +1024,9 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
 github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
 github.com/sanposhiho/wastedassign/v2 v2.0.6 h1:+6/hQIHKNJAUixEj6EmOngGIisyeI+T3335lYTyxRoA=
 github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI=
-github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa h1:0U2s5loxrTy6/VgfVoLuVLFJcURKLH49ie0zSch7gh4=
 github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
+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/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw=
 github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
 github.com/sashamelentyev/usestdlibvars v1.20.0 h1:K6CXjqqtSYSsuyRDDC7Sjn6vTMLiSJa4ZmDkiokoqtw=
@@ -1103,8 +1104,9 @@ github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3
 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -1113,8 +1115,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
 github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
@@ -1131,8 +1134,8 @@ github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM
 github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E=
 github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
 github.com/tendermint/tendermint v0.34.14/go.mod h1:FrwVm3TvsVicI9Z7FlucHV6Znfd5KBc/Lpp69cCwtk0=
-github.com/tendermint/tendermint v0.34.21 h1:UiGGnBFHVrZhoQVQ7EfwSOLuCtarqCSsRf8VrklqB7s=
-github.com/tendermint/tendermint v0.34.21/go.mod h1:XDvfg6U7grcFTDx7VkzxnhazQ/bspGJAn4DZ6DcLLjQ=
+github.com/tendermint/tendermint v0.34.22 h1:XMhtC8s8QqJO4l/dn+TkQvevTRSow3Vixjclr41o+2Q=
+github.com/tendermint/tendermint v0.34.22/go.mod h1:YpP5vBEAKUT4g6oyfjKgFeZmdB/GjkJAxfF+cgmJg6Y=
 github.com/tendermint/tm-db v0.6.4/go.mod h1:dptYhIpJ2M5kUuenLr+Yyf3zQOv1SgBZcl8/BmWlMBw=
 github.com/tendermint/tm-db v0.6.8-0.20220506192307-f628bb5dc95b h1:Y3ZPG6gdDCAV2sdGkD759ji/09GzaNu1X3qKTmZIbTo=
 github.com/tendermint/tm-db v0.6.8-0.20220506192307-f628bb5dc95b/go.mod h1:ADqbS9NOSnBRK9R2RtYC61CdsHmVMD/yXAzcMuPexbU=
@@ -1153,10 +1156,10 @@ github.com/timonwong/loggercheck v0.9.3/go.mod h1:wUqnk9yAOIKtGA39l1KLE9Iz0QiToc
 github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/tomarrell/wrapcheck/v2 v2.6.2 h1:3dI6YNcrJTQ/CJQ6M/DUkc0gnqYSIk6o0rChn9E/D0M=
-github.com/tomarrell/wrapcheck/v2 v2.6.2/go.mod h1:ao7l5p0aOlUNJKI0qVwB4Yjlqutd0IvAB9Rdwyilxvg=
-github.com/tommy-muehle/go-mnd/v2 v2.5.0 h1:iAj0a8e6+dXSL7Liq0aXPox36FiN1dBbjA6lt9fl65s=
-github.com/tommy-muehle/go-mnd/v2 v2.5.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
+github.com/tomarrell/wrapcheck/v2 v2.7.0 h1:J/F8DbSKJC83bAvC6FoZaRjZiZ/iKoueSdrEkmGeacA=
+github.com/tomarrell/wrapcheck/v2 v2.7.0/go.mod h1:ao7l5p0aOlUNJKI0qVwB4Yjlqutd0IvAB9Rdwyilxvg=
+github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw=
+github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
 github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q=
 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
 github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
@@ -1230,20 +1233,18 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
-go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
+go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
 go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
-go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
-go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
 go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
 go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
 go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
-go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
+go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0=
+go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U=
 golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -1265,8 +1266,8 @@ golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWP
 golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
-golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
+golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1312,8 +1313,9 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
 golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
+golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
 golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1368,8 +1370,8 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220726230323-06994584191e h1:wOQNKh1uuDGRnmgF0jDxh7ctgGy/3P4rYWQRVJD4/Yg=
-golang.org/x/net v0.0.0-20220726230323-06994584191e/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM=
+golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
+golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1484,13 +1486,13 @@ golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41 h1:ohgcoMbSofXygzo6AD2I1kz3BFmW1QArPYTtwEM3UXc=
-golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
-golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
+golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1499,8 +1501,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1594,8 +1597,9 @@ golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da/go.mod h1:nABZi5QlRsZVlz
 golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
 golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
 golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
-golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
+golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/osmomath/decimal.go b/osmomath/decimal.go
index 265f6ada0..381729988 100644
--- a/osmomath/decimal.go
+++ b/osmomath/decimal.go
@@ -30,6 +30,9 @@ const (
 
 	// max number of iterations in ApproxRoot function
 	maxApproxRootIterations = 100
+
+	// max number of iterations in Log2 function
+	maxLog2Iterations = 300
 )
 
 var (
@@ -39,6 +42,10 @@ var (
 	zeroInt              = big.NewInt(0)
 	oneInt               = big.NewInt(1)
 	tenInt               = big.NewInt(10)
+
+	// initialized in init() since requires
+	// precision to be defined.
+	twoBigDec BigDec
 )
 
 // Decimal errors
@@ -54,6 +61,8 @@ func init() {
 	for i := 0; i <= Precision; i++ {
 		precisionMultipliers[i] = calcPrecisionMultiplier(int64(i))
 	}
+
+	twoBigDec = NewBigDec(2)
 }
 
 func precisionInt() *big.Int {
@@ -211,7 +220,8 @@ func (d BigDec) GTE(d2 BigDec) bool   { return (d.i).Cmp(d2.i) >= 0 }          /
 func (d BigDec) LT(d2 BigDec) bool    { return (d.i).Cmp(d2.i) < 0 }           // less than
 func (d BigDec) LTE(d2 BigDec) bool   { return (d.i).Cmp(d2.i) <= 0 }          // less than or equal
 func (d BigDec) Neg() BigDec          { return BigDec{new(big.Int).Neg(d.i)} } // reverse the decimal sign
-func (d BigDec) Abs() BigDec          { return BigDec{new(big.Int).Abs(d.i)} } // absolute value
+// nolint: stylecheck
+func (d BigDec) Abs() BigDec { return BigDec{new(big.Int).Abs(d.i)} } // absolute value
 
 // BigInt returns a copy of the underlying big.Int.
 func (d BigDec) BigInt() *big.Int {
@@ -856,3 +866,54 @@ func DecApproxEq(t *testing.T, d1 BigDec, d2 BigDec, tol BigDec) (*testing.T, bo
 	diff := d1.Sub(d2).Abs()
 	return t, diff.LTE(tol), "expected |d1 - d2| <:\t%v\ngot |d1 - d2| = \t\t%v", tol.String(), diff.String()
 }
+
+// LogBase2 returns log_2 {x}.
+// Rounds down by truncations during division and right shifting.
+// Accurate up to 32 precision digits.
+// Implementation is based on:
+// https://stm32duinoforum.com/forum/dsp/BinaryLogarithm.pdf
+func (x BigDec) LogBase2() BigDec {
+	// create a new decimal to avoid mutating
+	// the receiver's int buffer.
+	xCopy := ZeroDec()
+	xCopy.i = new(big.Int).Set(x.i)
+	if xCopy.LTE(ZeroDec()) {
+		panic(fmt.Sprintf("log is not defined at <= 0, given (%s)", xCopy))
+	}
+
+	// Normalize x to be 1 <= x < 2.
+
+	// y is the exponent that results in a whole multiple of 2.
+	y := ZeroDec()
+
+	// repeat until: x >= 1.
+	for xCopy.LT(OneDec()) {
+		xCopy.i.Lsh(xCopy.i, 1)
+		y = y.Sub(OneDec())
+	}
+
+	// repeat until: x < 2.
+	for xCopy.GTE(twoBigDec) {
+		xCopy.i.Rsh(xCopy.i, 1)
+		y = y.Add(OneDec())
+	}
+
+	b := OneDec().Quo(twoBigDec)
+
+	// N.B. At this point x is a positive real number representing
+	// mantissa of the log. We estimate it using the following
+	// algorithm:
+	// https://stm32duinoforum.com/forum/dsp/BinaryLogarithm.pdf
+	// This has shown precision of 32 digits relative
+	// to Wolfram Alpha in tests.
+	for i := 0; i < maxLog2Iterations; i++ {
+		xCopy = xCopy.Mul(xCopy)
+		if xCopy.GTE(twoBigDec) {
+			xCopy.i.Rsh(xCopy.i, 1)
+			y = y.Add(b)
+		}
+		b.i.Rsh(b.i, 1)
+	}
+
+	return y
+}
diff --git a/x/gamm/keeper/invariants.go b/x/gamm/keeper/invariants.go
index c7e966b4f..9843623bf 100644
--- a/x/gamm/keeper/invariants.go
+++ b/x/gamm/keeper/invariants.go
@@ -12,7 +12,7 @@ import (
 
 const poolBalanceInvariantName = "pool-account-balance-equals-expected"
 
-// RegisterInvariants registers all governance invariants.
+// RegisterInvariants registers all gamm invariants.
 func RegisterInvariants(ir sdk.InvariantRegistry, keeper Keeper, bk types.BankKeeper) {
 	ir.RegisterRoute(types.ModuleName, poolBalanceInvariantName, PoolAccountInvariant(keeper, bk))
 }
diff --git a/x/gamm/keeper/pool.go b/x/gamm/keeper/pool.go
index 98cd12367..53a55119d 100644
--- a/x/gamm/keeper/pool.go
+++ b/x/gamm/keeper/pool.go
@@ -22,8 +22,8 @@ func (k Keeper) UnmarshalPool(bz []byte) (types.PoolI, error) {
 	return acc, k.cdc.UnmarshalInterface(bz, &acc)
 }
 
-// GetPoolAndPoke returns a PoolI based on it's identifier if one exists. Prior
-// to returning the pool, the weights of the pool are updated via PokePool.
+// GetPoolAndPoke returns a PoolI based on it's identifier if one exists. If poolId corresponds
+// to a pool with weights (e.g. balancer), the weights of the pool are updated via PokePool prior to returning.
 // TODO: Consider rename to GetPool due to downstream API confusion.
 func (k Keeper) GetPoolAndPoke(ctx sdk.Context, poolId uint64) (types.PoolI, error) {
 	store := ctx.KVStore(k.storeKey)
@@ -39,7 +39,9 @@ func (k Keeper) GetPoolAndPoke(ctx sdk.Context, poolId uint64) (types.PoolI, err
 		return nil, err
 	}
 
-	pool.PokePool(ctx.BlockTime())
+	if pokePool, ok := pool.(types.WeightedPoolExtension); ok {
+		pokePool.PokePool(ctx.BlockTime())
+	}
 
 	return pool, nil
 }
@@ -74,7 +76,9 @@ func (k Keeper) GetPoolsAndPoke(ctx sdk.Context) (res []types.PoolI, err error)
 			return nil, err
 		}
 
-		pool.PokePool(ctx.BlockTime())
+		if pokePool, ok := pool.(types.WeightedPoolExtension); ok {
+			pokePool.PokePool(ctx.BlockTime())
+		}
 		res = append(res, pool)
 	}
 
diff --git a/x/gamm/keeper/swap.go b/x/gamm/keeper/swap.go
index 3ef7a466a..f048784c2 100644
--- a/x/gamm/keeper/swap.go
+++ b/x/gamm/keeper/swap.go
@@ -92,7 +92,7 @@ func (k Keeper) SwapExactAmountOut(
 	return k.swapExactAmountOut(ctx, sender, pool, tokenInDenom, tokenInMaxAmount, tokenOut, swapFee)
 }
 
-// swapExactAmountIn is an internal method for swapping to get an exact number of tokens out of a pool,
+// swapExactAmountOut is an internal method for swapping to get an exact number of tokens out of a pool,
 // using the provided swapFee.
 // This is intended to allow different swap fees as determined by multi-hops,
 // or when recovering from chain liveness failures.
diff --git a/x/gamm/module.go b/x/gamm/module.go
index 907db5b90..efe35309d 100644
--- a/x/gamm/module.go
+++ b/x/gamm/module.go
@@ -29,6 +29,7 @@ import (
 	"github.com/osmosis-labs/osmosis/v12/x/gamm/client/cli"
 	"github.com/osmosis-labs/osmosis/v12/x/gamm/keeper"
 	"github.com/osmosis-labs/osmosis/v12/x/gamm/pool-models/balancer"
+	"github.com/osmosis-labs/osmosis/v12/x/gamm/pool-models/stableswap"
 	simulation "github.com/osmosis-labs/osmosis/v12/x/gamm/simulation"
 	"github.com/osmosis-labs/osmosis/v12/x/gamm/types"
 )
@@ -88,7 +89,7 @@ func (b AppModuleBasic) GetQueryCmd() *cobra.Command {
 func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) {
 	types.RegisterInterfaces(registry)
 	balancer.RegisterInterfaces(registry)
-	// stableswap.RegisterInterfaces(registry)
+	stableswap.RegisterInterfaces(registry)
 }
 
 type AppModule struct {
diff --git a/x/gamm/pool-models/balancer/pool.go b/x/gamm/pool-models/balancer/pool.go
index 22a07f7d1..ddbbecf00 100644
--- a/x/gamm/pool-models/balancer/pool.go
+++ b/x/gamm/pool-models/balancer/pool.go
@@ -29,6 +29,7 @@ const (
 var (
 	_ types.PoolI                  = &Pool{}
 	_ types.PoolAmountOutExtension = &Pool{}
+	_ types.WeightedPoolExtension  = &Pool{}
 )
 
 // NewPool returns a weighted CPMM pool with the provided parameters, and initial assets.
diff --git a/x/gamm/pool-models/stableswap/README.md b/x/gamm/pool-models/stableswap/README.md
index 1f377f34b..30cfca5ea 100644
--- a/x/gamm/pool-models/stableswap/README.md
+++ b/x/gamm/pool-models/stableswap/README.md
@@ -146,7 +146,7 @@ def iterative_search(x_f, y_0, w, k, err_tolerance):
     # k_0 < k. Need to find an upperbound. Worst case assume a linear relationship, gives an upperbound
     # TODO: In the future, we can derive better bounds via reasoning about coefficients in the cubic
     # These are quite close when we are in the "stable" part of the curve though.
-    upperbound = ceil(y_0 * k_ratio)
+    upperbound = ceil(y_0 / k_ratio)
   elif k_ratio > 1:
     # need to find a lowerbound. We could use a cubic relation, but for now we just set it to 0.
     lowerbound = 0
diff --git a/x/gamm/pool-models/stableswap/amm.go b/x/gamm/pool-models/stableswap/amm.go
index 4619cc427..25a35cdb2 100644
--- a/x/gamm/pool-models/stableswap/amm.go
+++ b/x/gamm/pool-models/stableswap/amm.go
@@ -64,9 +64,6 @@ func cfmmConstantMulti(xReserve, yReserve, u, v osmomath.BigDec) osmomath.BigDec
 // and the following expression for `a` in multi-asset pools:
 // xyz(x^2 + y^2 + w) = (x - a)(y + b)z((x - a)^2 + (y + b)^2 + w)
 func solveCfmm(xReserve, yReserve osmomath.BigDec, remReserves []osmomath.BigDec, yIn osmomath.BigDec) osmomath.BigDec {
-	if len(remReserves) == 0 {
-		return solveCFMMBinarySearch(cfmmConstant)(xReserve, yReserve, yIn)
-	}
 	wSumSquares := osmomath.ZeroDec()
 	for _, assetReserve := range remReserves {
 		wSumSquares = wSumSquares.Add(assetReserve.Mul(assetReserve))
@@ -152,6 +149,84 @@ func solveCfmmDirect(xReserve, yReserve, yIn osmomath.BigDec) osmomath.BigDec {
 	return xOut
 }
 
+// multi-asset CFMM is xyu(x^2 + y^2 + w) = k
+// As described in our spec, we can ignore the u term and simply solve within the bounds of k' = k / u
+// since u remains constant throughout any independent operation this solver would be used for.
+// We want to solve for a given addition of `b` units of y into the pool,
+// how many units `a` of x do we get out.
+// Let y' = y + b
+// we solve k = (x'y')(x'^2 + y^2 + w) for x', using the following equation: https://www.wolframalpha.com/input?i2d=true&i=solve+for+y%5C%2844%29+x*y*%5C%2840%29Power%5Bx%2C2%5D+%2B+Power%5By%2C2%5D+%2B+w%5C%2841%29%3Dk
+// which we simplify to be the following: https://www.desmos.com/calculator/zx2qslqndl
+// Then we use that to derive the change in x as x_out = x' - x
+//
+// Since original reserves, y' and k are known and remain constant throughout the calculation,
+// deriving x' and then finding x_out is equivalent to finding x_out directly.
+func solveCFMMMultiDirect(xReserve, yReserve, wSumSquares, yIn osmomath.BigDec) osmomath.BigDec {
+	if !xReserve.IsPositive() || !yReserve.IsPositive() || wSumSquares.IsNegative() || !yIn.IsPositive() {
+		panic("invalid input: reserves and input must be positive")
+	} else if yIn.GTE(yReserve) {
+		panic("cannot input more than pool reserves")
+	}
+
+	// find k' using existing reserves (k' = k / v term)
+	k := cfmmConstantMultiNoV(xReserve, yReserve, wSumSquares)
+	k2 := k.Mul(k)
+
+	// find new yReserve after join
+	y_new := yReserve.Add(yIn)
+
+	// store powers to simplify calculations
+	y2 := y_new.Mul(y_new)
+	y3 := y2.Mul(y_new)
+	y4 := y3.Mul(y_new)
+
+	// We then solve for new xReserve using new yReserve and old k using a solver derived from xy(x^2 + y^2 + w) = k
+	// Full equation: x' = (sqrt(729 k^2 y^4 + 108 y^3 (w y + y^3)^3) + 27 k y^2)^(1/3) / (3 2^(1/3) y)
+	// 								- (2^(1/3) (w y + y^3))/(sqrt(729 k^2 y^4 + 108 y^3 (w y + y^3)^3) + 27 k y^2)^(1/3)
+	//
+	//
+	// To simplify, we make the following abstractions:
+	// 1. sqrt_term = sqrt(729 k^2 y^4 + 108 y^3 (w y + y^3)^3)
+	// 2. cube_root_term = (sqrt_term + 27 k y^2)^(1/3)
+	// 3. term1 = cube_root_term / (3 2^(1/3) y)
+	// 4. term2 = (2^(1/3) (w y + y^3)) / cube_root_term
+	//
+	// With these, the final equation becomes: x' = term1 - term2
+
+	// let sqrt_term = sqrt(729 k^2 y^4 + 108 y^3 (w y + y^3)^3)
+	wypy3 := (wSumSquares.Mul(y_new)).Add(y3)
+	wypy3pow3 := wypy3.Mul(wypy3).Mul(wypy3)
+
+	sqrt_term, err := ((k2.Mul(y4).MulInt64(729)).Add(y3.MulInt64(108).Mul(wypy3pow3))).ApproxRoot(2)
+	if err != nil {
+		panic(err)
+	}
+
+	// let cube_root_term = (sqrt_term + 27 k y^2)^(1/3)
+	cube_root_term, err := (sqrt_term.Add(k.Mul(y2).MulInt64(27))).ApproxRoot(3)
+	if err != nil {
+		panic(err)
+	}
+
+	// let term1 = cube_root_term / (3 2^(1/3) y)
+	term1 := cube_root_term.Quo(cubeRootTwo.MulInt64(3).Mul(y_new))
+
+	// let term2 = cube_root_term * (2^(1/3) (w y + y^3))
+	term2 := (cubeRootTwo.Mul(wypy3)).Quo(cube_root_term)
+
+	// finally, let x' = term1 - term2
+	x_new := term1.Sub(term2)
+
+	// find amount of x to output using initial and final xReserve values
+	xOut := xReserve.Sub(x_new)
+
+	if xOut.GTE(xReserve) {
+		panic("invalid output: greater than full pool reserves")
+	}
+
+	return xOut
+}
+
 func approxDecEqual(a, b, tol osmomath.BigDec) bool {
 	return (a.Sub(b).Abs()).LTE(tol)
 }
@@ -162,52 +237,31 @@ var (
 )
 
 // solveCFMMBinarySearch searches the correct dx using binary search over constant K.
-// added for future extension
-func solveCFMMBinarySearch(constantFunction func(osmomath.BigDec, osmomath.BigDec) osmomath.BigDec) func(osmomath.BigDec, osmomath.BigDec, osmomath.BigDec) osmomath.BigDec {
-	return func(xReserve, yReserve, yIn osmomath.BigDec) osmomath.BigDec {
-		if !xReserve.IsPositive() || !yReserve.IsPositive() {
-			panic("invalid input: reserves and input must be positive")
-		} else if yIn.Abs().GTE(yReserve) {
-			panic("cannot input more than pool reserves")
-		}
-		k := constantFunction(xReserve, yReserve)
-		yFinal := yReserve.Add(yIn)
-		xLowEst := osmomath.ZeroDec()
-		// we set upper bound at 2 * xReserve to accommodate negative yIns
-		xHighEst := xReserve.Mul(osmomath.NewBigDec(2))
-		maxIterations := 256
-		errTolerance := osmoutils.ErrTolerance{AdditiveTolerance: sdk.OneInt(), MultiplicativeTolerance: sdk.Dec{}}
-
-		// create single-input CFMM to pass into binary search
-		calc_x_est := func(xEst osmomath.BigDec) (osmomath.BigDec, error) {
-			return constantFunction(xEst, yFinal), nil
-		}
-
-		x_est, err := osmoutils.BinarySearchBigDec(calc_x_est, xLowEst, xHighEst, k, errTolerance, maxIterations)
-		if err != nil {
-			panic(err)
-		}
-
-		xOut := xReserve.Sub(x_est).Abs()
-		if xOut.GTE(xReserve) {
-			panic("invalid output: greater than full pool reserves")
-		}
-		return xOut
-	}
-}
-
-// solveCFMMBinarySearch searches the correct dx using binary search over constant K.
-// added for future extension
 func solveCFMMBinarySearchMulti(xReserve, yReserve, wSumSquares, yIn osmomath.BigDec) osmomath.BigDec {
-	if !xReserve.IsPositive() || !yReserve.IsPositive() || wSumSquares.IsNegative() || !yIn.IsPositive() {
+	if !xReserve.IsPositive() || !yReserve.IsPositive() || wSumSquares.IsNegative() {
 		panic("invalid input: reserves and input must be positive")
-	} else if yIn.GTE(yReserve) {
+	} else if yIn.Abs().GTE(yReserve) {
 		panic("cannot input more than pool reserves")
 	}
-	k := cfmmConstantMultiNoV(xReserve, yReserve, wSumSquares)
 	yFinal := yReserve.Add(yIn)
-	xLowEst := osmomath.ZeroDec()
-	xHighEst := xReserve
+	xLowEst, xHighEst := xReserve, xReserve
+	k0 := cfmmConstantMultiNoV(xReserve, yFinal, wSumSquares)
+	k := cfmmConstantMultiNoV(xReserve, yReserve, wSumSquares)
+	kRatio := k0.Quo(k)
+
+	if kRatio.LT(osmomath.OneDec()) {
+		// k_0 < k. Need to find an upperbound. Worst case assume a linear relationship, gives an upperbound
+		// TODO: In the future, we can derive better bounds via reasoning about coefficients in the cubic
+		// These are quite close when we are in the "stable" part of the curve though.
+		xHighEst = xReserve.Quo(kRatio).Ceil()
+	} else if kRatio.GT(osmomath.OneDec()) {
+		// need to find a lowerbound. We could use a cubic relation, but for now we just set it to 0.
+		xLowEst = osmomath.ZeroDec()
+	} else {
+		// k remains unchanged, so xOut = 0
+		return osmomath.ZeroDec()
+	}
+
 	maxIterations := 256
 	errTolerance := osmoutils.ErrTolerance{AdditiveTolerance: sdk.OneInt(), MultiplicativeTolerance: sdk.Dec{}}
 
@@ -296,6 +350,9 @@ func (p *Pool) calcInAmtGivenOut(tokenOut sdk.Coin, tokenInDenom string, swapFee
 	// 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
 	cfmmIn := solveCfmm(tokenInSupply, tokenOutSupply, remReserves, tokenOutAmount.Neg())
+	// returned cfmmIn is negative, representing we need to add this many tokens to pool.
+	// We invert that negative here.
+	cfmmIn = cfmmIn.Neg()
 	// handle swap fee
 	inAmt := cfmmIn.QuoRoundUp(oneMinus(swapFee))
 	// divide by (1 - swapfee) to force a corresponding increase in input asset
@@ -320,11 +377,14 @@ func (p *Pool) calcSingleAssetJoinShares(tokenIn sdk.Coin, swapFee sdk.Dec) (sdk
 // We can mutate pa here
 // TODO: some day switch this to a COW wrapped pa, for better perf
 func (p *Pool) joinPoolSharesInternal(ctx sdk.Context, tokensIn sdk.Coins, swapFee sdk.Dec) (numShares sdk.Int, newLiquidity sdk.Coins, err error) {
-	if len(tokensIn) == 1 {
+	if !tokensIn.DenomsSubsetOf(p.GetTotalPoolLiquidity(ctx)) {
+		return sdk.ZeroInt(), sdk.NewCoins(), errors.New("attempted joining pool with assets that do not exist in pool")
+	}
+	if len(tokensIn) == 1 && tokensIn[0].Amount.GT(sdk.OneInt()) {
 		numShares, err = p.calcSingleAssetJoinShares(tokensIn[0], swapFee)
 		newLiquidity = tokensIn
 		return numShares, newLiquidity, err
-	} else if len(tokensIn) != p.NumAssets() || !tokensIn.DenomsSubsetOf(p.GetTotalPoolLiquidity(ctx)) {
+	} else if len(tokensIn) != p.NumAssets() {
 		return sdk.ZeroInt(), sdk.NewCoins(), errors.New(
 			"stableswap pool only supports LP'ing with one asset, or all assets in pool")
 	}
@@ -336,15 +396,24 @@ func (p *Pool) joinPoolSharesInternal(ctx sdk.Context, tokensIn sdk.Coins, swapF
 	}
 	p.updatePoolForJoin(tokensIn.Sub(remCoins), numShares)
 
+	tokensJoined := tokensIn
 	for _, coin := range remCoins {
 		// TODO: Perhaps add a method to skip if this is too small.
-		newShare, err := p.calcSingleAssetJoinShares(coin, swapFee)
-		if err != nil {
-			return sdk.ZeroInt(), sdk.NewCoins(), err
+		if coin.Amount.GT(sdk.OneInt()) {
+			newShare, err := p.calcSingleAssetJoinShares(coin, swapFee)
+			if err != nil {
+				return sdk.ZeroInt(), sdk.NewCoins(), err
+			}
+			p.updatePoolForJoin(sdk.NewCoins(coin), newShare)
+			numShares = numShares.Add(newShare)
+		} else {
+			tokensJoined = tokensJoined.Sub(sdk.NewCoins(coin))
 		}
-		p.updatePoolForJoin(sdk.NewCoins(coin), newShare)
-		numShares = numShares.Add(newShare)
 	}
 
-	return numShares, tokensIn, nil
+	if err = validatePoolAssets(p.PoolLiquidity, p.ScalingFactor); err != nil {
+		return sdk.ZeroInt(), sdk.NewCoins(), err
+	}
+
+	return numShares, tokensJoined, nil
 }
diff --git a/x/gamm/pool-models/stableswap/msgs.go b/x/gamm/pool-models/stableswap/msgs.go
index 359c91fb5..bf7d13687 100644
--- a/x/gamm/pool-models/stableswap/msgs.go
+++ b/x/gamm/pool-models/stableswap/msgs.go
@@ -46,13 +46,6 @@ func (msg MsgCreateStableswapPool) ValidateBasic() error {
 		return err
 	}
 
-	// validation for pool initial liquidity
-	if len(msg.InitialPoolLiquidity) < 2 {
-		return types.ErrTooFewPoolAssets
-	} else if len(msg.InitialPoolLiquidity) > 8 {
-		return types.ErrTooManyPoolAssets
-	}
-
 	// validation for scaling factors
 	// The message's scaling factors must be empty or a valid set of scaling factors
 	if len(msg.ScalingFactors) != 0 {
@@ -61,6 +54,12 @@ func (msg MsgCreateStableswapPool) ValidateBasic() error {
 		}
 	}
 
+	// validation for pool initial liquidity
+	// The message's pool liquidity must have between 2 and 8 assets with at most 10B post-scaled units in each
+	if err = validatePoolAssets(msg.InitialPoolLiquidity, msg.ScalingFactors); err != nil {
+		return err
+	}
+
 	// validation for scaling factor owner
 	if err = validateScalingFactorController(msg.ScalingFactorController); err != nil {
 		return err
diff --git a/x/gamm/pool-models/stableswap/pool.go b/x/gamm/pool-models/stableswap/pool.go
index 46660b4d7..29418f546 100644
--- a/x/gamm/pool-models/stableswap/pool.go
+++ b/x/gamm/pool-models/stableswap/pool.go
@@ -4,7 +4,6 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"time"
 
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@@ -37,6 +36,10 @@ func NewStableswapPool(poolId uint64,
 		return Pool{}, err
 	}
 
+	if err := validatePoolAssets(initialLiquidity, scalingFactors); err != nil {
+		return Pool{}, err
+	}
+
 	pool := Pool{
 		Address:                 types.NewPoolAddress(poolId).String(),
 		Id:                      poolId,
@@ -230,6 +233,10 @@ func (p Pool) CalcOutAmtGivenIn(ctx sdk.Context, tokenIn sdk.Coins, tokenOutDeno
 }
 
 func (p *Pool) SwapOutAmtGivenIn(ctx sdk.Context, tokenIn sdk.Coins, tokenOutDenom string, swapFee sdk.Dec) (tokenOut sdk.Coin, err error) {
+	if err = validatePoolAssets(p.PoolLiquidity.Add(tokenIn...), p.ScalingFactor); err != nil {
+		return sdk.Coin{}, err
+	}
+
 	tokenOut, err = p.CalcOutAmtGivenIn(ctx, tokenIn, tokenOutDenom, swapFee)
 	if err != nil {
 		return sdk.Coin{}, err
@@ -266,6 +273,10 @@ func (p *Pool) SwapInAmtGivenOut(ctx sdk.Context, tokenOut sdk.Coins, tokenInDen
 		return sdk.Coin{}, err
 	}
 
+	if err = validatePoolAssets(p.PoolLiquidity.Add(tokenIn), p.ScalingFactor); err != nil {
+		return sdk.Coin{}, err
+	}
+
 	p.updatePoolLiquidityForSwap(sdk.NewCoins(tokenIn), tokenOut)
 
 	return tokenIn, nil
@@ -317,9 +328,6 @@ func (p Pool) CalcExitPoolCoinsFromShares(ctx sdk.Context, exitingShares sdk.Int
 	return cfmm_common.CalcExitPool(ctx, &p, exitingShares, exitFee)
 }
 
-// no-op for stableswap
-func (p *Pool) PokePool(blockTime time.Time) {}
-
 // SetStableSwapScalingFactors sets scaling factors for pool to the given amount
 // It should only be able to be successfully called by the pool's ScalingFactorGovernor
 // TODO: move commented test for this function from x/gamm/keeper/pool_service_test.go once a pool_test.go file has been created for stableswap
@@ -332,6 +340,10 @@ func (p *Pool) SetStableSwapScalingFactors(ctx sdk.Context, scalingFactors []uin
 		return err
 	}
 
+	if err := validatePoolAssets(p.PoolLiquidity, scalingFactors); err != nil {
+		return err
+	}
+
 	p.ScalingFactor = scalingFactors
 	return nil
 }
@@ -357,3 +369,19 @@ func validateScalingFactors(scalingFactors []uint64, numAssets int) error {
 
 	return nil
 }
+
+func validatePoolAssets(initialAssets sdk.Coins, scalingFactors []uint64) error {
+	if len(initialAssets) < types.MinPoolAssets {
+		return types.ErrTooFewPoolAssets
+	} else if len(initialAssets) > types.MaxPoolAssets {
+		return types.ErrTooManyPoolAssets
+	}
+
+	for i, asset := range initialAssets {
+		if asset.Amount.Quo(sdk.NewInt(int64(scalingFactors[i]))).GT(sdk.NewInt(types.StableswapMaxScaledAmtPerAsset)) {
+			return types.ErrHitMaxScaledAssets
+		}
+	}
+
+	return nil
+}
diff --git a/x/gamm/types/constants.go b/x/gamm/types/constants.go
index 76ad1efec..4152661d7 100644
--- a/x/gamm/types/constants.go
+++ b/x/gamm/types/constants.go
@@ -13,6 +13,8 @@ const (
 	// i.e. SigFigExponent = 8 is 10^8 which is 100000000. This gives 8 significant figures.
 	SigFigsExponent       = 8
 	BalancerGasFeeForSwap = 10_000
+
+	StableswapMaxScaledAmtPerAsset = 10_000_000_000
 )
 
 var (
diff --git a/x/gamm/types/errors.go b/x/gamm/types/errors.go
index 44693c63d..ce1828e59 100644
--- a/x/gamm/types/errors.go
+++ b/x/gamm/types/errors.go
@@ -20,7 +20,7 @@ var (
 	ErrPoolAlreadyExist    = sdkerrors.Register(ModuleName, 2, "pool already exist")
 	ErrPoolLocked          = sdkerrors.Register(ModuleName, 3, "pool is locked")
 	ErrTooFewPoolAssets    = sdkerrors.Register(ModuleName, 4, "pool should have at least 2 assets, as they must be swapping between at least two assets")
-	ErrTooManyPoolAssets   = sdkerrors.Register(ModuleName, 5, "pool has too many assets (currently capped at 8 assets per balancer pool and 2 per stableswap)")
+	ErrTooManyPoolAssets   = sdkerrors.Register(ModuleName, 5, "pool has too many assets (currently capped at 8 assets per pool)")
 	ErrLimitMaxAmount      = sdkerrors.Register(ModuleName, 6, "calculated amount is larger than max amount")
 	ErrLimitMinAmount      = sdkerrors.Register(ModuleName, 7, "calculated amount is lesser than min amount")
 	ErrInvalidMathApprox   = sdkerrors.Register(ModuleName, 8, "invalid calculated result")
@@ -52,4 +52,5 @@ var (
 	ErrInvalidStableswapScalingFactors = sdkerrors.Register(ModuleName, 62, "length between liquidity and scaling factors mismatch")
 	ErrNotScalingFactorGovernor        = sdkerrors.Register(ModuleName, 63, "not scaling factor governor")
 	ErrInvalidScalingFactors           = sdkerrors.Register(ModuleName, 64, "invalid scaling factor")
+	ErrHitMaxScaledAssets              = sdkerrors.Register(ModuleName, 65, "post-scaled pool assets can not exceed 10B")
 )
diff --git a/x/gamm/types/pool.go b/x/gamm/types/pool.go
index e1d5713f3..693904c65 100644
--- a/x/gamm/types/pool.go
+++ b/x/gamm/types/pool.go
@@ -79,10 +79,6 @@ type PoolI interface {
 	// CalcExitPoolCoinsFromShares returns how many coins ExitPool would return on these arguments.
 	// This does not mutate the pool, or state.
 	CalcExitPoolCoinsFromShares(ctx sdk.Context, numShares sdk.Int, exitFee sdk.Dec) (exitedCoins sdk.Coins, err error)
-
-	// PokePool determines if a pool's weights need to be updated and updates
-	// them if so.
-	PokePool(blockTime time.Time)
 }
 
 // PoolAmountOutExtension is an extension of the PoolI
@@ -126,6 +122,19 @@ type PoolAmountOutExtension interface {
 	IncreaseLiquidity(sharesOut sdk.Int, coinsIn sdk.Coins)
 }
 
+// WeightedPoolExtension is an extension of the PoolI interface
+// That defines an additional API for handling the pool's weights.
+type WeightedPoolExtension interface {
+	PoolI
+
+	// PokePool determines if a pool's weights need to be updated and updates
+	// them if so.
+	PokePool(blockTime time.Time)
+
+	// GetTokenWeight returns the weight of the specified token in the pool.
+	GetTokenWeight(denom string) (sdk.Int, error)
+}
+
 func NewPoolAddress(poolId uint64) sdk.AccAddress {
 	key := append([]byte("pool"), sdk.Uint64ToBigEndian(poolId)...)
 	return address.Module(ModuleName, key)
diff --git a/x/incentives/keeper/distribute.go b/x/incentives/keeper/distribute.go
index 90f3780e6..e3a4d06cf 100644
--- a/x/incentives/keeper/distribute.go
+++ b/x/incentives/keeper/distribute.go
@@ -386,7 +386,7 @@ func (k Keeper) checkFinishDistribution(ctx sdk.Context, gauges []types.Gauge) {
 // GetModuleToDistributeCoins returns sum of coins yet to be distributed for all of the module.
 func (k Keeper) GetModuleToDistributeCoins(ctx sdk.Context) sdk.Coins {
 	activeGaugesDistr := k.getToDistributeCoinsFromIterator(ctx, k.ActiveGaugesIterator(ctx))
-	upcomingGaugesDistr := k.getToDistributeCoinsFromIterator(ctx, k.UpcomingGaugesIteratorAfterTime(ctx, ctx.BlockTime()))
+	upcomingGaugesDistr := k.getToDistributeCoinsFromIterator(ctx, k.UpcomingGaugesIterator(ctx))
 	return activeGaugesDistr.Add(upcomingGaugesDistr...)
 }
 


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index b5ade8be7..cfbcdf553 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -7,6 +7,7 @@ on:
   push:
     branches:
       - "main"
+      - "concentrated-liquidity-main"
       - "v[0-9]**"
   workflow_dispatch:
 
diff --git a/osmomath/decimal_test.go b/osmomath/decimal_test.go
index 1650186dd..cabf1409c 100644
--- a/osmomath/decimal_test.go
+++ b/osmomath/decimal_test.go
@@ -11,6 +11,8 @@ import (
 	"github.com/stretchr/testify/require"
 	"github.com/stretchr/testify/suite"
 	"gopkg.in/yaml.v2"
+
+	"github.com/osmosis-labs/osmosis/v12/app/apptesting/osmoassert"
 )
 
 type decimalTestSuite struct {
@@ -152,8 +154,8 @@ func (s *decimalTestSuite) TestDecFloat64() {
 
 func (s *decimalTestSuite) TestSdkDec() {
 	tests := []struct {
-		d    BigDec
-		want sdk.Dec
+		d        BigDec
+		want     sdk.Dec
 		expPanic bool
 	}{
 		{NewBigDec(0), sdk.MustNewDecFromStr("0.000000000000000000"), false},
@@ -177,8 +179,8 @@ func (s *decimalTestSuite) TestSdkDec() {
 
 func (s *decimalTestSuite) TestBigDecFromSdkDec() {
 	tests := []struct {
-		d    sdk.Dec
-		want BigDec
+		d        sdk.Dec
+		want     BigDec
 		expPanic bool
 	}{
 		{sdk.MustNewDecFromStr("0.000000000000000000"), NewBigDec(0), false},
@@ -202,8 +204,8 @@ func (s *decimalTestSuite) TestBigDecFromSdkDec() {
 
 func (s *decimalTestSuite) TestBigDecFromSdkDecSlice() {
 	tests := []struct {
-		d    []sdk.Dec
-		want []BigDec
+		d        []sdk.Dec
+		want     []BigDec
 		expPanic bool
 	}{
 		{[]sdk.Dec{sdk.MustNewDecFromStr("0.000000000000000000")}, []BigDec{NewBigDec(0)}, false},
@@ -441,14 +443,14 @@ func (s *decimalTestSuite) TestDecCeil() {
 		input    BigDec
 		expected BigDec
 	}{
-		{MustNewDecFromStr("0.001"), NewBigDec(1)}, // 0.001 => 1.0
-		{MustNewDecFromStr("-0.001"), ZeroDec()},   // -0.001 => 0.0
-		{ZeroDec(), ZeroDec()}, // 0.0 => 0.0
-		{MustNewDecFromStr("0.9"), NewBigDec(1)},    // 0.9 => 1.0
+		{MustNewDecFromStr("0.001"), NewBigDec(1)},   // 0.001 => 1.0
+		{MustNewDecFromStr("-0.001"), ZeroDec()},     // -0.001 => 0.0
+		{ZeroDec(), ZeroDec()},                       // 0.0 => 0.0
+		{MustNewDecFromStr("0.9"), NewBigDec(1)},     // 0.9 => 1.0
 		{MustNewDecFromStr("4.001"), NewBigDec(5)},   // 4.001 => 5.0
 		{MustNewDecFromStr("-4.001"), NewBigDec(-4)}, // -4.001 => -4.0
-		{MustNewDecFromStr("4.7"), NewBigDec(5)},   // 4.7 => 5.0
-		{MustNewDecFromStr("-4.7"), NewBigDec(-4)}, // -4.7 => -4.0
+		{MustNewDecFromStr("4.7"), NewBigDec(5)},     // 4.7 => 5.0
+		{MustNewDecFromStr("-4.7"), NewBigDec(-4)},   // -4.7 => -4.0
 	}
 
 	for i, tc := range testCases {
@@ -463,11 +465,11 @@ func (s *decimalTestSuite) TestPower() {
 		power    uint64
 		expected BigDec
 	}{
-		{OneDec(), 10, OneDec()},                                               // 1.0 ^ (10) => 1.0
-		{NewDecWithPrec(5, 1), 2, NewDecWithPrec(25, 2)},                       // 0.5 ^ 2 => 0.25
-		{NewDecWithPrec(2, 1), 2, NewDecWithPrec(4, 2)},                        // 0.2 ^ 2 => 0.04
-		{NewDecFromInt(NewInt(3)), 3, NewDecFromInt(NewInt(27))},               // 3 ^ 3 => 27
-		{NewDecFromInt(NewInt(-3)), 4, NewDecFromInt(NewInt(81))},              // -3 ^ 4 = 81
+		{OneDec(), 10, OneDec()},                                                                   // 1.0 ^ (10) => 1.0
+		{NewDecWithPrec(5, 1), 2, NewDecWithPrec(25, 2)},                                           // 0.5 ^ 2 => 0.25
+		{NewDecWithPrec(2, 1), 2, NewDecWithPrec(4, 2)},                                            // 0.2 ^ 2 => 0.04
+		{NewDecFromInt(NewInt(3)), 3, NewDecFromInt(NewInt(27))},                                   // 3 ^ 3 => 27
+		{NewDecFromInt(NewInt(-3)), 4, NewDecFromInt(NewInt(81))},                                  // -3 ^ 4 = 81
 		{MustNewDecFromStr("1.414213562373095048801688724209698079"), 2, NewDecFromInt(NewInt(2))}, // 1.414213562373095048801688724209698079 ^ 2 = 2
 	}
 
@@ -483,14 +485,14 @@ func (s *decimalTestSuite) TestApproxRoot() {
 		root     uint64
 		expected BigDec
 	}{
-		{OneDec(), 10, OneDec()},                                                       // 1.0 ^ (0.1) => 1.0
-		{NewDecWithPrec(25, 2), 2, NewDecWithPrec(5, 1)},                               // 0.25 ^ (0.5) => 0.5
-		{NewDecWithPrec(4, 2), 2, NewDecWithPrec(2, 1)},                                // 0.04 ^ (0.5) => 0.2
-		{NewDecFromInt(NewInt(27)), 3, NewDecFromInt(NewInt(3))},                       // 27 ^ (1/3) => 3
-		{NewDecFromInt(NewInt(-81)), 4, NewDecFromInt(NewInt(-3))},                     // -81 ^ (0.25) => -3
-		{NewDecFromInt(NewInt(2)), 2, MustNewDecFromStr("1.414213562373095048801688724209698079")},         // 2 ^ (0.5) => 1.414213562373095048801688724209698079
+		{OneDec(), 10, OneDec()},                                                                         // 1.0 ^ (0.1) => 1.0
+		{NewDecWithPrec(25, 2), 2, NewDecWithPrec(5, 1)},                                                 // 0.25 ^ (0.5) => 0.5
+		{NewDecWithPrec(4, 2), 2, NewDecWithPrec(2, 1)},                                                  // 0.04 ^ (0.5) => 0.2
+		{NewDecFromInt(NewInt(27)), 3, NewDecFromInt(NewInt(3))},                                         // 27 ^ (1/3) => 3
+		{NewDecFromInt(NewInt(-81)), 4, NewDecFromInt(NewInt(-3))},                                       // -81 ^ (0.25) => -3
+		{NewDecFromInt(NewInt(2)), 2, MustNewDecFromStr("1.414213562373095048801688724209698079")},       // 2 ^ (0.5) => 1.414213562373095048801688724209698079
 		{NewDecWithPrec(1005, 3), 31536000, MustNewDecFromStr("1.000000000158153903837946258002096839")}, // 1.005 ^ (1/31536000) ≈ 1.000000000158153903837946258002096839
-		{SmallestDec(), 2, NewDecWithPrec(1, 18)},                                       // 1e-36 ^ (0.5) => 1e-18
+		{SmallestDec(), 2, NewDecWithPrec(1, 18)},                                                        // 1e-36 ^ (0.5) => 1e-18
 		{SmallestDec(), 3, MustNewDecFromStr("0.000000000001000000000000000002431786")},                  // 1e-36 ^ (1/3) => 1e-12
 		{NewDecWithPrec(1, 8), 3, MustNewDecFromStr("0.002154434690031883721759293566519280")},           // 1e-8 ^ (1/3) ≈ 0.002154434690031883721759293566519
 	}
@@ -511,11 +513,11 @@ func (s *decimalTestSuite) TestApproxSqrt() {
 		input    BigDec
 		expected BigDec
 	}{
-		{OneDec(), OneDec()},                                                // 1.0 => 1.0
-		{NewDecWithPrec(25, 2), NewDecWithPrec(5, 1)},                       // 0.25 => 0.5
-		{NewDecWithPrec(4, 2), NewDecWithPrec(2, 1)},                        // 0.09 => 0.3
-		{NewDecFromInt(NewInt(9)), NewDecFromInt(NewInt(3))},                // 9 => 3
-		{NewDecFromInt(NewInt(-9)), NewDecFromInt(NewInt(-3))},              // -9 => -3
+		{OneDec(), OneDec()},                                                                    // 1.0 => 1.0
+		{NewDecWithPrec(25, 2), NewDecWithPrec(5, 1)},                                           // 0.25 => 0.5
+		{NewDecWithPrec(4, 2), NewDecWithPrec(2, 1)},                                            // 0.09 => 0.3
+		{NewDecFromInt(NewInt(9)), NewDecFromInt(NewInt(3))},                                    // 9 => 3
+		{NewDecFromInt(NewInt(-9)), NewDecFromInt(NewInt(-3))},                                  // -9 => -3
 		{NewDecFromInt(NewInt(2)), MustNewDecFromStr("1.414213562373095048801688724209698079")}, // 2 => 1.414213562373095048801688724209698079
 	}
 
@@ -650,3 +652,90 @@ func BenchmarkMarshalTo(b *testing.B) {
 		}
 	}
 }
+
+func (s *decimalTestSuite) TestLog2() {
+	var expectedErrTolerance = MustNewDecFromStr("0.000000000000000000000000000000000100")
+
+	tests := map[string]struct {
+		initialValue BigDec
+		expected     BigDec
+
+		expectedPanic bool
+	}{
+		"log_2{-1}; invalid; panic": {
+			initialValue:  OneDec().Neg(),
+			expectedPanic: true,
+		},
+		"log_2{0}; invalid; panic": {
+			initialValue:  ZeroDec(),
+			expectedPanic: true,
+		},
+		"log_2{0.001} = -9.965784284662087043610958288468170528": {
+			initialValue: MustNewDecFromStr("0.001"),
+			// From: https://www.wolframalpha.com/input?i=log+base+2+of+0.999912345+with+33+digits
+			expected: MustNewDecFromStr("-9.965784284662087043610958288468170528"),
+		},
+		"log_2{0.56171821941421412902170941} = -0.832081497183140708984033250637831402": {
+			initialValue: MustNewDecFromStr("0.56171821941421412902170941"),
+			// From: https://www.wolframalpha.com/input?i=log+base+2+of+0.56171821941421412902170941+with+36+digits
+			expected: MustNewDecFromStr("-0.832081497183140708984033250637831402"),
+		},
+		"log_2{0.999912345} = -0.000126464976533858080645902722235833": {
+			initialValue: MustNewDecFromStr("0.999912345"),
+			// From: https://www.wolframalpha.com/input?i=log+base+2+of+0.999912345+with+37+digits
+			expected: MustNewDecFromStr("-0.000126464976533858080645902722235833"),
+		},
+		"log_2{1} = 0": {
+			initialValue: NewBigDec(1),
+			expected:     NewBigDec(0),
+		},
+		"log_2{2} = 1": {
+			initialValue: NewBigDec(2),
+			expected:     NewBigDec(1),
+		},
+		"log_2{7} = 2.807354922057604107441969317231830809": {
+			initialValue: NewBigDec(7),
+			// From: https://www.wolframalpha.com/input?i=log+base+2+of+7+37+digits
+			expected: MustNewDecFromStr("2.807354922057604107441969317231830809"),
+		},
+		"log_2{512} = 9": {
+			initialValue: NewBigDec(512),
+			expected:     NewBigDec(9),
+		},
+		"log_2{580} = 9.179909090014934468590092754117374938": {
+			initialValue: NewBigDec(580),
+			// From: https://www.wolframalpha.com/input?i=log+base+2+of+600+37+digits
+			expected: MustNewDecFromStr("9.179909090014934468590092754117374938"),
+		},
+		"log_2{1024} = 10": {
+			initialValue: NewBigDec(1024),
+			expected:     NewBigDec(10),
+		},
+		"log_2{1024.987654321} = 10.001390817654141324352719749259888355": {
+			initialValue: NewDecWithPrec(1024987654321, 9),
+			// From: https://www.wolframalpha.com/input?i=log+base+2+of+1024.987654321+38+digits
+			expected: MustNewDecFromStr("10.001390817654141324352719749259888355"),
+		},
+		"log_2{912648174127941279170121098210.92821920190204131121} = 99.525973560175362367047484597337715868": {
+			initialValue: MustNewDecFromStr("912648174127941279170121098210.92821920190204131121"),
+			// From: https://www.wolframalpha.com/input?i=log+base+2+of+912648174127941279170121098210.92821920190204131121+38+digits
+			expected: MustNewDecFromStr("99.525973560175362367047484597337715868"),
+		},
+	}
+
+	for name, tc := range tests {
+		s.Run(name, func() {
+			osmoassert.ConditionalPanic(s.T(), tc.expectedPanic, func() {
+				// Create a copy to test that the original was not modified.
+				// That is, that LogbBase2() is non-mutative.
+				initialCopy := ZeroDec()
+				initialCopy.i.Set(tc.initialValue.i)
+
+				// system under test.
+				res := tc.initialValue.LogBase2()
+				require.True(DecApproxEq(s.T(), tc.expected, res, expectedErrTolerance))
+				require.Equal(s.T(), initialCopy, tc.initialValue)
+			})
+		})
+	}
+}
diff --git a/osmomath/log2_bench_test.go b/osmomath/log2_bench_test.go
new file mode 100644
index 000000000..9b4ab3c33
--- /dev/null
+++ b/osmomath/log2_bench_test.go
@@ -0,0 +1,24 @@
+package osmomath
+
+import (
+	"math/rand"
+	"testing"
+)
+
+func BenchmarkLog2(b *testing.B) {
+	tests := []BigDec{
+		MustNewDecFromStr("1.2"),
+		MustNewDecFromStr("1.234"),
+		MustNewDecFromStr("1024"),
+		NewBigDec(2048 * 2048 * 2048 * 2048 * 2048),
+		MustNewDecFromStr("999999999999999999999999999999999999999999999999999999.9122181273612911"),
+		MustNewDecFromStr("0.563289239121902491248219047129047129"),
+	}
+
+	for i := 0; i < b.N; i++ {
+		b.StopTimer()
+		test := tests[rand.Int63n(int64(len(tests)))]
+		b.StartTimer()
+		_ = test.LogBase2()
+	}
+}
diff --git a/osmomath/rounding_direction_test.go b/osmomath/rounding_direction_test.go
index 8668c84c6..3fac4b62c 100644
--- a/osmomath/rounding_direction_test.go
+++ b/osmomath/rounding_direction_test.go
@@ -42,7 +42,6 @@ func TestDivIntByU64ToBigDec(t *testing.T) {
 	addTCForAllRoundingModes("odd divided by 2", sdk.NewInt(5), 2, NewDecWithPrec(25, 1))
 
 	for name, tt := range tests {
-		fmt.Println("start")
 		t.Run(name, func(t *testing.T) {
 			got, err := DivIntByU64ToBigDec(tt.i, tt.u, tt.round)
 			require.Equal(t, tt.want, got)
diff --git a/tests/mocks/mock_pool.go b/tests/mocks/mock_pool.go
index 57ba6b28d..961b82cf7 100644
--- a/tests/mocks/mock_pool.go
+++ b/tests/mocks/mock_pool.go
@@ -1,7 +1,7 @@
 // Code generated by MockGen. DO NOT EDIT.
 // Source: x/gamm/types/pool.go
 
-// Package mocks is a generated GoMock package.
+// Package mock_types is a generated GoMock package.
 package mocks
 
 import (
@@ -255,18 +255,6 @@ func (mr *MockPoolIMockRecorder) JoinPoolNoSwap(ctx, tokensIn, swapFee interface
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "JoinPoolNoSwap", reflect.TypeOf((*MockPoolI)(nil).JoinPoolNoSwap), ctx, tokensIn, swapFee)
 }
 
-// PokePool mocks base method.
-func (m *MockPoolI) PokePool(blockTime time.Time) {
-	m.ctrl.T.Helper()
-	m.ctrl.Call(m, "PokePool", blockTime)
-}
-
-// PokePool indicates an expected call of PokePool.
-func (mr *MockPoolIMockRecorder) PokePool(blockTime interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PokePool", reflect.TypeOf((*MockPoolI)(nil).PokePool), blockTime)
-}
-
 // ProtoMessage mocks base method.
 func (m *MockPoolI) ProtoMessage() {
 	m.ctrl.T.Helper()
@@ -650,18 +638,6 @@ func (mr *MockPoolAmountOutExtensionMockRecorder) JoinPoolTokenInMaxShareAmountO
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "JoinPoolTokenInMaxShareAmountOut", reflect.TypeOf((*MockPoolAmountOutExtension)(nil).JoinPoolTokenInMaxShareAmountOut), ctx, tokenInDenom, shareOutAmount)
 }
 
-// PokePool mocks base method.
-func (m *MockPoolAmountOutExtension) PokePool(blockTime time.Time) {
-	m.ctrl.T.Helper()
-	m.ctrl.Call(m, "PokePool", blockTime)
-}
-
-// PokePool indicates an expected call of PokePool.
-func (mr *MockPoolAmountOutExtensionMockRecorder) PokePool(blockTime interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PokePool", reflect.TypeOf((*MockPoolAmountOutExtension)(nil).PokePool), blockTime)
-}
-
 // ProtoMessage mocks base method.
 func (m *MockPoolAmountOutExtension) ProtoMessage() {
 	m.ctrl.T.Helper()
@@ -744,3 +720,341 @@ func (mr *MockPoolAmountOutExtensionMockRecorder) SwapOutAmtGivenIn(ctx, tokenIn
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SwapOutAmtGivenIn", reflect.TypeOf((*MockPoolAmountOutExtension)(nil).SwapOutAmtGivenIn), ctx, tokenIn, tokenOutDenom, swapFee)
 }
+
+// MockPokePoolExtension is a mock of PokePoolExtension interface.
+type MockPokePoolExtension struct {
+	ctrl     *gomock.Controller
+	recorder *MockPokePoolExtensionMockRecorder
+}
+
+// MockPokePoolExtensionMockRecorder is the mock recorder for MockPokePoolExtension.
+type MockPokePoolExtensionMockRecorder struct {
+	mock *MockPokePoolExtension
+}
+
+// NewMockPokePoolExtension creates a new mock instance.
+func NewMockPokePoolExtension(ctrl *gomock.Controller) *MockPokePoolExtension {
+	mock := &MockPokePoolExtension{ctrl: ctrl}
+	mock.recorder = &MockPokePoolExtensionMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockPokePoolExtension) EXPECT() *MockPokePoolExtensionMockRecorder {
+	return m.recorder
+}
+
+// CalcExitPoolCoinsFromShares mocks base method.
+func (m *MockPokePoolExtension) CalcExitPoolCoinsFromShares(ctx types.Context, numShares types.Int, exitFee types.Dec) (types.Coins, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "CalcExitPoolCoinsFromShares", ctx, numShares, exitFee)
+	ret0, _ := ret[0].(types.Coins)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// CalcExitPoolCoinsFromShares indicates an expected call of CalcExitPoolCoinsFromShares.
+func (mr *MockPokePoolExtensionMockRecorder) CalcExitPoolCoinsFromShares(ctx, numShares, exitFee interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalcExitPoolCoinsFromShares", reflect.TypeOf((*MockPokePoolExtension)(nil).CalcExitPoolCoinsFromShares), ctx, numShares, exitFee)
+}
+
+// CalcInAmtGivenOut mocks base method.
+func (m *MockPokePoolExtension) CalcInAmtGivenOut(ctx types.Context, tokenOut types.Coins, tokenInDenom string, swapFee types.Dec) (types.Coin, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "CalcInAmtGivenOut", ctx, tokenOut, tokenInDenom, swapFee)
+	ret0, _ := ret[0].(types.Coin)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// CalcInAmtGivenOut indicates an expected call of CalcInAmtGivenOut.
+func (mr *MockPokePoolExtensionMockRecorder) CalcInAmtGivenOut(ctx, tokenOut, tokenInDenom, swapFee interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalcInAmtGivenOut", reflect.TypeOf((*MockPokePoolExtension)(nil).CalcInAmtGivenOut), ctx, tokenOut, tokenInDenom, swapFee)
+}
+
+// CalcJoinPoolNoSwapShares mocks base method.
+func (m *MockPokePoolExtension) CalcJoinPoolNoSwapShares(ctx types.Context, tokensIn types.Coins, swapFee types.Dec) (types.Int, types.Coins, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "CalcJoinPoolNoSwapShares", ctx, tokensIn, swapFee)
+	ret0, _ := ret[0].(types.Int)
+	ret1, _ := ret[1].(types.Coins)
+	ret2, _ := ret[2].(error)
+	return ret0, ret1, ret2
+}
+
+// CalcJoinPoolNoSwapShares indicates an expected call of CalcJoinPoolNoSwapShares.
+func (mr *MockPokePoolExtensionMockRecorder) CalcJoinPoolNoSwapShares(ctx, tokensIn, swapFee interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalcJoinPoolNoSwapShares", reflect.TypeOf((*MockPokePoolExtension)(nil).CalcJoinPoolNoSwapShares), ctx, tokensIn, swapFee)
+}
+
+// CalcJoinPoolShares mocks base method.
+func (m *MockPokePoolExtension) CalcJoinPoolShares(ctx types.Context, tokensIn types.Coins, swapFee types.Dec) (types.Int, types.Coins, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "CalcJoinPoolShares", ctx, tokensIn, swapFee)
+	ret0, _ := ret[0].(types.Int)
+	ret1, _ := ret[1].(types.Coins)
+	ret2, _ := ret[2].(error)
+	return ret0, ret1, ret2
+}
+
+// CalcJoinPoolShares indicates an expected call of CalcJoinPoolShares.
+func (mr *MockPokePoolExtensionMockRecorder) CalcJoinPoolShares(ctx, tokensIn, swapFee interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalcJoinPoolShares", reflect.TypeOf((*MockPokePoolExtension)(nil).CalcJoinPoolShares), ctx, tokensIn, swapFee)
+}
+
+// CalcOutAmtGivenIn mocks base method.
+func (m *MockPokePoolExtension) CalcOutAmtGivenIn(ctx types.Context, tokenIn types.Coins, tokenOutDenom string, swapFee types.Dec) (types.Coin, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "CalcOutAmtGivenIn", ctx, tokenIn, tokenOutDenom, swapFee)
+	ret0, _ := ret[0].(types.Coin)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// CalcOutAmtGivenIn indicates an expected call of CalcOutAmtGivenIn.
+func (mr *MockPokePoolExtensionMockRecorder) CalcOutAmtGivenIn(ctx, tokenIn, tokenOutDenom, swapFee interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalcOutAmtGivenIn", reflect.TypeOf((*MockPokePoolExtension)(nil).CalcOutAmtGivenIn), ctx, tokenIn, tokenOutDenom, swapFee)
+}
+
+// ExitPool mocks base method.
+func (m *MockPokePoolExtension) ExitPool(ctx types.Context, numShares types.Int, exitFee types.Dec) (types.Coins, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "ExitPool", ctx, numShares, exitFee)
+	ret0, _ := ret[0].(types.Coins)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// ExitPool indicates an expected call of ExitPool.
+func (mr *MockPokePoolExtensionMockRecorder) ExitPool(ctx, numShares, exitFee interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExitPool", reflect.TypeOf((*MockPokePoolExtension)(nil).ExitPool), ctx, numShares, exitFee)
+}
+
+// GetAddress mocks base method.
+func (m *MockPokePoolExtension) GetAddress() types.AccAddress {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetAddress")
+	ret0, _ := ret[0].(types.AccAddress)
+	return ret0
+}
+
+// GetAddress indicates an expected call of GetAddress.
+func (mr *MockPokePoolExtensionMockRecorder) GetAddress() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAddress", reflect.TypeOf((*MockPokePoolExtension)(nil).GetAddress))
+}
+
+// GetExitFee mocks base method.
+func (m *MockPokePoolExtension) GetExitFee(ctx types.Context) types.Dec {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetExitFee", ctx)
+	ret0, _ := ret[0].(types.Dec)
+	return ret0
+}
+
+// GetExitFee indicates an expected call of GetExitFee.
+func (mr *MockPokePoolExtensionMockRecorder) GetExitFee(ctx interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExitFee", reflect.TypeOf((*MockPokePoolExtension)(nil).GetExitFee), ctx)
+}
+
+// GetId mocks base method.
+func (m *MockPokePoolExtension) GetId() uint64 {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetId")
+	ret0, _ := ret[0].(uint64)
+	return ret0
+}
+
+// GetId indicates an expected call of GetId.
+func (mr *MockPokePoolExtensionMockRecorder) GetId() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetId", reflect.TypeOf((*MockPokePoolExtension)(nil).GetId))
+}
+
+// GetSwapFee mocks base method.
+func (m *MockPokePoolExtension) GetSwapFee(ctx types.Context) types.Dec {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetSwapFee", ctx)
+	ret0, _ := ret[0].(types.Dec)
+	return ret0
+}
+
+// GetSwapFee indicates an expected call of GetSwapFee.
+func (mr *MockPokePoolExtensionMockRecorder) GetSwapFee(ctx interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSwapFee", reflect.TypeOf((*MockPokePoolExtension)(nil).GetSwapFee), ctx)
+}
+
+// GetTotalPoolLiquidity mocks base method.
+func (m *MockPokePoolExtension) GetTotalPoolLiquidity(ctx types.Context) types.Coins {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetTotalPoolLiquidity", ctx)
+	ret0, _ := ret[0].(types.Coins)
+	return ret0
+}
+
+// GetTotalPoolLiquidity indicates an expected call of GetTotalPoolLiquidity.
+func (mr *MockPokePoolExtensionMockRecorder) GetTotalPoolLiquidity(ctx interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTotalPoolLiquidity", reflect.TypeOf((*MockPokePoolExtension)(nil).GetTotalPoolLiquidity), ctx)
+}
+
+// GetTotalShares mocks base method.
+func (m *MockPokePoolExtension) GetTotalShares() types.Int {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetTotalShares")
+	ret0, _ := ret[0].(types.Int)
+	return ret0
+}
+
+// GetTotalShares indicates an expected call of GetTotalShares.
+func (mr *MockPokePoolExtensionMockRecorder) GetTotalShares() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTotalShares", reflect.TypeOf((*MockPokePoolExtension)(nil).GetTotalShares))
+}
+
+// IsActive mocks base method.
+func (m *MockPokePoolExtension) IsActive(ctx types.Context) bool {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "IsActive", ctx)
+	ret0, _ := ret[0].(bool)
+	return ret0
+}
+
+// IsActive indicates an expected call of IsActive.
+func (mr *MockPokePoolExtensionMockRecorder) IsActive(ctx interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsActive", reflect.TypeOf((*MockPokePoolExtension)(nil).IsActive), ctx)
+}
+
+// JoinPool mocks base method.
+func (m *MockPokePoolExtension) JoinPool(ctx types.Context, tokensIn types.Coins, swapFee types.Dec) (types.Int, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "JoinPool", ctx, tokensIn, swapFee)
+	ret0, _ := ret[0].(types.Int)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// JoinPool indicates an expected call of JoinPool.
+func (mr *MockPokePoolExtensionMockRecorder) JoinPool(ctx, tokensIn, swapFee interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "JoinPool", reflect.TypeOf((*MockPokePoolExtension)(nil).JoinPool), ctx, tokensIn, swapFee)
+}
+
+// JoinPoolNoSwap mocks base method.
+func (m *MockPokePoolExtension) JoinPoolNoSwap(ctx types.Context, tokensIn types.Coins, swapFee types.Dec) (types.Int, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "JoinPoolNoSwap", ctx, tokensIn, swapFee)
+	ret0, _ := ret[0].(types.Int)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// JoinPoolNoSwap indicates an expected call of JoinPoolNoSwap.
+func (mr *MockPokePoolExtensionMockRecorder) JoinPoolNoSwap(ctx, tokensIn, swapFee interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "JoinPoolNoSwap", reflect.TypeOf((*MockPokePoolExtension)(nil).JoinPoolNoSwap), ctx, tokensIn, swapFee)
+}
+
+// PokePool mocks base method.
+func (m *MockPokePoolExtension) PokePool(blockTime time.Time) {
+	m.ctrl.T.Helper()
+	m.ctrl.Call(m, "PokePool", blockTime)
+}
+
+// PokePool indicates an expected call of PokePool.
+func (mr *MockPokePoolExtensionMockRecorder) PokePool(blockTime interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PokePool", reflect.TypeOf((*MockPokePoolExtension)(nil).PokePool), blockTime)
+}
+
+// ProtoMessage mocks base method.
+func (m *MockPokePoolExtension) ProtoMessage() {
+	m.ctrl.T.Helper()
+	m.ctrl.Call(m, "ProtoMessage")
+}
+
+// ProtoMessage indicates an expected call of ProtoMessage.
+func (mr *MockPokePoolExtensionMockRecorder) ProtoMessage() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProtoMessage", reflect.TypeOf((*MockPokePoolExtension)(nil).ProtoMessage))
+}
+
+// Reset mocks base method.
+func (m *MockPokePoolExtension) Reset() {
+	m.ctrl.T.Helper()
+	m.ctrl.Call(m, "Reset")
+}
+
+// Reset indicates an expected call of Reset.
+func (mr *MockPokePoolExtensionMockRecorder) Reset() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reset", reflect.TypeOf((*MockPokePoolExtension)(nil).Reset))
+}
+
+// SpotPrice mocks base method.
+func (m *MockPokePoolExtension) SpotPrice(ctx types.Context, baseAssetDenom, quoteAssetDenom string) (types.Dec, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "SpotPrice", ctx, baseAssetDenom, quoteAssetDenom)
+	ret0, _ := ret[0].(types.Dec)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// SpotPrice indicates an expected call of SpotPrice.
+func (mr *MockPokePoolExtensionMockRecorder) SpotPrice(ctx, baseAssetDenom, quoteAssetDenom interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpotPrice", reflect.TypeOf((*MockPokePoolExtension)(nil).SpotPrice), ctx, baseAssetDenom, quoteAssetDenom)
+}
+
+// String mocks base method.
+func (m *MockPokePoolExtension) String() string {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "String")
+	ret0, _ := ret[0].(string)
+	return ret0
+}
+
+// String indicates an expected call of String.
+func (mr *MockPokePoolExtensionMockRecorder) String() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockPokePoolExtension)(nil).String))
+}
+
+// SwapInAmtGivenOut mocks base method.
+func (m *MockPokePoolExtension) SwapInAmtGivenOut(ctx types.Context, tokenOut types.Coins, tokenInDenom string, swapFee types.Dec) (types.Coin, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "SwapInAmtGivenOut", ctx, tokenOut, tokenInDenom, swapFee)
+	ret0, _ := ret[0].(types.Coin)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// SwapInAmtGivenOut indicates an expected call of SwapInAmtGivenOut.
+func (mr *MockPokePoolExtensionMockRecorder) SwapInAmtGivenOut(ctx, tokenOut, tokenInDenom, swapFee interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SwapInAmtGivenOut", reflect.TypeOf((*MockPokePoolExtension)(nil).SwapInAmtGivenOut), ctx, tokenOut, tokenInDenom, swapFee)
+}
+
+// SwapOutAmtGivenIn mocks base method.
+func (m *MockPokePoolExtension) SwapOutAmtGivenIn(ctx types.Context, tokenIn types.Coins, tokenOutDenom string, swapFee types.Dec) (types.Coin, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "SwapOutAmtGivenIn", ctx, tokenIn, tokenOutDenom, swapFee)
+	ret0, _ := ret[0].(types.Coin)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// SwapOutAmtGivenIn indicates an expected call of SwapOutAmtGivenIn.
+func (mr *MockPokePoolExtensionMockRecorder) SwapOutAmtGivenIn(ctx, tokenIn, tokenOutDenom, swapFee interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SwapOutAmtGivenIn", reflect.TypeOf((*MockPokePoolExtension)(nil).SwapOutAmtGivenIn), ctx, tokenIn, tokenOutDenom, swapFee)
+}
diff --git a/x/gamm/keeper/keeper_test.go b/x/gamm/keeper/keeper_test.go
index 3c2d13dd7..cf0a08a3a 100644
--- a/x/gamm/keeper/keeper_test.go
+++ b/x/gamm/keeper/keeper_test.go
@@ -10,6 +10,7 @@ import (
 	"github.com/osmosis-labs/osmosis/v12/app/apptesting"
 	"github.com/osmosis-labs/osmosis/v12/x/gamm/pool-models/balancer"
 	balancertypes "github.com/osmosis-labs/osmosis/v12/x/gamm/pool-models/balancer"
+	"github.com/osmosis-labs/osmosis/v12/x/gamm/pool-models/stableswap"
 	"github.com/osmosis-labs/osmosis/v12/x/gamm/types"
 )
 
@@ -34,9 +35,7 @@ func (suite *KeeperTestSuite) prepareCustomBalancerPool(
 	poolAssets []balancertypes.PoolAsset,
 	poolParams balancer.PoolParams,
 ) uint64 {
-	for _, acc := range suite.TestAccs {
-		suite.FundAcc(acc, balances)
-	}
+	suite.fundAllAccountsWith(balances)
 
 	poolID, err := suite.App.GAMMKeeper.CreatePool(
 		suite.Ctx,
@@ -46,3 +45,26 @@ func (suite *KeeperTestSuite) prepareCustomBalancerPool(
 
 	return poolID
 }
+
+func (suite *KeeperTestSuite) prepareCustomStableswapPool(
+	balances sdk.Coins,
+	poolParams stableswap.PoolParams,
+	initialLiquidity sdk.Coins,
+	scalingFactors []uint64,
+) uint64 {
+	suite.fundAllAccountsWith(balances)
+
+	poolID, err := suite.App.GAMMKeeper.CreatePool(
+		suite.Ctx,
+		stableswap.NewMsgCreateStableswapPool(suite.TestAccs[0], poolParams, initialLiquidity, scalingFactors, ""),
+	)
+	suite.Require().NoError(err)
+
+	return poolID
+}
+
+func (suite *KeeperTestSuite) fundAllAccountsWith(balances sdk.Coins) {
+	for _, acc := range suite.TestAccs {
+		suite.FundAcc(acc, balances)
+	}
+}
diff --git a/x/gamm/keeper/pool_test.go b/x/gamm/keeper/pool_test.go
index 95ecad3d0..3bcd9f2a3 100644
--- a/x/gamm/keeper/pool_test.go
+++ b/x/gamm/keeper/pool_test.go
@@ -1,5 +1,16 @@
 package keeper_test
 
+import (
+	"time"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/osmosis-labs/osmosis/v12/x/gamm/pool-models/balancer"
+	balancertypes "github.com/osmosis-labs/osmosis/v12/x/gamm/pool-models/balancer"
+	"github.com/osmosis-labs/osmosis/v12/x/gamm/pool-models/stableswap"
+	"github.com/osmosis-labs/osmosis/v12/x/gamm/types"
+)
+
 // import (
 // 	"math/rand"
 // 	"time"
@@ -220,3 +231,87 @@ package keeper_test
 // 			"Expected equal %s: %d, %d", amt.Denom, amt.Amount.Int64(), sdk.NewInt(1000).Int64())
 // 	}
 // }
+
+// TestGetPoolAndPoke tests that the right pools is returned from GetPoolAndPoke.
+// For the pools implementing the weighted extension, asserts that PokePool is called.
+func (suite *KeeperTestSuite) TestGetPoolAndPoke() {
+	const (
+		startTime = 1000
+		blockTime = startTime + 100
+	)
+
+	// N.B.: We make a copy because SmoothWeightChangeParams get mutated.
+	// We would like to avoid mutating global pool assets that are used in other tests.
+	defaultPoolAssetsCopy := make([]balancertypes.PoolAsset, 2)
+	copy(defaultPoolAssetsCopy, defaultPoolAssets)
+
+	startPoolWeightAssets := []balancertypes.PoolAsset{
+		{
+			Weight: defaultPoolAssets[0].Weight.Quo(sdk.NewInt(2)),
+			Token:  defaultPoolAssets[0].Token,
+		},
+		{
+			Weight: defaultPoolAssets[1].Weight.Mul(sdk.NewInt(3)),
+			Token:  defaultPoolAssets[1].Token,
+		},
+	}
+
+	tests := map[string]struct {
+		isPokePool bool
+		poolId     uint64
+	}{
+		"weighted pool - change weights": {
+			isPokePool: true,
+			poolId: suite.prepareCustomBalancerPool(defaultAcctFunds, startPoolWeightAssets, balancer.PoolParams{
+				SwapFee: defaultSwapFee,
+				ExitFee: defaultExitFee,
+				SmoothWeightChangeParams: &balancer.SmoothWeightChangeParams{
+					StartTime:          time.Unix(startTime, 0), // start time is before block time so the weights should change
+					Duration:           time.Hour,
+					InitialPoolWeights: startPoolWeightAssets,
+					TargetPoolWeights:  defaultPoolAssetsCopy,
+				},
+			}),
+		},
+		"non weighted pool": {
+			poolId: suite.prepareCustomStableswapPool(
+				defaultAcctFunds,
+				stableswap.PoolParams{
+					SwapFee: defaultSwapFee,
+					ExitFee: defaultExitFee,
+				},
+				sdk.NewCoins(sdk.NewCoin(defaultAcctFunds[0].Denom, defaultAcctFunds[0].Amount.QuoRaw(2)), sdk.NewCoin(defaultAcctFunds[1].Denom, defaultAcctFunds[1].Amount.QuoRaw(2))),
+				[]uint64{1, 1},
+			)},
+	}
+
+	for name, tc := range tests {
+		suite.Run(name, func() {
+			k := suite.App.GAMMKeeper
+			ctx := suite.Ctx.WithBlockTime(time.Unix(blockTime, 0))
+
+			pool, err := k.GetPoolAndPoke(ctx, tc.poolId)
+
+			suite.Require().NoError(err)
+			suite.Require().Equal(tc.poolId, pool.GetId())
+
+			if tc.isPokePool {
+				pokePool, ok := pool.(types.WeightedPoolExtension)
+				suite.Require().True(ok)
+
+				poolAssetWeight0, err := pokePool.GetTokenWeight(startPoolWeightAssets[0].Token.Denom)
+				suite.Require().NoError(err)
+
+				poolAssetWeight1, err := pokePool.GetTokenWeight(startPoolWeightAssets[1].Token.Denom)
+				suite.Require().NoError(err)
+
+				suite.Require().NotEqual(startPoolWeightAssets[0].Weight, poolAssetWeight0)
+				suite.Require().NotEqual(startPoolWeightAssets[1].Weight, poolAssetWeight1)
+				return
+			}
+
+			_, ok := pool.(types.WeightedPoolExtension)
+			suite.Require().False(ok)
+		})
+	}
+}
diff --git a/x/gamm/pool-models/stableswap/amm_bench_test.go b/x/gamm/pool-models/stableswap/amm_bench_test.go
index e07cf1369..4a779a792 100644
--- a/x/gamm/pool-models/stableswap/amm_bench_test.go
+++ b/x/gamm/pool-models/stableswap/amm_bench_test.go
@@ -14,12 +14,6 @@ func BenchmarkCFMM(b *testing.B) {
 	}
 }
 
-func BenchmarkBinarySearchTwoAsset(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		runCalcTwoAsset(solveCFMMBinarySearch(cfmmConstant))
-	}
-}
-
 func BenchmarkBinarySearchMultiAsset(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		runCalcMultiAsset(solveCFMMBinarySearchMulti)
diff --git a/x/gamm/pool-models/stableswap/amm_test.go b/x/gamm/pool-models/stableswap/amm_test.go
index a5c1f1ec2..a186242e0 100644
--- a/x/gamm/pool-models/stableswap/amm_test.go
+++ b/x/gamm/pool-models/stableswap/amm_test.go
@@ -224,16 +224,38 @@ var (
 			yIn:         osmomath.NewBigDec(1),
 			expectPanic: false,
 		},
-		/* TODO: increase BigDec precision (36 -> 72) to be able to accommodate this
-		"even 4-asset large pool, small input": {
+		"even 4-asset large pool (100M each), small input": {
 			xReserve: osmomath.NewBigDec(100000000),
 			yReserve: osmomath.NewBigDec(100000000),
 			// represents a 4-asset pool with 100M in each reserve
 			remReserves: []osmomath.BigDec{osmomath.NewBigDec(100000000), osmomath.NewBigDec(100000000)},
-			yIn: osmomath.NewBigDec(100),
+			yIn:         osmomath.NewBigDec(100),
+			expectPanic: false,
+		},
+		"even 4-asset pool (10B each post-scaled), small input": {
+			xReserve: osmomath.NewBigDec(10000000000),
+			yReserve: osmomath.NewBigDec(10000000000),
+			// represents a 4-asset pool with 10B in each reserve
+			remReserves: []osmomath.BigDec{osmomath.NewBigDec(10000000000), osmomath.NewBigDec(10000000000)},
+			yIn:         osmomath.NewBigDec(100000000),
+			expectPanic: false,
+		},
+		"even 10-asset pool (10B each post-scaled), small input": {
+			xReserve: osmomath.NewBigDec(10_000_000_000),
+			yReserve: osmomath.NewBigDec(10_000_000_000),
+			// represents a 10-asset pool with 10B in each reserve
+			remReserves: []osmomath.BigDec{osmomath.NewBigDec(10_000_000_000), osmomath.NewBigDec(10_000_000_000), osmomath.NewBigDec(10_000_000_000), osmomath.NewBigDec(10_000_000_000), osmomath.NewBigDec(10_000_000_000), osmomath.NewBigDec(10_000_000_000), osmomath.NewBigDec(10_000_000_000), osmomath.NewBigDec(10_000_000_000)},
+			yIn:         osmomath.NewBigDec(100),
+			expectPanic: false,
+		},
+		"even 10-asset pool (100B each post-scaled), large input": {
+			xReserve: osmomath.NewBigDec(100_000_000_000),
+			yReserve: osmomath.NewBigDec(100_000_000_000),
+			// represents a 10-asset pool with 100B in each reserve
+			remReserves: []osmomath.BigDec{osmomath.NewBigDec(100_000_000_000), osmomath.NewBigDec(100_000_000_000), osmomath.NewBigDec(100_000_000_000), osmomath.NewBigDec(100_000_000_000), osmomath.NewBigDec(100_000_000_000), osmomath.NewBigDec(100_000_000_000), osmomath.NewBigDec(100_000_000_000), osmomath.NewBigDec(100_000_000_000)},
+			yIn:         osmomath.NewBigDec(10_000_000_000),
 			expectPanic: false,
 		},
-		*/
 
 		// uneven pools
 		"uneven 3-asset pool, even swap assets as pool minority": {
@@ -406,7 +428,7 @@ func TestCFMMInvariantTwoAssets(t *testing.T) {
 	}
 }
 
-func TestCFMMInvariantTwoAssetsBinarySearch(t *testing.T) {
+func TestCFMMInvariantTwoAssetsDirect(t *testing.T) {
 	kErrTolerance := osmomath.OneDec()
 
 	tests := twoAssetCFMMTestCases
@@ -415,9 +437,9 @@ func TestCFMMInvariantTwoAssetsBinarySearch(t *testing.T) {
 		t.Run(name, func(t *testing.T) {
 			// system under test
 			sut := func() {
-				// using two-asset binary search cfmm solver
+				// using two-asset cfmm
 				k0 := cfmmConstant(test.xReserve, test.yReserve)
-				xOut := solveCFMMBinarySearch(cfmmConstant)(test.xReserve, test.yReserve, test.yIn)
+				xOut := solveCfmmDirect(test.xReserve, test.yReserve, test.yIn)
 
 				k1 := cfmmConstant(test.xReserve.Sub(xOut), test.yReserve.Add(test.yIn))
 				osmomath.DecApproxEq(t, k0, k1, kErrTolerance)
@@ -428,21 +450,23 @@ func TestCFMMInvariantTwoAssetsBinarySearch(t *testing.T) {
 	}
 }
 
-func TestCFMMInvariantTwoAssetsDirect(t *testing.T) {
+func TestCFMMInvariantMultiAssets(t *testing.T) {
 	kErrTolerance := osmomath.OneDec()
 
-	tests := twoAssetCFMMTestCases
+	tests := multiAssetCFMMTestCases
 
 	for name, test := range tests {
 		t.Run(name, func(t *testing.T) {
 			// system under test
 			sut := func() {
-				// using two-asset cfmm
-				k0 := cfmmConstant(test.xReserve, test.yReserve)
-				xOut := solveCfmmDirect(test.xReserve, test.yReserve, test.yIn)
+				uReserve := calcUReserve(test.remReserves)
+				wSumSquares := calcWSumSquares(test.remReserves)
 
-				k1 := cfmmConstant(test.xReserve.Sub(xOut), test.yReserve.Add(test.yIn))
-				osmomath.DecApproxEq(t, k0, k1, kErrTolerance)
+				// using multi-asset cfmm
+				k2 := cfmmConstantMulti(test.xReserve, test.yReserve, uReserve, wSumSquares)
+				xOut2 := solveCfmm(test.xReserve, test.yReserve, test.remReserves, test.yIn)
+				k3 := cfmmConstantMulti(test.xReserve.Sub(xOut2), test.yReserve.Add(test.yIn), uReserve, wSumSquares)
+				osmomath.DecApproxEq(t, k2, k3, kErrTolerance)
 			}
 
 			osmoassert.ConditionalPanic(t, test.expectPanic, sut)
@@ -450,7 +474,7 @@ func TestCFMMInvariantTwoAssetsDirect(t *testing.T) {
 	}
 }
 
-func TestCFMMInvariantMultiAssets(t *testing.T) {
+func TestCFMMInvariantMultiAssetsDirect(t *testing.T) {
 	kErrTolerance := osmomath.OneDec()
 
 	tests := multiAssetCFMMTestCases
@@ -464,7 +488,7 @@ func TestCFMMInvariantMultiAssets(t *testing.T) {
 
 				// using multi-asset cfmm
 				k2 := cfmmConstantMulti(test.xReserve, test.yReserve, uReserve, wSumSquares)
-				xOut2 := solveCfmm(test.xReserve, test.yReserve, test.remReserves, test.yIn)
+				xOut2 := solveCFMMMultiDirect(test.xReserve, test.yReserve, wSumSquares, test.yIn)
 				k3 := cfmmConstantMulti(test.xReserve.Sub(xOut2), test.yReserve.Add(test.yIn), uReserve, wSumSquares)
 				osmomath.DecApproxEq(t, k2, k3, kErrTolerance)
 			}
@@ -505,41 +529,153 @@ func (suite *StableSwapTestSuite) Test_StableSwap_CalculateAmountOutAndIn_Invers
 
 		denomIn       string
 		initialPoolIn int64
+
+		poolLiquidity  sdk.Coins
+		scalingFactors []uint64
 	}
 
 	// For every test case in testcases, apply a swap fee in swapFeeCases.
-	testcases := []testcase{
-		{
+	testcases := map[string]testcase{
+		// two-asset pools
+		"even pool": {
+			denomIn:        "ion",
+			denomOut:       "uosmo",
+			initialCalcOut: 100,
+
+			poolLiquidity: sdk.NewCoins(
+				sdk.NewCoin("ion", sdk.NewInt(1_000_000_000)),
+				sdk.NewCoin("uosmo", sdk.NewInt(1_000_000_000)),
+			),
+			scalingFactors: []uint64{1, 1},
+		},
+		"uneven pool (2:1)": {
+			denomIn:        "ion",
+			denomOut:       "uosmo",
+			initialCalcOut: 100,
+
+			poolLiquidity: sdk.NewCoins(
+				sdk.NewCoin("ion", sdk.NewInt(1_000_000)),
+				sdk.NewCoin("uosmo", sdk.NewInt(500_000)),
+			),
+			scalingFactors: []uint64{1, 1},
+		},
+		"uneven pool (1_000_000:1)": {
+			denomIn:        "ion",
+			denomOut:       "uosmo",
+			initialCalcOut: 100,
+
+			poolLiquidity: sdk.NewCoins(
+				sdk.NewCoin("ion", sdk.NewInt(1_000_000_000)),
+				sdk.NewCoin("uosmo", sdk.NewInt(1_000)),
+			),
+			scalingFactors: []uint64{1, 1},
+		},
+		"uneven pool (1:1_000_000)": {
+			denomIn:        "ion",
+			denomOut:       "uosmo",
+			initialCalcOut: 100,
+
+			poolLiquidity: sdk.NewCoins(
+				sdk.NewCoin("ion", sdk.NewInt(1_000)),
+				sdk.NewCoin("uosmo", sdk.NewInt(1_000_000_000)),
+			),
+			scalingFactors: []uint64{1, 1},
+		},
+		"even pool, uneven scaling factors": {
+			denomIn:        "ion",
+			denomOut:       "uosmo",
+			initialCalcOut: 100,
+
+			poolLiquidity: sdk.NewCoins(
+				sdk.NewCoin("ion", sdk.NewInt(1_000_000_000)),
+				sdk.NewCoin("uosmo", sdk.NewInt(1_000_000_000)),
+			),
+			scalingFactors: []uint64{1, 8},
+		},
+		"uneven pool, uneven scaling factors": {
+			denomIn:        "ion",
+			denomOut:       "uosmo",
+			initialCalcOut: 100,
+
+			poolLiquidity: sdk.NewCoins(
+				sdk.NewCoin("ion", sdk.NewInt(1_000_000)),
+				sdk.NewCoin("uosmo", sdk.NewInt(500_000)),
+			),
+			scalingFactors: []uint64{1, 9},
+		},
+
+		// multi asset pools
+		"even multi-asset pool": {
+			denomIn:        "ion",
+			denomOut:       "uosmo",
+			initialCalcOut: 100,
+
+			poolLiquidity: sdk.NewCoins(
+				sdk.NewCoin("ion", sdk.NewInt(1_000_000)),
+				sdk.NewCoin("uosmo", sdk.NewInt(1_000_000)),
+				sdk.NewCoin("foo", sdk.NewInt(1_000_000)),
+			),
+			scalingFactors: []uint64{1, 1, 1},
+		},
+		"uneven multi-asset pool (2:1:2)": {
+			denomIn:        "ion",
+			denomOut:       "uosmo",
+			initialCalcOut: 100,
+
+			poolLiquidity: sdk.NewCoins(
+				sdk.NewCoin("ion", sdk.NewInt(1_000_000)),
+				sdk.NewCoin("uosmo", sdk.NewInt(500_000)),
+				sdk.NewCoin("foo", sdk.NewInt(1_000_000)),
+			),
+			scalingFactors: []uint64{1, 1, 1},
+		},
+		"uneven multi-asset pool (1_000_000:1:1_000_000)": {
+			denomIn:        "ion",
 			denomOut:       "uosmo",
-			initialPoolOut: 1_000_000_000,
 			initialCalcOut: 100,
 
-			denomIn:       "ion",
-			initialPoolIn: 1_000_000_000,
+			poolLiquidity: sdk.NewCoins(
+				sdk.NewCoin("ion", sdk.NewInt(1_000_000)),
+				sdk.NewCoin("uosmo", sdk.NewInt(1_000)),
+				sdk.NewCoin("foo", sdk.NewInt(1_000_000)),
+			),
+			scalingFactors: []uint64{1, 1, 1},
 		},
-		{
+		"uneven multi-asset pool (1:1_000_000:1_000_000)": {
+			denomIn:        "ion",
 			denomOut:       "uosmo",
-			initialPoolOut: 500_000,
 			initialCalcOut: 100,
 
-			denomIn:       "ion",
-			initialPoolIn: 1_000_000,
+			poolLiquidity: sdk.NewCoins(
+				sdk.NewCoin("ion", sdk.NewInt(1_000)),
+				sdk.NewCoin("uosmo", sdk.NewInt(1_000_000)),
+				sdk.NewCoin("foo", sdk.NewInt(1_000_000)),
+			),
+			scalingFactors: []uint64{1, 1, 1},
 		},
-		{
+		"even multi-asset pool, uneven scaling factors": {
+			denomIn:        "ion",
 			denomOut:       "uosmo",
-			initialPoolOut: 1_000,
 			initialCalcOut: 100,
 
-			denomIn:       "ion",
-			initialPoolIn: 1_000_000_000,
+			poolLiquidity: sdk.NewCoins(
+				sdk.NewCoin("ion", sdk.NewInt(1_000_000)),
+				sdk.NewCoin("uosmo", sdk.NewInt(1_000_000)),
+				sdk.NewCoin("foo", sdk.NewInt(1_000_000)),
+			),
+			scalingFactors: []uint64{5, 3, 9},
 		},
-		{
+		"uneven multi-asset pool (2:1:2), uneven scaling factors": {
+			denomIn:        "ion",
 			denomOut:       "uosmo",
-			initialPoolOut: 1_000_000_000,
 			initialCalcOut: 100,
 
-			denomIn:       "ion",
-			initialPoolIn: 1_000,
+			poolLiquidity: sdk.NewCoins(
+				sdk.NewCoin("ion", sdk.NewInt(1_000_000)),
+				sdk.NewCoin("uosmo", sdk.NewInt(500_000)),
+				sdk.NewCoin("foo", sdk.NewInt(1_000_000)),
+			),
+			scalingFactors: []uint64{100, 76, 33},
 		},
 	}
 
@@ -559,9 +695,8 @@ func (suite *StableSwapTestSuite) Test_StableSwap_CalculateAmountOutAndIn_Invers
 			suite.Run(getTestCaseName(tc, swapFee), func() {
 				ctx := suite.CreateTestContext()
 
-				poolLiquidityIn := sdk.NewInt64Coin(tc.denomOut, tc.initialPoolOut)
-				poolLiquidityOut := sdk.NewInt64Coin(tc.denomIn, tc.initialPoolIn)
-				poolLiquidity := sdk.NewCoins(poolLiquidityIn, poolLiquidityOut)
+				poolLiquidityIn := sdk.NewInt64Coin(tc.denomIn, tc.initialPoolIn)
+				poolLiquidityOut := sdk.NewInt64Coin(tc.denomOut, tc.initialPoolOut)
 
 				swapFeeDec, err := sdk.NewDecFromStr(swapFee)
 				suite.Require().NoError(err)
@@ -570,7 +705,7 @@ func (suite *StableSwapTestSuite) Test_StableSwap_CalculateAmountOutAndIn_Invers
 				suite.Require().NoError(err)
 
 				// TODO: add scaling factors into inverse relationship tests
-				pool := createTestPool(suite.T(), poolLiquidity, swapFeeDec, exitFeeDec, []uint64{1, 1})
+				pool := createTestPool(suite.T(), tc.poolLiquidity, swapFeeDec, exitFeeDec, tc.scalingFactors)
 				suite.Require().NotNil(pool)
 				test_helpers.TestCalculateAmountOutAndIn_InverseRelationship(suite.T(), ctx, pool, poolLiquidityIn.Denom, poolLiquidityOut.Denom, tc.initialCalcOut, swapFeeDec)
 			})
@@ -677,8 +812,6 @@ func TestCalcSingleAssetJoinShares(t *testing.T) {
 			swapFee:        sdk.MustNewDecFromStr("0.03"),
 			expectedOut:    sdk.NewInt(100 - 3),
 		},
-
-		// TODO: increase BigDec precision further to be able to accommodate 5-asset pool tests
 	}
 
 	for name, tc := range tests {
@@ -702,3 +835,117 @@ func TestCalcSingleAssetJoinShares(t *testing.T) {
 		})
 	}
 }
+
+func TestJoinPoolSharesInternal(t *testing.T) {
+	tenPercentOfTwoPoolRaw := int64(1000000000 / 10)
+	tenPercentOfTwoPoolCoins := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(int64(1000000000/10))), sdk.NewCoin("bar", sdk.NewInt(int64(1000000000/10))))
+	twoAssetPlusTenPercent := twoEvenStablePoolAssets.Add(tenPercentOfTwoPoolCoins...)
+	type testcase struct {
+		tokensIn        sdk.Coins
+		poolAssets      sdk.Coins
+		scalingFactors  []uint64
+		swapFee         sdk.Dec
+		expNumShare     sdk.Int
+		expTokensJoined sdk.Coins
+		expPoolAssets   sdk.Coins
+		expectPass      bool
+	}
+
+	tests := map[string]testcase{
+		"even two asset pool, same tokenIn ratio": {
+			tokensIn:        tenPercentOfTwoPoolCoins,
+			poolAssets:      twoEvenStablePoolAssets,
+			scalingFactors:  defaultTwoAssetScalingFactors,
+			swapFee:         sdk.ZeroDec(),
+			expNumShare:     sdk.NewIntFromUint64(10000000000000000000),
+			expTokensJoined: tenPercentOfTwoPoolCoins,
+			expPoolAssets:   twoAssetPlusTenPercent,
+			expectPass:      true,
+		},
+		"even two asset pool, different tokenIn ratio with pool": {
+			tokensIn:        sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(tenPercentOfTwoPoolRaw)), sdk.NewCoin("bar", sdk.NewInt(10+tenPercentOfTwoPoolRaw))),
+			poolAssets:      twoEvenStablePoolAssets,
+			scalingFactors:  defaultTwoAssetScalingFactors,
+			swapFee:         sdk.ZeroDec(),
+			expNumShare:     sdk.NewIntFromUint64(10000000500000000000),
+			expTokensJoined: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(tenPercentOfTwoPoolRaw)), sdk.NewCoin("bar", sdk.NewInt(10+tenPercentOfTwoPoolRaw))),
+			expPoolAssets:   twoAssetPlusTenPercent.Add(sdk.NewCoin("bar", sdk.NewInt(10))),
+			expectPass:      true,
+		},
+		"all-asset pool join attempt exceeds max scaled asset amount": {
+			tokensIn: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 1),
+				sdk.NewInt64Coin("bar", 1),
+			),
+			poolAssets: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 10_000_000_000),
+				sdk.NewInt64Coin("bar", 10_000_000_000),
+			),
+			scalingFactors:  defaultTwoAssetScalingFactors,
+			swapFee:         sdk.ZeroDec(),
+			expNumShare:     sdk.ZeroInt(),
+			expTokensJoined: sdk.Coins{},
+			expPoolAssets: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 10_000_000_000),
+				sdk.NewInt64Coin("bar", 10_000_000_000),
+			),
+			expectPass: false,
+		},
+		"single-asset pool join exceeds hits max scaled asset amount": {
+			tokensIn: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 1),
+			),
+			poolAssets: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 10_000_000_000),
+				sdk.NewInt64Coin("bar", 10_000_000_000),
+			),
+			scalingFactors:  defaultTwoAssetScalingFactors,
+			swapFee:         sdk.ZeroDec(),
+			expNumShare:     sdk.ZeroInt(),
+			expTokensJoined: sdk.Coins{},
+			expPoolAssets: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 10_000_000_000),
+				sdk.NewInt64Coin("bar", 10_000_000_000),
+			),
+			expectPass: false,
+		},
+		"all-asset pool join attempt exactly hits max scaled asset amount": {
+			tokensIn: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 1),
+				sdk.NewInt64Coin("bar", 1),
+			),
+			poolAssets: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 9_999_999_999),
+				sdk.NewInt64Coin("bar", 9_999_999_999),
+			),
+			scalingFactors: defaultTwoAssetScalingFactors,
+			swapFee:        sdk.ZeroDec(),
+			expNumShare:    sdk.NewInt(10000000000),
+			expTokensJoined: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 1),
+				sdk.NewInt64Coin("bar", 1),
+			),
+			expPoolAssets: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 10_000_000_000),
+				sdk.NewInt64Coin("bar", 10_000_000_000),
+			),
+			expectPass: true,
+		},
+	}
+
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			ctx := sdk.Context{}
+			p := poolStructFromAssets(tc.poolAssets, tc.scalingFactors)
+
+			shares, joinedLiquidity, err := p.joinPoolSharesInternal(ctx, tc.tokensIn, tc.swapFee)
+
+			if tc.expectPass {
+				require.Equal(t, tc.expNumShare, shares)
+				require.Equal(t, tc.expTokensJoined, joinedLiquidity)
+				require.Equal(t, tc.expPoolAssets, p.PoolLiquidity)
+			}
+			osmoassert.ConditionalError(t, !tc.expectPass, err)
+		})
+	}
+}
diff --git a/x/gamm/pool-models/stableswap/msgs_test.go b/x/gamm/pool-models/stableswap/msgs_test.go
index aa3fcbd8e..727e4dc3f 100644
--- a/x/gamm/pool-models/stableswap/msgs_test.go
+++ b/x/gamm/pool-models/stableswap/msgs_test.go
@@ -209,6 +209,60 @@ func TestMsgCreateStableswapPool(t *testing.T) {
 			}),
 			expectPass: true,
 		},
+		{
+			name: "max asset amounts",
+			msg: createMsg(func(msg stableswap.MsgCreateStableswapPool) stableswap.MsgCreateStableswapPool {
+				msg.InitialPoolLiquidity = sdk.Coins{
+					sdk.NewCoin("osmo", sdk.NewInt(10_000_000_000)),
+					sdk.NewCoin("atom", sdk.NewInt(10_000_000_000)),
+					sdk.NewCoin("usdt", sdk.NewInt(10_000_000_000)),
+					sdk.NewCoin("usdc", sdk.NewInt(10_000_000_000)),
+					sdk.NewCoin("juno", sdk.NewInt(10_000_000_000)),
+					sdk.NewCoin("akt", sdk.NewInt(10_000_000_000)),
+					sdk.NewCoin("regen", sdk.NewInt(10_000_000_000)),
+					sdk.NewCoin("band", sdk.NewInt(10_000_000_000)),
+				}
+				msg.ScalingFactors = []uint64{1, 1, 1, 1, 1, 1, 1, 1}
+				return msg
+			}),
+			expectPass: true,
+		},
+		{
+			name: "greater than max post-scaled amount with regular scaling factors",
+			msg: createMsg(func(msg stableswap.MsgCreateStableswapPool) stableswap.MsgCreateStableswapPool {
+				msg.InitialPoolLiquidity = sdk.Coins{
+					sdk.NewCoin("osmo", sdk.NewInt(1+10_000_000_000)),
+					sdk.NewCoin("atom", sdk.NewInt(10_000_000_000)),
+					sdk.NewCoin("usdt", sdk.NewInt(10_000_000_000)),
+					sdk.NewCoin("usdc", sdk.NewInt(10_000_000_000)),
+					sdk.NewCoin("juno", sdk.NewInt(10_000_000_000)),
+					sdk.NewCoin("akt", sdk.NewInt(10_000_000_000)),
+					sdk.NewCoin("regen", sdk.NewInt(10_000_000_000)),
+					sdk.NewCoin("band", sdk.NewInt(10_000_000_000)),
+				}
+				msg.ScalingFactors = []uint64{1, 1, 1, 1, 1, 1, 1, 1}
+				return msg
+			}),
+			expectPass: false,
+		},
+		{
+			name: "100B token 8-asset pool using large scaling factors",
+			msg: createMsg(func(msg stableswap.MsgCreateStableswapPool) stableswap.MsgCreateStableswapPool {
+				msg.InitialPoolLiquidity = sdk.Coins{
+					sdk.NewCoin("osmo", sdk.NewInt(100_000_000_000_000_000)),
+					sdk.NewCoin("atom", sdk.NewInt(100_000_000_000_000_000)),
+					sdk.NewCoin("usdt", sdk.NewInt(100_000_000_000_000_000)),
+					sdk.NewCoin("usdc", sdk.NewInt(100_000_000_000_000_000)),
+					sdk.NewCoin("juno", sdk.NewInt(100_000_000_000_000_000)),
+					sdk.NewCoin("akt", sdk.NewInt(100_000_000_000_000_000)),
+					sdk.NewCoin("regen", sdk.NewInt(100_000_000_000_000_000)),
+					sdk.NewCoin("band", sdk.NewInt(100_000_000_000_000_000)),
+				}
+				msg.ScalingFactors = []uint64{10000000, 10000000, 10000000, 10000000, 10000000, 10000000, 10000000, 10000000}
+				return msg
+			}),
+			expectPass: true,
+		},
 	}
 
 	for _, test := range tests {
diff --git a/x/gamm/pool-models/stableswap/pool_test.go b/x/gamm/pool-models/stableswap/pool_test.go
index 1f48ca853..67e1d6f16 100644
--- a/x/gamm/pool-models/stableswap/pool_test.go
+++ b/x/gamm/pool-models/stableswap/pool_test.go
@@ -505,3 +505,137 @@ func TestScaleCoin(t *testing.T) {
 		})
 	}
 }
+
+func TestSwapOutAmtGivenIn(t *testing.T) {
+	tests := map[string]struct {
+		poolAssets            sdk.Coins
+		scalingFactors        []uint64
+		tokenIn               sdk.Coins
+		expectedTokenOut      sdk.Coin
+		expectedPoolLiquidity sdk.Coins
+		swapFee               sdk.Dec
+		expError              bool
+	}{
+		"even pool basic trade": {
+			poolAssets:     twoEvenStablePoolAssets,
+			scalingFactors: defaultTwoAssetScalingFactors,
+			tokenIn:        sdk.NewCoins(sdk.NewInt64Coin("foo", 100)),
+			// we expect at least a 1 token difference since output is truncated
+			expectedTokenOut:      sdk.NewInt64Coin("bar", 99),
+			expectedPoolLiquidity: twoEvenStablePoolAssets.Add(sdk.NewInt64Coin("foo", 100)).Sub(sdk.NewCoins(sdk.NewInt64Coin("bar", 99))),
+			swapFee:               sdk.ZeroDec(),
+			expError:              false,
+		},
+		"trade hits max pool capacity for asset": {
+			poolAssets: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 9_999_999_998),
+				sdk.NewInt64Coin("bar", 9_999_999_999),
+			),
+			scalingFactors:   defaultTwoAssetScalingFactors,
+			tokenIn:          sdk.NewCoins(sdk.NewInt64Coin("foo", 1)),
+			expectedTokenOut: sdk.NewInt64Coin("bar", 1),
+			expectedPoolLiquidity: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 9_999_999_999),
+				sdk.NewInt64Coin("bar", 9_999_999_998),
+			),
+			swapFee:  sdk.ZeroDec(),
+			expError: false,
+		},
+		"trade exceeds max pool capacity for asset": {
+			poolAssets: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 10_000_000_000),
+				sdk.NewInt64Coin("bar", 10_000_000_000),
+			),
+			scalingFactors:   defaultTwoAssetScalingFactors,
+			tokenIn:          sdk.NewCoins(sdk.NewInt64Coin("foo", 1)),
+			expectedTokenOut: sdk.Coin{},
+			expectedPoolLiquidity: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 10_000_000_000),
+				sdk.NewInt64Coin("bar", 10_000_000_000),
+			),
+			swapFee:  sdk.ZeroDec(),
+			expError: true,
+		},
+	}
+
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			ctx := sdk.Context{}
+			p := poolStructFromAssets(tc.poolAssets, tc.scalingFactors)
+
+			tokenOut, err := p.SwapOutAmtGivenIn(ctx, tc.tokenIn, tc.expectedTokenOut.Denom, tc.swapFee)
+			if !tc.expError {
+				require.Equal(t, tc.expectedTokenOut, tokenOut)
+				require.Equal(t, tc.expectedPoolLiquidity, p.PoolLiquidity)
+			}
+			osmoassert.ConditionalError(t, tc.expError, err)
+		})
+	}
+}
+
+func TestSwapInAmtGivenOut(t *testing.T) {
+	tests := map[string]struct {
+		poolAssets            sdk.Coins
+		scalingFactors        []uint64
+		tokenOut              sdk.Coins
+		expectedTokenIn       sdk.Coin
+		expectedPoolLiquidity sdk.Coins
+		swapFee               sdk.Dec
+		expError              bool
+	}{
+		"even pool basic trade": {
+			poolAssets:     twoEvenStablePoolAssets,
+			scalingFactors: defaultTwoAssetScalingFactors,
+			tokenOut:       sdk.NewCoins(sdk.NewInt64Coin("bar", 99)),
+			// we expect at least a 1 token difference from our true expected output since it is truncated
+			expectedTokenIn:       sdk.NewInt64Coin("foo", 99),
+			expectedPoolLiquidity: twoEvenStablePoolAssets.Add(sdk.NewInt64Coin("foo", 99)).Sub(sdk.NewCoins(sdk.NewInt64Coin("bar", 99))),
+			swapFee:               sdk.ZeroDec(),
+			expError:              false,
+		},
+		"trade hits max pool capacity for asset": {
+			poolAssets: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 9_999_999_998),
+				sdk.NewInt64Coin("bar", 9_999_999_999),
+			),
+			scalingFactors:  defaultTwoAssetScalingFactors,
+			tokenOut:        sdk.NewCoins(sdk.NewInt64Coin("bar", 1)),
+			expectedTokenIn: sdk.NewInt64Coin("foo", 1),
+			expectedPoolLiquidity: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 9_999_999_999),
+				sdk.NewInt64Coin("bar", 9_999_999_998),
+			),
+			swapFee:  sdk.ZeroDec(),
+			expError: false,
+		},
+		"trade exceeds max pool capacity for asset": {
+			poolAssets: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 10_000_000_000),
+				sdk.NewInt64Coin("bar", 10_000_000_000),
+			),
+			scalingFactors:  defaultTwoAssetScalingFactors,
+			tokenOut:        sdk.NewCoins(sdk.NewInt64Coin("bar", 1)),
+			expectedTokenIn: sdk.Coin{},
+			expectedPoolLiquidity: sdk.NewCoins(
+				sdk.NewInt64Coin("foo", 10_000_000_000),
+				sdk.NewInt64Coin("bar", 10_000_000_000),
+			),
+			swapFee:  sdk.ZeroDec(),
+			expError: true,
+		},
+	}
+
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			ctx := sdk.Context{}
+			p := poolStructFromAssets(tc.poolAssets, tc.scalingFactors)
+
+			tokenIn, err := p.SwapInAmtGivenOut(ctx, tc.tokenOut, tc.expectedTokenIn.Denom, tc.swapFee)
+			if !tc.expError {
+				require.Equal(t, tc.expectedTokenIn, tokenIn)
+				require.Equal(t, tc.expectedPoolLiquidity, p.PoolLiquidity)
+			}
+			osmoassert.ConditionalError(t, tc.expError, err)
+		})
+	}
+}
diff --git a/x/twap/export_test.go b/x/twap/export_test.go
index f4cf043f7..462808753 100644
--- a/x/twap/export_test.go
+++ b/x/twap/export_test.go
@@ -93,3 +93,7 @@ func (k *Keeper) GetAmmInterface() types.AmmInterface {
 func (k *Keeper) SetAmmInterface(ammInterface types.AmmInterface) {
 	k.ammkeeper = ammInterface
 }
+
+func (k *Keeper) AfterCreatePool(ctx sdk.Context, poolId uint64) error {
+	return k.afterCreatePool(ctx, poolId)
+}
diff --git a/x/twap/logic_test.go b/x/twap/logic_test.go
index a5e3beecc..8d629c995 100644
--- a/x/twap/logic_test.go
+++ b/x/twap/logic_test.go
@@ -10,6 +10,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/osmosis-labs/osmosis/v12/app/apptesting/osmoassert"
+	"github.com/osmosis-labs/osmosis/v12/osmoutils"
 	gammtypes "github.com/osmosis-labs/osmosis/v12/x/gamm/types"
 	"github.com/osmosis-labs/osmosis/v12/x/twap"
 	"github.com/osmosis-labs/osmosis/v12/x/twap/types"
@@ -1275,3 +1276,93 @@ func (s *TestSuite) TestUpdateRecords() {
 		})
 	}
 }
+
+func (s *TestSuite) TestAfterCreatePool() {
+	tests := map[string]struct {
+		poolId    uint64
+		poolCoins sdk.Coins
+		// if this field is set true, we swap in the same block with pool creation
+		runSwap     bool
+		expectedErr bool
+	}{
+		"Pool not existing": {
+			poolId:      2,
+			expectedErr: true,
+		},
+		"Default Pool, no swap on pool creation block": {
+			poolId:    1,
+			poolCoins: defaultTwoAssetCoins,
+			runSwap:   false,
+		},
+		"Default Pool, swap on pool creation block": {
+			poolId:    1,
+			poolCoins: defaultTwoAssetCoins,
+			runSwap:   true,
+		},
+		"Multi assets pool, no swap on pool creation block": {
+			poolId:    1,
+			poolCoins: defaultThreeAssetCoins,
+			runSwap:   false,
+		},
+		"Multi assets pool, swap on pool creation block": {
+			poolId:    1,
+			poolCoins: defaultThreeAssetCoins,
+			runSwap:   true,
+		},
+	}
+
+	for name, tc := range tests {
+		s.Run(name, func() {
+			s.SetupTest()
+			var poolId uint64
+
+			// set up pool with input coins
+			if tc.poolCoins != nil {
+				poolId = s.PrepareBalancerPoolWithCoins(tc.poolCoins...)
+				if tc.runSwap {
+					s.RunBasicSwap(poolId)
+				}
+			}
+
+			err := s.twapkeeper.AfterCreatePool(s.Ctx, tc.poolId)
+			if tc.expectedErr {
+				s.Require().Error(err)
+				return
+			}
+			s.Require().Equal(tc.poolId, poolId)
+			s.Require().NoError(err)
+
+			denoms := osmoutils.CoinsDenoms(tc.poolCoins)
+			denomPairs0, denomPairs1 := types.GetAllUniqueDenomPairs(denoms)
+			expectedRecords := []types.TwapRecord{}
+			for i := 0; i < len(denomPairs0); i++ {
+				expectedRecord, err := twap.NewTwapRecord(s.App.GAMMKeeper, s.Ctx, poolId, denomPairs0[i], denomPairs1[i])
+				s.Require().NoError(err)
+				expectedRecords = append(expectedRecords, expectedRecord)
+			}
+
+			// consistency check that the number of records is exactly equal to the number of denompairs
+			allRecords, err := s.twapkeeper.GetAllMostRecentRecordsForPool(s.Ctx, poolId)
+			s.Require().NoError(err)
+			s.Require().Equal(len(denomPairs0), len(allRecords))
+			s.Require().Equal(len(expectedRecords), len(allRecords))
+
+			// check on the correctness of all individual twap records
+			for i := 0; i < len(denomPairs0); i++ {
+				actualRecord, err := s.twapkeeper.GetMostRecentRecordStoreRepresentation(s.Ctx, poolId, denomPairs0[i], denomPairs1[i])
+				s.Require().NoError(err)
+				s.Require().Equal(expectedRecords[i], actualRecord)
+				actualRecord, err = s.twapkeeper.GetRecordAtOrBeforeTime(s.Ctx, poolId, s.Ctx.BlockTime(), denomPairs0[i], denomPairs1[i])
+				s.Require().NoError(err)
+				s.Require().Equal(expectedRecords[i], actualRecord)
+			}
+
+			// test that after creating a pool 
+			// has triggered `trackChangedPool`, 
+			// and that we have the state of price impacted pools.
+			changedPools := s.twapkeeper.GetChangedPools(s.Ctx)
+			s.Require().Equal(1, len(changedPools))
+			s.Require().Equal(tc.poolId, changedPools[0])
+		})
+	}
+}