diff --git a/.github/mergify.yml b/.github/mergify.yml index 813908c8bb0..6aa7a4a36b6 100644 --- a/.github/mergify.yml +++ b/.github/mergify.yml @@ -110,3 +110,11 @@ pull_request_rules: backport: branches: - v16.x + - name: backport patches to v17.x branch + conditions: + - base=main + - label=A:backport/v17.x + actions: + backport: + branches: + - v17.x diff --git a/.github/workflows/changelog-entry-reminder.yml b/.github/workflows/changelog-entry-reminder.yml index 2994fa9e2c0..f7bf66c6e80 100644 --- a/.github/workflows/changelog-entry-reminder.yml +++ b/.github/workflows/changelog-entry-reminder.yml @@ -14,7 +14,7 @@ jobs: - name: Get changed files id: changed-files - uses: tj-actions/changed-files@v37.1.0 + uses: tj-actions/changed-files@v37.6.1 with: files_ignore: | **/*.md diff --git a/.github/workflows/check-generated.yml b/.github/workflows/check-generated.yml index 3452bbf8eaf..25e1ff46897 100644 --- a/.github/workflows/check-generated.yml +++ b/.github/workflows/check-generated.yml @@ -1,18 +1,22 @@ -# Verify that generated code is up-to-date. -# -# Note that we run these checks regardless whether the input files have -# changed, because generated code can change in response to toolchain updates -# even if no files in the repository are modified. -name: Check generated code +# Ensure that generated code is up-to-date. +name: Generate protobuf code on: workflow_dispatch: pull_request: - branches: - - '*' + paths: + - 'proto/**' + - '**/**.proto' + - '**/**.pb.go' + - '**/**.sum' + - '**/client/**.go' + - 'scripts/ci/**' + - 'Makefile' + - 'Dockerfile' + - '.github/workflows/check-generated.yml' permissions: - contents: read + contents: write concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -22,15 +26,21 @@ jobs: check-proto: runs-on: ubuntu-latest steps: - - - name: 🐿 Setup Golang - uses: actions/setup-go@v4 - with: - go-version: '^1.20' - uses: actions/checkout@v3 with: - fetch-depth: 1 # we need a .git directory to run git diff + ref: ${{ github.event.pull_request.head.ref }} - name: "Check protobuf generated code" - run: scripts/ci/check-generated.sh + run: | + make proto-all + make run-querygen + - + name: Commit changes + run: | + git config user.name github-actions + git config user.email github-actions@github.com + git add *.go + git add *.proto + git commit -m "Generated protofile changes" || echo "No changes to commit" + git push \ No newline at end of file diff --git a/.github/workflows/contracts.yml b/.github/workflows/contracts.yml index d9dc994c981..ce3eba2c679 100644 --- a/.github/workflows/contracts.yml +++ b/.github/workflows/contracts.yml @@ -4,10 +4,26 @@ on: pull_request: branches: - "**" + paths: + - "**/**/*.wasm" + - "**/**.rs" + - "**/**Cargo.toml" + - "**/**Cargo.lock" + - "**/cosmwasm/**" + - "**/wasmbinding/**" + - ".github/workflows/contracts.yml" push: branches: - "main" - "v[0-9]**" + paths: + - "**/**/*.wasm" + - "**/**.rs" + - "**/**Cargo.toml" + - "**/**Cargo.lock" + - "**/cosmwasm/**" + - "**/wasmbinding/**" + - ".github/workflows/contracts.yml" workflow_dispatch: concurrency: @@ -30,13 +46,6 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v3 - - uses: technote-space/get-diff-action@v6.1.2 - with: - PATTERNS: | - **/**/*.wasm - **/**.rs - **/**Cargo.toml - **/**Cargo.lock - name: Optimize working-directory: ${{ matrix.contract.workdir }} @@ -46,7 +55,6 @@ jobs: --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ cosmwasm/workspace-optimizer:0.12.10 - if: env.GIT_DIFF - name: 'Upload optimized contract artifact' uses: actions/upload-artifact@v3 @@ -54,7 +62,6 @@ jobs: name: ${{ matrix.contract.name }} path: ${{ matrix.contract.workdir }}${{ matrix.contract.build }} retention-days: 1 - if: env.GIT_DIFF - name: 'Upload Cargo.lock artifact' uses: actions/upload-artifact@v3 @@ -62,13 +69,11 @@ jobs: name: Cargo.lock path: ${{ matrix.contract.workdir }}Cargo.lock retention-days: 1 - if: env.GIT_DIFF # TODO: workspace optimizer compiles all contracts in the workspace, so we could just run that once # and then check the diff of all the bytecode files - name: Check Test Data working-directory: ${{ matrix.contract.workdir }} - if: env.GIT_DIFF run: > diff "${GITHUB_WORKSPACE}/${{ matrix.contract.bytecode }}" ${{ matrix.contract.build }} @@ -87,31 +92,20 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v3 - - uses: technote-space/get-diff-action@v6.1.2 - with: - PATTERNS: | - **/bytecode/*.wasm - **/**.rs - **/**Cargo.toml - **/**Cargo.lock - name: Install toolchain uses: dtolnay/rust-toolchain@1.69.0 with: target: wasm32-unknown-unknown - if: env.GIT_DIFF - name: Add the wasm target working-directory: ${{ matrix.contract.workdir }} run: > rustup target add wasm32-unknown-unknown; - if: env.GIT_DIFF - name: Test working-directory: ${{ matrix.contract.workdir }} run: > cargo test - if: env.GIT_DIFF - lints: name: Cosmwasm Lints @@ -123,27 +117,18 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v3 - - uses: technote-space/get-diff-action@v6.1.2 - with: - PATTERNS: | - **/**.rs - **/**Cargo.toml - **/**Cargo.lock - name: Install toolchain uses: dtolnay/rust-toolchain@1.69.0 with: components: rustfmt, clippy - if: env.GIT_DIFF - name: Format working-directory: ${{ matrix.workdir }} run: > cargo fmt --all -- --check - if: env.GIT_DIFF - name: run cargo clippy working-directory: ${{ matrix.workdir }} run: > cargo clippy -- -D warnings - if: env.GIT_DIFF diff --git a/.github/workflows/protocol-docs.yml b/.github/workflows/protocol-docs.yml new file mode 100644 index 00000000000..499829768c3 --- /dev/null +++ b/.github/workflows/protocol-docs.yml @@ -0,0 +1,52 @@ +name: Render and Deploy Docs + +on: + workflow_dispatch: + push: + branches: + - main + +jobs: + + build: + name: Render and deploy protocol and API docs + timeout-minutes: 30 + runs-on: ubuntu-latest + steps: + - name: Checkout the source code + uses: actions/checkout@v3 + with: + lfs: true + - name: Install rust toolchain + uses: dtolnay/rust-toolchain@nightly + - name: Load Rust caching + uses: astriaorg/buildjet-rust-cache@v2.5.1 + - name: Load get-version action to grab version component of deployment path + uses: battila7/get-version-action@v2 + id: get_version + - name: Print version component of deployment path + run: echo ${{ steps.get_version.outputs.version }} + - name: Install mdbook + run: cargo install mdbook mdbook-katex mdbook-mermaid + - name: Build protocol spec + run: cd docs/protocol && mdbook build + - name: Move protocol spec to subdirectory & Deploy + env: + DO_DOCS_PK: ${{ secrets.DO_DOCS_PK }} + DO_DOCS_IP: ${{ secrets.DO_DOCS_IP }} + run: | + cd docs/protocol + if [ -d "do-tmp" ]; then rm -rf do-tmp; fi + mkdir do-tmp + mv book do-tmp/${{ steps.get_version.outputs.version }} + tree do-tmp + which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y ) + which rsync || ( apt-get update -y && apt-get install rsync -y ) + eval $(ssh-agent -s) + ssh-add <(echo "$DO_DOCS_PK" ) + mkdir -p ~/.ssh + chmod 700 ~/.ssh + ssh-keyscan $DO_DOCS_IP >> ~/.ssh/known_hosts + chmod 644 ~/.ssh/known_hosts + cd do-tmp/main + scp -r * root@$DO_DOCS_IP:/var/www/html diff --git a/.github/workflows/required_labels.yml b/.github/workflows/required_labels.yml index d3d569d6355..2c8e983971f 100644 --- a/.github/workflows/required_labels.yml +++ b/.github/workflows/required_labels.yml @@ -14,7 +14,7 @@ jobs: - name: Get changed files id: changed-files - uses: tj-actions/changed-files@v37.1.0 + uses: tj-actions/changed-files@v37.6.1 with: files_ignore: | **/*.md diff --git a/.gitignore b/.gitignore index 4313d719db8..84f981a2f3a 100644 --- a/.gitignore +++ b/.gitignore @@ -250,5 +250,8 @@ blocks.db tests/cl-genesis-positions/script tests/cl-genesis-positions/*.json +# Docs +docs/protocol/book + # Release folder -dist/ \ No newline at end of file +dist/ diff --git a/.golangci.yml b/.golangci.yml index 5847017c11c..c43cf7e0c6f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -43,6 +43,7 @@ linters: - unconvert - unused - whitespace + - unparam issues: exclude-rules: diff --git a/.vscode/launch.json b/.vscode/launch.json index fba480ca4ce..4618a2f8c6d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -21,7 +21,7 @@ "OSMOSIS_E2E_SKIP_UPGRADE": "true", "OSMOSIS_E2E_SKIP_CLEANUP": "true", "OSMOSIS_E2E_SKIP_STATE_SYNC": "true", - "OSMOSIS_E2E_UPGRADE_VERSION": "v17", + "OSMOSIS_E2E_UPGRADE_VERSION": "v18", "OSMOSIS_E2E_DEBUG_LOG": "false", }, "preLaunchTask": "e2e-setup" diff --git a/CHANGELOG.md b/CHANGELOG.md index 30a8f46f185..b770c2af6bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### API breaks + +* [#6071](https://github.com/osmosis-labs/osmosis/pull/6071) reduce number of returns for UpdatePosition and TicksToSqrtPrice functions +* [#5906](https://github.com/osmosis-labs/osmosis/pull/5906) Add `AccountLockedCoins` query in lockup module to stargate whitelist. + +## v17.0.0 + +### API breaks + +* [#6014](https://github.com/osmosis-labs/osmosis/pull/6014) refactor: reduce the number of returns in superfluid migration +* [#5983](https://github.com/osmosis-labs/osmosis/pull/5983) refactor(CL): 6 return values in CL CreatePosition with a struct +* [#6004](https://github.com/osmosis-labs/osmosis/pull/6004) reduce number of returns for creating full range position +* [#6018](https://github.com/osmosis-labs/osmosis/pull/6018) golangci: add unused parameters linter +* [#6033](https://github.com/osmosis-labs/osmosis/pull/6033) change tick API from sdk.Dec to osmomath.BigDec + +### Features + +* [#5072](https://github.com/osmosis-labs/osmosis/pull/5072) IBC-hooks: Add support for async acks when processing onRecvPacket + ### State Breaking * [#5532](https://github.com/osmosis-labs/osmosis/pull/5532) fix: Fix x/tokenfactory genesis import denoms reset x/bank existing denom metadata @@ -52,27 +71,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [#5874](https://github.com/osmosis-labs/osmosis/pull/5874) Remove Partial Migration from superfluid migration to CL * [#5901](https://github.com/osmosis-labs/osmosis/pull/5901) Adding support for CW pools in ProtoRev * [#5937](https://github.com/osmosis-labs/osmosis/pull/5937) feat: add SetScalingFactorController gov prop +* [#5949](https://github.com/osmosis-labs/osmosis/pull/5949) Add message to convert from superfluid / locks to native staking directly. +* [#5939](https://github.com/osmosis-labs/osmosis/pull/5939) Fix: Flip existing twapRecords base/quote price denoms +* [#5938](https://github.com/osmosis-labs/osmosis/pull/5938) Chore: Fix valset amino codec ### BugFix * [#5831](https://github.com/osmosis-labs/osmosis/pull/5831) Fix superfluid_delegations query * [#5835](https://github.com/osmosis-labs/osmosis/pull/5835) Fix println's for "amountZeroInRemainingBigDec before fee" making it into production -* [#5841] (https://github.com/osmosis-labs/osmosis/pull/5841) Fix protorev's out of gas erroring of the user's transcation. -* [#5930] (https://github.com/osmosis-labs/osmosis/pull/5930) Updating Protorev Binary Search Range Logic with CL Pools +* [#5841](https://github.com/osmosis-labs/osmosis/pull/5841) Fix protorev's out of gas erroring of the user's transcation. +* [#5930](https://github.com/osmosis-labs/osmosis/pull/5930) Updating Protorev Binary Search Range Logic with CL Pools +* [#5950](https://github.com/osmosis-labs/osmosis/pull/5950) fix: spot price for cosmwasm pool types ### Misc Improvements * [#5534](https://github.com/osmosis-labs/osmosis/pull/5534) fix: fix the account number of x/tokenfactory module account * [#5750](https://github.com/osmosis-labs/osmosis/pull/5750) feat: add cli commmand for converting proto structs to proto marshalled bytes * [#5889](https://github.com/osmosis-labs/osmosis/pull/5889) provides an API for protorev to determine max amountIn that can be swapped based on max ticks willing to be traversed -* [#5849] (https://github.com/osmosis-labs/osmosis/pull/5849) CL: Lower gas for leaving a position and withdrawing rewards +* [#5849](https://github.com/osmosis-labs/osmosis/pull/5849) CL: Lower gas for leaving a position and withdrawing rewards * [#5855](https://github.com/osmosis-labs/osmosis/pull/5855) feat(x/cosmwasmpool): Sending token_in_max_amount to the contract before running contract msg -* [#5893] (https://github.com/osmosis-labs/osmosis/pull/5893) Export createPosition method in CL so other modules can use it in testing -* [#5870] (https://github.com/osmosis-labs/osmosis/pull/5870) Remove v14/ separator in protorev rest endpoints -* [#5923] (https://github.com/osmosis-labs/osmosis/pull/5923) CL: Lower gas for initializing ticks -* [#5927] (https://github.com/osmosis-labs/osmosis/pull/5927) Add gas metering to x/tokenfactory trackBeforeSend hook +* [#5893](https://github.com/osmosis-labs/osmosis/pull/5893) Export createPosition method in CL so other modules can use it in testing +* [#5870](https://github.com/osmosis-labs/osmosis/pull/5870) Remove v14/ separator in protorev rest endpoints +* [#5923](https://github.com/osmosis-labs/osmosis/pull/5923) CL: Lower gas for initializing ticks +* [#5927](https://github.com/osmosis-labs/osmosis/pull/5927) Add gas metering to x/tokenfactory trackBeforeSend hook * [#5890](https://github.com/osmosis-labs/osmosis/pull/5890) feat: CreateCLPool & LinkCFMMtoCL pool into one gov-prop +* [#5959](https://github.com/osmosis-labs/osmosis/pull/5959) allow testing with different chain-id's in E2E testing * [#5964](https://github.com/osmosis-labs/osmosis/pull/5964) fix e2e test concurrency bugs +* [#5948] (https://github.com/osmosis-labs/osmosis/pull/5948) Parameterizing Pool Type Information in Protorev +* [#6001](https://github.com/osmosis-labs/osmosis/pull/6001) feat: improve set-env CLI cmd\ +* [#5953] (https://github.com/osmosis-labs/osmosis/pull/5953) Supporting two pool routes in ProtoRev +* [#6012](https://github.com/osmosis-labs/osmosis/pull/6012) chore: add autocomplete to makefile +* [#6085](https://github.com/osmosis-labs/osmosis/pull/6085) (v18: feat) Volume-Split, setup gauges to split evenly ### Minor improvements & Bug Fixes diff --git a/Makefile b/Makefile index 83d075f9752..d82507b70f3 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,8 @@ LEDGER_ENABLED ?= true SDK_PACK := $(shell go list -m github.com/cosmos/cosmos-sdk | sed 's/ /\@/g') BUILDDIR ?= $(CURDIR)/build DOCKER := $(shell which docker) -E2E_UPGRADE_VERSION := "v17" +E2E_UPGRADE_VERSION := "v18" +#SHELL := /bin/bash GO_VERSION := $(shell cat go.mod | grep -E 'go [0-9].[0-9]+' | cut -d ' ' -f 2) GO_MODULE := $(shell cat go.mod | grep "module " | cut -d ' ' -f 2) @@ -119,6 +120,45 @@ build-all: check_version go.sum install: check_version go.sum GOWORK=off go install -mod=readonly $(BUILD_FLAGS) $(GO_MODULE)/cmd/osmosisd +install-with-autocomplete: check_version go.sum + GOWORK=off go install -mod=readonly $(BUILD_FLAGS) $(GO_MODULE)/cmd/osmosisd + @PARENT_SHELL=$$(ps -o ppid= -p $$PPID | xargs ps -o comm= -p); \ + if echo "$$PARENT_SHELL" | grep -q "zsh"; then \ + if ! grep -q ". <(osmosisd enable-cli-autocomplete zsh)" ~/.zshrc; then \ + echo ". <(osmosisd enable-cli-autocomplete zsh)" >> ~/.zshrc; \ + echo; \ + echo "Autocomplete enabled. Run 'source ~/.zshrc' to complete installation."; \ + else \ + echo; \ + echo "Autocomplete already enabled in ~/.zshrc"; \ + fi \ + elif echo "$$PARENT_SHELL" | grep -q "bash" && [ "$$(uname)" = "Darwin" ]; then \ + if ! grep -q -e "\. <(osmosisd enable-cli-autocomplete bash)" -e '\[\[ -r "/opt/homebrew/etc/profile.d/bash_completion.sh" \]\] && \. "/opt/homebrew/etc/profile.d/bash_completion.sh"' ~/.bash_profile; then \ + brew install bash-completion; \ + echo '[ -r "/opt/homebrew/etc/profile.d/bash_completion.sh" ] && . "/opt/homebrew/etc/profile.d/bash_completion.sh"' >> ~/.bash_profile; \ + echo ". <(osmosisd enable-cli-autocomplete bash)" >> ~/.bash_profile; \ + echo; \ + echo; \ + echo "Autocomplete enabled. Run 'source ~/.bash_profile' to complete installation."; \ + else \ + echo "Autocomplete already enabled in ~/.bash_profile"; \ + fi \ + elif echo "$$PARENT_SHELL" | grep -q "bash" && [ "$$(uname)" = "Linux" ]; then \ + if ! grep -q ". <(osmosisd enable-cli-autocomplete bash)" ~/.bash_profile; then \ + sudo apt-get install -y bash-completion; \ + echo '[ -r "/etc/bash_completion" ] && . "/etc/bash_completion"' >> ~/.bash_profile; \ + echo ". <(osmosisd enable-cli-autocomplete bash)" >> ~/.bash_profile; \ + echo; \ + echo "Autocomplete enabled. Run 'source ~/.bash_profile' to complete installation."; \ + else \ + echo; \ + echo "Autocomplete already enabled in ~/.bash_profile"; \ + fi \ + else \ + echo "Shell or OS not recognized. Skipping autocomplete setup."; \ + fi + + # Cross-building for arm64 from amd64 (or viceversa) takes # a lot of time due to QEMU virtualization but it's the only way (afaik) # to get a statically linked binary with CosmWasm diff --git a/app/app.go b/app/app.go index ab126d38c01..56e97081517 100644 --- a/app/app.go +++ b/app/app.go @@ -58,6 +58,7 @@ import ( v15 "github.com/osmosis-labs/osmosis/v17/app/upgrades/v15" v16 "github.com/osmosis-labs/osmosis/v17/app/upgrades/v16" v17 "github.com/osmosis-labs/osmosis/v17/app/upgrades/v17" + v18 "github.com/osmosis-labs/osmosis/v17/app/upgrades/v18" v3 "github.com/osmosis-labs/osmosis/v17/app/upgrades/v3" v4 "github.com/osmosis-labs/osmosis/v17/app/upgrades/v4" v5 "github.com/osmosis-labs/osmosis/v17/app/upgrades/v5" @@ -103,7 +104,7 @@ var ( // _ sdksimapp.App = (*OsmosisApp)(nil) - Upgrades = []upgrades.Upgrade{v4.Upgrade, v5.Upgrade, v7.Upgrade, v9.Upgrade, v11.Upgrade, v12.Upgrade, v13.Upgrade, v14.Upgrade, v15.Upgrade, v16.Upgrade, v17.Upgrade} + Upgrades = []upgrades.Upgrade{v4.Upgrade, v5.Upgrade, v7.Upgrade, v9.Upgrade, v11.Upgrade, v12.Upgrade, v13.Upgrade, v14.Upgrade, v15.Upgrade, v16.Upgrade, v17.Upgrade, v18.Upgrade} Forks = []upgrades.Fork{v3.Fork, v6.Fork, v8.Fork, v10.Fork} ) diff --git a/app/apptesting/concentrated_liquidity.go b/app/apptesting/concentrated_liquidity.go index 4ef899b4297..8107f23ed68 100644 --- a/app/apptesting/concentrated_liquidity.go +++ b/app/apptesting/concentrated_liquidity.go @@ -14,6 +14,7 @@ import ( var ( ETH = "eth" USDC = "usdc" + WBTC = "ibc/D1542AA8762DB13087D8364F3EA6509FD6F009A34F00426AF9E4F9FA85CBBF1F" DefaultTickSpacing = uint64(100) DefaultLowerTick = int64(30545000) DefaultUpperTick = int64(31500000) @@ -72,11 +73,11 @@ func (s *KeeperTestHelper) PrepareConcentratedPoolWithCoinsAndLockedFullRangePos clPool := s.PrepareCustomConcentratedPool(s.TestAccs[0], denom1, denom2, DefaultTickSpacing, sdk.ZeroDec()) fundCoins := sdk.NewCoins(sdk.NewCoin(denom1, DefaultCoinAmount), sdk.NewCoin(denom2, DefaultCoinAmount)) s.FundAcc(s.TestAccs[0], fundCoins) - positionId, _, _, _, concentratedLockId, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPool.GetId(), s.TestAccs[0], fundCoins, time.Hour*24*14) + positionData, concentratedLockId, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPool.GetId(), s.TestAccs[0], fundCoins, time.Hour*24*14) s.Require().NoError(err) clPool, err = s.App.ConcentratedLiquidityKeeper.GetConcentratedPoolById(s.Ctx, clPool.GetId()) s.Require().NoError(err) - return clPool, concentratedLockId, positionId + return clPool, concentratedLockId, positionData.ID } // PrepareCustomConcentratedPool sets up a concentrated liquidity pool with the custom parameters. @@ -113,9 +114,9 @@ func (s *KeeperTestHelper) PrepareMultipleConcentratedPools(poolsToCreate uint16 // CreateFullRangePosition creates a full range position and returns position id and the liquidity created. func (s *KeeperTestHelper) CreateFullRangePosition(pool types.ConcentratedPoolExtension, coins sdk.Coins) (uint64, sdk.Dec) { s.FundAcc(s.TestAccs[0], coins) - positionId, _, _, liquidity, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, pool.GetId(), s.TestAccs[0], coins) + positionData, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, pool.GetId(), s.TestAccs[0], coins) s.Require().NoError(err) - return positionId, liquidity + return positionData.ID, positionData.Liquidity } // WithdrawFullRangePosition withdraws given liquidity from a position specified by id. @@ -138,6 +139,6 @@ func (s *KeeperTestHelper) SetupConcentratedLiquidityDenomsAndPoolCreation() { // modify authorized quote denoms to include test denoms. defaultParams := types.DefaultParams() defaultParams.IsPermissionlessPoolCreationEnabled = true - defaultParams.AuthorizedQuoteDenoms = append(defaultParams.AuthorizedQuoteDenoms, ETH, USDC, BAR, BAZ, FOO, UOSMO, STAKE) + defaultParams.AuthorizedQuoteDenoms = append(defaultParams.AuthorizedQuoteDenoms, ETH, USDC, BAR, BAZ, FOO, UOSMO, STAKE, WBTC) s.App.ConcentratedLiquidityKeeper.SetParams(s.Ctx, defaultParams) } diff --git a/app/apptesting/cosmwasmpool.go b/app/apptesting/cosmwasmpool.go index bc17512accb..94251291fa6 100644 --- a/app/apptesting/cosmwasmpool.go +++ b/app/apptesting/cosmwasmpool.go @@ -29,7 +29,7 @@ func (s *KeeperTestHelper) PrepareCosmWasmPool() cosmwasmpooltypes.CosmWasmExten return s.PrepareCustomTransmuterPool(s.TestAccs[0], []string{DefaultTransmuterDenomA, DefaultTransmuterDenomB}) } -// PrepareCustomConcentratedPool sets up a concentrated liquidity pool with the custom parameters. +// PrepareCustomTransmuterPool sets up a transmuter pool with the custom parameters. func (s *KeeperTestHelper) PrepareCustomTransmuterPool(owner sdk.AccAddress, denoms []string) cosmwasmpooltypes.CosmWasmExtension { // Mint some assets to the account. s.FundAcc(s.TestAccs[0], DefaultAcctFunds) diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 10f6f726ed8..175c4175c11 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -62,9 +62,9 @@ import ( ibchost "github.com/cosmos/ibc-go/v4/modules/core/24-host" ibckeeper "github.com/cosmos/ibc-go/v4/modules/core/keeper" - packetforward "github.com/strangelove-ventures/packet-forward-middleware/v4/router" - packetforwardkeeper "github.com/strangelove-ventures/packet-forward-middleware/v4/router/keeper" - packetforwardtypes "github.com/strangelove-ventures/packet-forward-middleware/v4/router/types" + packetforward "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v4/router" + packetforwardkeeper "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v4/router/keeper" + packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v4/router/types" // IBC Transfer: Defines the "transfer" IBC port transfer "github.com/cosmos/ibc-go/v4/modules/apps/transfer" @@ -246,8 +246,11 @@ func (appKeepers *AppKeepers) InitNormalKeepers( // Configure the hooks keeper hooksKeeper := ibchookskeeper.NewKeeper( appKeepers.keys[ibchookstypes.StoreKey], + appKeepers.GetSubspace(ibchookstypes.ModuleName), + appKeepers.IBCKeeper.ChannelKeeper, + nil, ) - appKeepers.IBCHooksKeeper = &hooksKeeper + appKeepers.IBCHooksKeeper = hooksKeeper appKeepers.WireICS20PreWasmKeeper(appCodec, bApp, appKeepers.IBCHooksKeeper) @@ -341,7 +344,10 @@ func (appKeepers *AppKeepers) InitNormalKeepers( appKeepers.BankKeeper, appKeepers.AccountKeeper, appKeepers.DistrKeeper, + appKeepers.StakingKeeper, + appKeepers.ProtoRevKeeper, ) + appKeepers.PoolManagerKeeper.SetStakingKeeper(appKeepers.StakingKeeper) appKeepers.GAMMKeeper.SetPoolManager(appKeepers.PoolManagerKeeper) appKeepers.ConcentratedLiquidityKeeper.SetPoolManagerKeeper(appKeepers.PoolManagerKeeper) appKeepers.CosmwasmPoolKeeper.SetPoolManagerKeeper(appKeepers.PoolManagerKeeper) @@ -365,6 +371,7 @@ func (appKeepers *AppKeepers) InitNormalKeepers( appKeepers.ConcentratedLiquidityKeeper, ) appKeepers.ProtoRevKeeper = &protorevKeeper + appKeepers.PoolManagerKeeper.SetProtorevKeeper(appKeepers.ProtoRevKeeper) txFeesKeeper := txfeeskeeper.NewKeeper( appKeepers.AccountKeeper, @@ -391,11 +398,6 @@ func (appKeepers *AppKeepers) InitNormalKeepers( appKeepers.ConcentratedLiquidityKeeper.SetIncentivesKeeper(appKeepers.IncentivesKeeper) appKeepers.GAMMKeeper.SetIncentivesKeeper(appKeepers.IncentivesKeeper) - appKeepers.SuperfluidKeeper = superfluidkeeper.NewKeeper( - appKeepers.keys[superfluidtypes.StoreKey], appKeepers.GetSubspace(superfluidtypes.ModuleName), - *appKeepers.AccountKeeper, appKeepers.BankKeeper, appKeepers.StakingKeeper, appKeepers.DistrKeeper, appKeepers.EpochsKeeper, appKeepers.LockupKeeper, appKeepers.GAMMKeeper, appKeepers.IncentivesKeeper, - lockupkeeper.NewMsgServerImpl(appKeepers.LockupKeeper), appKeepers.ConcentratedLiquidityKeeper) - mintKeeper := mintkeeper.NewKeeper( appKeepers.keys[minttypes.StoreKey], appKeepers.GetSubspace(minttypes.ModuleName), @@ -428,7 +430,7 @@ func (appKeepers *AppKeepers) InitNormalKeepers( appKeepers.keys[tokenfactorytypes.StoreKey], appKeepers.GetSubspace(tokenfactorytypes.ModuleName), appKeepers.AccountKeeper, - appKeepers.BankKeeper.WithMintCoinsRestriction(tokenfactorytypes.NewTokenFactoryDenomMintCoinsRestriction()), + appKeepers.BankKeeper, appKeepers.DistrKeeper, ) appKeepers.TokenFactoryKeeper = &tokenFactoryKeeper @@ -443,6 +445,11 @@ func (appKeepers *AppKeepers) InitNormalKeepers( appKeepers.ValidatorSetPreferenceKeeper = &validatorSetPreferenceKeeper + appKeepers.SuperfluidKeeper = superfluidkeeper.NewKeeper( + appKeepers.keys[superfluidtypes.StoreKey], appKeepers.GetSubspace(superfluidtypes.ModuleName), + *appKeepers.AccountKeeper, appKeepers.BankKeeper, appKeepers.StakingKeeper, appKeepers.DistrKeeper, appKeepers.EpochsKeeper, appKeepers.LockupKeeper, appKeepers.GAMMKeeper, appKeepers.IncentivesKeeper, + lockupkeeper.NewMsgServerImpl(appKeepers.LockupKeeper), appKeepers.ConcentratedLiquidityKeeper, appKeepers.PoolManagerKeeper, appKeepers.ValidatorSetPreferenceKeeper) + // The last arguments can contain custom message handlers, and custom query handlers, // if we want to allow any custom callbacks supportedFeatures := "iterator,staking,stargate,osmosis,cosmwasm_1_1,cosmwasm_1_2" @@ -477,6 +484,7 @@ func (appKeepers *AppKeepers) InitNormalKeepers( appKeepers.RateLimitingICS4Wrapper.ContractKeeper = appKeepers.ContractKeeper appKeepers.Ics20WasmHooks.ContractKeeper = appKeepers.ContractKeeper appKeepers.CosmwasmPoolKeeper.SetContractKeeper(appKeepers.ContractKeeper) + appKeepers.IBCHooksKeeper.ContractKeeper = appKeepers.ContractKeeper // set token factory contract keeper appKeepers.TokenFactoryKeeper.SetContractKeeper(appKeepers.ContractKeeper) @@ -524,7 +532,7 @@ func (appKeepers *AppKeepers) InitNormalKeepers( // // Note that the forward middleware is only integrated on the "reveive" direction. It can be safely skipped when sending. // Note also that the forward middleware is called "router", but we are using the name "forward" for clarity -// This may later be renamed upstream: https://github.com/strangelove-ventures/packet-forward-middleware/issues/10 +// This may later be renamed upstream: https://github.com/ibc-apps/middleware/packet-forward-middleware/issues/10 // // After this, the wasm keeper is required to be set on both // appkeepers.WasmHooks AND appKeepers.RateLimitingICS4Wrapper @@ -671,6 +679,7 @@ func (appKeepers *AppKeepers) initParamsKeeper(appCodec codec.BinaryCodec, legac paramsKeeper.Subspace(icqtypes.ModuleName) paramsKeeper.Subspace(packetforwardtypes.ModuleName).WithKeyTable(packetforwardtypes.ParamKeyTable()) paramsKeeper.Subspace(cosmwasmpooltypes.ModuleName) + paramsKeeper.Subspace(ibchookstypes.ModuleName) return paramsKeeper } diff --git a/app/keepers/modules.go b/app/keepers/modules.go index 9a7460d4bbe..a52bc234f7f 100644 --- a/app/keepers/modules.go +++ b/app/keepers/modules.go @@ -3,10 +3,10 @@ package keepers import ( "github.com/CosmWasm/wasmd/x/wasm" wasmclient "github.com/CosmWasm/wasmd/x/wasm/client" + "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v4/router" transfer "github.com/cosmos/ibc-go/v4/modules/apps/transfer" ibc "github.com/cosmos/ibc-go/v4/modules/core" ibcclientclient "github.com/cosmos/ibc-go/v4/modules/core/02-client/client" - "github.com/strangelove-ventures/packet-forward-middleware/v4/router" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/auth" diff --git a/app/modules.go b/app/modules.go index 88c9765f2d9..75ccd2de33f 100644 --- a/app/modules.go +++ b/app/modules.go @@ -12,8 +12,8 @@ import ( ibchost "github.com/cosmos/ibc-go/v4/modules/core/24-host" ibckeeper "github.com/cosmos/ibc-go/v4/modules/core/keeper" - packetforward "github.com/strangelove-ventures/packet-forward-middleware/v4/router" - packetforwardtypes "github.com/strangelove-ventures/packet-forward-middleware/v4/router/types" + packetforward "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v4/router" + packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v4/router/types" ibchookstypes "github.com/osmosis-labs/osmosis/x/ibc-hooks/types" @@ -181,7 +181,7 @@ func appModules( tokenfactory.NewAppModule(*app.TokenFactoryKeeper, app.AccountKeeper, app.BankKeeper), valsetprefmodule.NewAppModule(appCodec, *app.ValidatorSetPreferenceKeeper), ibcratelimitmodule.NewAppModule(*app.RateLimitingICS4Wrapper), - ibc_hooks.NewAppModule(app.AccountKeeper), + ibc_hooks.NewAppModule(app.AccountKeeper, *app.IBCHooksKeeper), icq.NewAppModule(*app.AppKeepers.ICQKeeper), packetforward.NewAppModule(app.PacketForwardKeeper), cwpoolmodule.NewAppModule(appCodec, *app.CosmwasmPoolKeeper), diff --git a/app/upgrades/v15/constants.go b/app/upgrades/v15/constants.go index 2338b59a01d..509f367b25a 100644 --- a/app/upgrades/v15/constants.go +++ b/app/upgrades/v15/constants.go @@ -2,8 +2,8 @@ package v15 import ( store "github.com/cosmos/cosmos-sdk/store/types" + packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v4/router/types" icqtypes "github.com/cosmos/ibc-apps/modules/async-icq/v4/types" - packetforwardtypes "github.com/strangelove-ventures/packet-forward-middleware/v4/router/types" "github.com/osmosis-labs/osmosis/v17/app/upgrades" poolmanagertypes "github.com/osmosis-labs/osmosis/v17/x/poolmanager/types" diff --git a/app/upgrades/v15/export_test.go b/app/upgrades/v15/export_test.go index 8369670efc7..0049ed724d0 100644 --- a/app/upgrades/v15/export_test.go +++ b/app/upgrades/v15/export_test.go @@ -26,8 +26,8 @@ func SetICQParams(ctx sdk.Context, icqKeeper *icqkeeper.Keeper) { setICQParams(ctx, icqKeeper) } -func MigrateBalancerPoolToSolidlyStable(ctx sdk.Context, gammKeeper *gammkeeper.Keeper, poolmanager *poolmanager.Keeper, bankKeeper bankkeeper.Keeper, poolId uint64) { - migrateBalancerPoolToSolidlyStable(ctx, gammKeeper, poolmanager, bankKeeper, poolId) +func MigrateBalancerPoolToSolidlyStable(ctx sdk.Context, gammKeeper *gammkeeper.Keeper, bankKeeper bankkeeper.Keeper, poolId uint64) { + migrateBalancerPoolToSolidlyStable(ctx, gammKeeper, bankKeeper, poolId) } func SetRateLimits(ctx sdk.Context, accountKeeper *authkeeper.AccountKeeper, rateLimitingICS4Wrapper *ibcratelimit.ICS4Wrapper, wasmKeeper *wasmkeeper.Keeper) { diff --git a/app/upgrades/v15/upgrade_test.go b/app/upgrades/v15/upgrade_test.go index e3bea7929f7..effeb441dc8 100644 --- a/app/upgrades/v15/upgrade_test.go +++ b/app/upgrades/v15/upgrade_test.go @@ -90,7 +90,6 @@ func (suite *UpgradeTestSuite) TestMigrateBalancerToStablePools() { ctx := suite.Ctx gammKeeper := suite.App.GAMMKeeper - poolmanagerKeeper := suite.App.PoolManagerKeeper // bankKeeper := suite.App.BankKeeper testAccount := suite.TestAccs[0] @@ -138,7 +137,7 @@ func (suite *UpgradeTestSuite) TestMigrateBalancerToStablePools() { balancerBalances := suite.App.BankKeeper.GetAllBalances(ctx, balancerPool.GetAddress()) // test migrating the balancer pool to a stable pool - v15.MigrateBalancerPoolToSolidlyStable(ctx, gammKeeper, poolmanagerKeeper, suite.App.BankKeeper, poolID) + v15.MigrateBalancerPoolToSolidlyStable(ctx, gammKeeper, suite.App.BankKeeper, poolID) // check that the pool is now a stable pool stablepool, err := gammKeeper.GetCFMMPool(ctx, poolID) diff --git a/app/upgrades/v15/upgrades.go b/app/upgrades/v15/upgrades.go index d2f0ba48893..1c3d036c87f 100644 --- a/app/upgrades/v15/upgrades.go +++ b/app/upgrades/v15/upgrades.go @@ -1,7 +1,7 @@ package v15 import ( - packetforwardtypes "github.com/strangelove-ventures/packet-forward-middleware/v4/router/types" + packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v4/router/types" poolmanagertypes "github.com/osmosis-labs/osmosis/v17/x/poolmanager/types" @@ -63,7 +63,7 @@ func CreateUpgradeHandler( // Stride stXXX/XXX pools are being migrated from the standard balancer curve to the // solidly stable curve. - migrateBalancerPoolsToSolidlyStable(ctx, keepers.GAMMKeeper, keepers.PoolManagerKeeper, keepers.BankKeeper) + migrateBalancerPoolsToSolidlyStable(ctx, keepers.GAMMKeeper, keepers.BankKeeper) setRateLimits(ctx, keepers.AccountKeeper, keepers.RateLimitingICS4Wrapper, keepers.WasmKeeper) @@ -79,15 +79,15 @@ func setICQParams(ctx sdk.Context, icqKeeper *icqkeeper.Keeper) { icqKeeper.SetParams(ctx, icqparams) } -func migrateBalancerPoolsToSolidlyStable(ctx sdk.Context, gammKeeper *gammkeeper.Keeper, poolmanagerKeeper *poolmanager.Keeper, bankKeeper bankkeeper.Keeper) { +func migrateBalancerPoolsToSolidlyStable(ctx sdk.Context, gammKeeper *gammkeeper.Keeper, bankKeeper bankkeeper.Keeper) { // migrate stOSMO_OSMOPoolId, stJUNO_JUNOPoolId, stSTARS_STARSPoolId pools := []uint64{stOSMO_OSMOPoolId, stJUNO_JUNOPoolId, stSTARS_STARSPoolId} for _, poolId := range pools { - migrateBalancerPoolToSolidlyStable(ctx, gammKeeper, poolmanagerKeeper, bankKeeper, poolId) + migrateBalancerPoolToSolidlyStable(ctx, gammKeeper, bankKeeper, poolId) } } -func migrateBalancerPoolToSolidlyStable(ctx sdk.Context, gammKeeper *gammkeeper.Keeper, poolmanagerKeeper *poolmanager.Keeper, bankKeeper bankkeeper.Keeper, poolId uint64) { +func migrateBalancerPoolToSolidlyStable(ctx sdk.Context, gammKeeper *gammkeeper.Keeper, bankKeeper bankkeeper.Keeper, poolId uint64) { // fetch the pool with the given poolId balancerPool, err := gammKeeper.GetCFMMPool(ctx, poolId) if err != nil { diff --git a/app/upgrades/v16/upgrades.go b/app/upgrades/v16/upgrades.go index 9234578817d..9918a2c2b35 100644 --- a/app/upgrades/v16/upgrades.go +++ b/app/upgrades/v16/upgrades.go @@ -149,7 +149,7 @@ func CreateUpgradeHandler( // Create a full range position via the community pool with the funds that were swapped. fullRangeOsmoDaiCoins := sdk.NewCoins(respectiveOsmo, oneDai) - _, actualOsmoAmtUsed, actualDaiAmtUsed, _, err := keepers.ConcentratedLiquidityKeeper.CreateFullRangePosition(ctx, clPoolId, communityPoolAddress, fullRangeOsmoDaiCoins) + positionData, err := keepers.ConcentratedLiquidityKeeper.CreateFullRangePosition(ctx, clPoolId, communityPoolAddress, fullRangeOsmoDaiCoins) if err != nil { return nil, err } @@ -158,7 +158,7 @@ func CreateUpgradeHandler( // Remove coins we used from the community pool to make the CL position feePool := keepers.DistrKeeper.GetFeePool(ctx) - fulllRangeOsmoDaiCoinsUsed := sdk.NewCoins(sdk.NewCoin(DesiredDenom0, actualOsmoAmtUsed), sdk.NewCoin(DAIIBCDenom, actualDaiAmtUsed)) + fulllRangeOsmoDaiCoinsUsed := sdk.NewCoins(sdk.NewCoin(DesiredDenom0, positionData.Amount0), sdk.NewCoin(DAIIBCDenom, positionData.Amount1)) newPool, negative := feePool.CommunityPool.SafeSub(sdk.NewDecCoinsFromCoins(fulllRangeOsmoDaiCoinsUsed...)) if negative { return nil, fmt.Errorf("community pool cannot be negative: %s", newPool) diff --git a/app/upgrades/v17/constants.go b/app/upgrades/v17/constants.go index b1a8a292c01..a6ae46508e8 100644 --- a/app/upgrades/v17/constants.go +++ b/app/upgrades/v17/constants.go @@ -1,9 +1,16 @@ package v17 import ( + "errors" "fmt" + errorsmod "cosmossdk.io/errors" + "github.com/osmosis-labs/osmosis/v17/app/upgrades" + cltypes "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" + gammtypes "github.com/osmosis-labs/osmosis/v17/x/gamm/types" + poolManagerTypes "github.com/osmosis-labs/osmosis/v17/x/poolmanager/types" + "github.com/osmosis-labs/osmosis/v17/x/superfluid/types" store "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -24,12 +31,13 @@ var Upgrade = upgrades.Upgrade{ } const ( - QuoteAsset = "uosmo" + OSMO = "uosmo" TickSpacing = 100 ) type AssetPair struct { BaseAsset string + QuoteAsset string SpreadFactor sdk.Dec LinkedClassicPool uint64 Superfluid bool @@ -41,7 +49,10 @@ var AssetPairs = []AssetPair{ SpreadFactor: sdk.MustNewDecFromStr("0.0005"), // Normally 0.0002, but is not authorized LinkedClassicPool: 857, }, - {LinkedClassicPool: 712}, + { + LinkedClassicPool: 712, + QuoteAsset: WBTCIBCDenom, // WBTC supersedes OSMO in quote asset status + }, {LinkedClassicPool: 773}, {LinkedClassicPool: 9}, {LinkedClassicPool: 3}, @@ -68,24 +79,37 @@ var AssetPairs = []AssetPair{ } // AssetPairs contract: all AssetPairs being initialized in this upgrade handler all have the same quote asset (OSMO). -func InitializeAssetPairs(ctx sdk.Context, keepers *keepers.AppKeepers) []AssetPair { +func InitializeAssetPairs(ctx sdk.Context, keepers *keepers.AppKeepers) ([]AssetPair, error) { gammKeeper := keepers.GAMMKeeper superfluidKeeper := keepers.SuperfluidKeeper for i, assetPair := range AssetPairs { pool, err := gammKeeper.GetCFMMPool(ctx, assetPair.LinkedClassicPool) if err != nil { - panic(err) + return nil, err } - // Set the base asset as the non-osmo asset in the pool + // Unless the quote asset is manually set above, set the quote asset to OSMO. + if assetPair.QuoteAsset == "" { + assetPair.QuoteAsset = OSMO + AssetPairs[i].QuoteAsset = OSMO + } + + // Set the base asset for the asset pair. poolLiquidity := pool.GetTotalPoolLiquidity(ctx) + found := false for _, coin := range poolLiquidity { - if coin.Denom != QuoteAsset { + if coin.Denom != assetPair.QuoteAsset { AssetPairs[i].BaseAsset = coin.Denom + found = true break } } + // If the quote asset is the only asset in the pool, return an error. + if !found { + return nil, errors.New("quote asset is the only asset in the pool") + } + // If the spread factor is not manually set above, set it to the the same value as the pool's spread factor. if assetPair.SpreadFactor.IsNil() { AssetPairs[i].SpreadFactor = pool.GetSpreadFactor(ctx) @@ -100,7 +124,7 @@ func InitializeAssetPairs(ctx sdk.Context, keepers *keepers.AppKeepers) []AssetP } AssetPairs[i].Superfluid = true } - return AssetPairs + return AssetPairs, nil } // The values below this comment are used strictly for testing. @@ -139,159 +163,299 @@ var ( var AssetPairsForTestsOnly = []AssetPair{ { + QuoteAsset: OSMO, BaseAsset: ISTIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 837, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: CMSTIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.0005"), // Normally 0.0002, but is not authorized LinkedClassicPool: 857, Superfluid: false, }, { - BaseAsset: WBTCIBCDenom, + QuoteAsset: WBTCIBCDenom, + BaseAsset: OSMO, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 712, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: DOTIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 773, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: CROIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 9, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: AKTIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 3, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: AXLIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 812, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: SCRTIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 584, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: STARSIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.003"), LinkedClassicPool: 604, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: JUNOIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.003"), LinkedClassicPool: 497, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: STRDIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 806, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: MARSIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 907, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: ION, SpreadFactor: sdk.MustNewDecFromStr("0.005"), LinkedClassicPool: 1013, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: XPRTIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 15, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: MEDIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 586, Superfluid: false, }, { + QuoteAsset: OSMO, BaseAsset: SOMMIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 627, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: BLDIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 795, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: KAVAIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 730, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: IRISIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 7, Superfluid: false, }, { + QuoteAsset: OSMO, BaseAsset: stIBCXDenom, SpreadFactor: sdk.MustNewDecFromStr("0.003"), LinkedClassicPool: 1039, Superfluid: false, }, { + QuoteAsset: OSMO, BaseAsset: DVPNIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 5, Superfluid: false, }, { + QuoteAsset: OSMO, BaseAsset: BTSGIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 573, Superfluid: false, }, { + QuoteAsset: OSMO, BaseAsset: UMEEIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 641, Superfluid: false, }, { + QuoteAsset: OSMO, BaseAsset: HUAHUAIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 605, Superfluid: true, }, { + QuoteAsset: OSMO, BaseAsset: NCTIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 971, Superfluid: false, }, { + QuoteAsset: OSMO, BaseAsset: GRAVIBCDenom, SpreadFactor: sdk.MustNewDecFromStr("0.002"), LinkedClassicPool: 625, Superfluid: false, }, } + +// InitializeAssetPairsTestnet initializes the asset pairs for the testnet, which is every osmo paired gamm pool with exactly 2 tokens. +func InitializeAssetPairsTestnet(ctx sdk.Context, keepers *keepers.AppKeepers) ([]AssetPair, error) { + superfluidKeeper := keepers.SuperfluidKeeper + testnetAssetPairs := []AssetPair{} + + // Retrieve all GAMM pools on the testnet. + pools, err := keepers.GAMMKeeper.GetPools(ctx) + if err != nil { + return nil, err + } + + for _, pool := range pools { + if pool.GetType() != poolManagerTypes.Balancer { + continue + } + + gammPoolId := pool.GetId() + + // Skip pools that are already linked. + clPoolId, err := keepers.GAMMKeeper.GetLinkedConcentratedPoolID(ctx, gammPoolId) + if err == nil && clPoolId != 0 { + ctx.Logger().Info(fmt.Sprintf("gammPoolId %d is already linked to CL pool %d, skipping", gammPoolId, clPoolId)) + continue + } + + cfmmPool, err := keepers.GAMMKeeper.GetCFMMPool(ctx, gammPoolId) + if err != nil { + return nil, err + } + + totalPoolLiquidity := cfmmPool.GetTotalPoolLiquidity(ctx) + + // Skip pools that are not paired with exactly 2 tokens. + if len(totalPoolLiquidity) != 2 { + continue + } + + // Skip pools that aren't paired with OSMO. OSMO will be the quote asset. + quoteAsset, baseAsset := "", "" + for _, coin := range totalPoolLiquidity { + if coin.Denom == OSMO { + quoteAsset = coin.Denom + } else { + baseAsset = coin.Denom + } + } + if quoteAsset == "" || baseAsset == "" { + continue + } + + spreadFactor := cfmmPool.GetSpreadFactor(ctx) + err = validateSpotPriceFallsInBounds(ctx, cfmmPool, keepers, baseAsset, quoteAsset, spreadFactor) + if err != nil { + continue + } + + // Set the spread factor to the same spread factor the GAMM pool was. + // If its spread factor is not authorized, set it to the first authorized non-zero spread factor. + authorizedSpreadFactors := keepers.ConcentratedLiquidityKeeper.GetParams(ctx).AuthorizedSpreadFactors + spreadFactorAuthorized := false + for _, authorizedSpreadFactor := range authorizedSpreadFactors { + if authorizedSpreadFactor.Equal(spreadFactor) { + spreadFactorAuthorized = true + break + } + } + if !spreadFactorAuthorized { + spreadFactor = authorizedSpreadFactors[1] + } + + isSuperfluid := false + poolShareDenom := fmt.Sprintf("gamm/pool/%d", gammPoolId) + _, err = superfluidKeeper.GetSuperfluidAsset(ctx, poolShareDenom) + if err != nil && !errors.Is(err, errorsmod.Wrapf(types.ErrNonSuperfluidAsset, "denom: %s", poolShareDenom)) { + return nil, err + } else if err == nil { + isSuperfluid = true + } + + internalAssetPair := AssetPair{ + QuoteAsset: quoteAsset, + BaseAsset: baseAsset, + SpreadFactor: spreadFactor, + LinkedClassicPool: gammPoolId, + Superfluid: isSuperfluid, + } + testnetAssetPairs = append(testnetAssetPairs, internalAssetPair) + } + return testnetAssetPairs, nil +} + +// validateSpotPriceFallsInBounds ensures that after swapping in the OSMO for the baseAsset, the resulting spot price is within the +// min and max spot price bounds of the concentrated liquidity module. +func validateSpotPriceFallsInBounds(ctx sdk.Context, cfmmPool gammtypes.CFMMPoolI, keepers *keepers.AppKeepers, baseAsset, quoteAsset string, spreadFactor sdk.Dec) error { + // Check if swapping 0.1 OSMO results in a spot price less than the min or greater than the max + var respectiveBaseAsset sdk.Coin + var err error + if baseAsset == OSMO { + respectiveBaseAsset, err = keepers.GAMMKeeper.CalcOutAmtGivenIn(ctx, cfmmPool, sdk.NewCoin(baseAsset, sdk.NewInt(100000)), quoteAsset, spreadFactor) + } else if quoteAsset == OSMO { + respectiveBaseAsset, err = keepers.GAMMKeeper.CalcOutAmtGivenIn(ctx, cfmmPool, sdk.NewCoin(quoteAsset, sdk.NewInt(100000)), baseAsset, spreadFactor) + } else { + return fmt.Errorf("expected one of the pool's assets to be OSMO, got %s and %s", baseAsset, quoteAsset) + } + if err != nil { + return err + } + expectedSpotPriceFromSwap := sdk.NewDec(100000).Quo(respectiveBaseAsset.Amount.ToDec()) + if expectedSpotPriceFromSwap.LT(cltypes.MinSpotPrice) || expectedSpotPriceFromSwap.GT(cltypes.MaxSpotPrice) { + return fmt.Errorf("expected spot price from swap to be between %s and %s, got %s", cltypes.MinSpotPrice, cltypes.MaxSpotPrice, expectedSpotPriceFromSwap) + } + return nil +} diff --git a/app/upgrades/v17/upgrades.go b/app/upgrades/v17/upgrades.go index f01cb002a83..8405158a7b9 100644 --- a/app/upgrades/v17/upgrades.go +++ b/app/upgrades/v17/upgrades.go @@ -1,9 +1,13 @@ package v17 import ( + "errors" "fmt" "time" + ibchookstypes "github.com/osmosis-labs/osmosis/x/ibc-hooks/types" + + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" @@ -11,14 +15,25 @@ import ( distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" cltypes "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" + gammtypes "github.com/osmosis-labs/osmosis/v17/x/gamm/types" gammmigration "github.com/osmosis-labs/osmosis/v17/x/gamm/types/migration" superfluidtypes "github.com/osmosis-labs/osmosis/v17/x/superfluid/types" "github.com/osmosis-labs/osmosis/v17/app/keepers" "github.com/osmosis-labs/osmosis/v17/app/upgrades" "github.com/osmosis-labs/osmosis/v17/x/protorev/types" + + poolmanagertypes "github.com/osmosis-labs/osmosis/v17/x/poolmanager/types" ) +const ( + mainnetChainID = "osmosis-1" + e2eChainA = "osmo-test-a" + e2eChainB = "osmo-test-b" +) + +var notEnoughLiquidityForSwapErr = errorsmod.Wrapf(gammtypes.ErrInvalidMathApprox, "token amount must be positive") + func CreateUpgradeHandler( mm *module.Manager, configurator module.Configurator, @@ -26,6 +41,8 @@ func CreateUpgradeHandler( keepers *keepers.AppKeepers, ) upgradetypes.UpgradeHandler { return func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + var assetPairs []AssetPair + // Run migrations before applying any other state changes. // NOTE: DO NOT PUT ANY STATE CHANGES BEFORE RunMigrations(). migrations, err := mm.RunMigrations(ctx, configurator, fromVM) @@ -33,73 +50,61 @@ func CreateUpgradeHandler( return nil, err } - // Get community pool address. - communityPoolAddress := keepers.AccountKeeper.GetModuleAddress(distrtypes.ModuleName) + // get all the existing CL pools + pools, err := keepers.ConcentratedLiquidityKeeper.GetPools(ctx) + if err != nil { + return nil, err + } - // fullRangeCoinsUsed tracks the coins we use in the below for loop from the community pool to create the full range position for each new pool. - fullRangeCoinsUsed := sdk.NewCoins() + // migrate twap records for CL Pools + err = flipTwapSpotPriceRecords(ctx, pools, keepers) + if err != nil { + return nil, err + } - poolLinks := []gammmigration.BalancerToConcentratedPoolLink{} + // Set the asset pair list depending on the chain ID. + if ctx.ChainID() == mainnetChainID || ctx.ChainID() == e2eChainA || ctx.ChainID() == e2eChainB { + // Upgrades specific balancer pools to concentrated liquidity pools and links them to their CL equivalent. + ctx.Logger().Info(fmt.Sprintf("Chain ID is %s, running mainnet upgrade handler", ctx.ChainID())) + assetPairs, err = InitializeAssetPairs(ctx, keepers) + } else { + // Upgrades all existing balancer pools to concentrated liquidity pools and links them to their CL equivalent. + ctx.Logger().Info(fmt.Sprintf("Chain ID is %s, running testnet upgrade handler", ctx.ChainID())) + assetPairs, err = InitializeAssetPairsTestnet(ctx, keepers) + } + if err != nil { + return nil, err + } - assetPairs := InitializeAssetPairs(ctx, keepers) + // Get community pool address. + communityPoolAddress := keepers.AccountKeeper.GetModuleAddress(distrtypes.ModuleName) + poolLinks := []gammmigration.BalancerToConcentratedPoolLink{} + fullRangeCoinsUsed := sdk.Coins{} for _, assetPair := range assetPairs { - // Create a concentrated liquidity pool for asset pair. - clPool, err := keepers.GAMMKeeper.CreateConcentratedPoolFromCFMM(ctx, assetPair.LinkedClassicPool, assetPair.BaseAsset, assetPair.SpreadFactor, TickSpacing) - if err != nil { - return nil, err - } - clPoolId := clPool.GetId() - clPoolDenom := cltypes.GetConcentratedLockupDenomFromPoolId(clPoolId) - - // Add the pool link to the list of pool links (we set them all at once later) - poolLinks = append(poolLinks, gammmigration.BalancerToConcentratedPoolLink{ - BalancerPoolId: assetPair.LinkedClassicPool, - ClPoolId: clPoolId, - }) - - // Determine the amount of baseAsset that can be bought with 1 OSMO. - oneOsmo := sdk.NewCoin(QuoteAsset, sdk.NewInt(1000000)) - linkedClassicPool, err := keepers.PoolManagerKeeper.GetPool(ctx, assetPair.LinkedClassicPool) - if err != nil { - return nil, err - } - respectiveBaseAsset, err := keepers.GAMMKeeper.CalcOutAmtGivenIn(ctx, linkedClassicPool, oneOsmo, assetPair.BaseAsset, sdk.ZeroDec()) - if err != nil { + clPoolDenom, clPoolId, poolLink, coinsUsed, err := createCLPoolWithCommunityPoolPosition(ctx, keepers, assetPair, communityPoolAddress) + if errors.Is(err, notEnoughLiquidityForSwapErr) { + continue + } else if err != nil { return nil, err } - // Create a full range position via the community pool with the funds we calculated above. - fullRangeCoins := sdk.NewCoins(respectiveBaseAsset, oneOsmo) - _, actualBaseAmtUsed, actualQuoteAmtUsed, _, err := keepers.ConcentratedLiquidityKeeper.CreateFullRangePosition(ctx, clPoolId, communityPoolAddress, fullRangeCoins) - if err != nil { - return nil, err - } - - // Track the coins used to create the full range position (we manually update the fee pool later all at once). - fullRangeCoinsUsed = fullRangeCoinsUsed.Add(sdk.NewCoins(sdk.NewCoin(QuoteAsset, actualQuoteAmtUsed), sdk.NewCoin(assetPair.BaseAsset, actualBaseAmtUsed))...) + // Track pool link created and coins used for the community pool. + poolLinks = append(poolLinks, poolLink) + fullRangeCoinsUsed = fullRangeCoinsUsed.Add(coinsUsed...) - // If pair was previously superfluid enabled, add the cl pool's full range denom as an authorized superfluid asset. if assetPair.Superfluid { - superfluidAsset := superfluidtypes.SuperfluidAsset{ - Denom: clPoolDenom, - AssetType: superfluidtypes.SuperfluidAssetTypeConcentratedShare, - } - err = keepers.SuperfluidKeeper.AddNewSuperfluidAsset(ctx, superfluidAsset) + ctx.Logger().Info(fmt.Sprintf("gammPoolId %d is superfluid enabled, enabling %s as a superfluid asset", assetPair.LinkedClassicPool, clPoolDenom)) + err := authorizeSuperfluid(ctx, keepers, clPoolDenom) if err != nil { return nil, err } } - clPoolTwapRecords, err := keepers.TwapKeeper.GetAllMostRecentRecordsForPool(ctx, clPoolId) + err = manuallySetTWAPRecords(ctx, keepers, clPoolId) if err != nil { return nil, err } - - for _, twapRecord := range clPoolTwapRecords { - twapRecord.LastErrorTime = time.Time{} - keepers.TwapKeeper.StoreNewRecord(ctx, twapRecord) - } } // Set the migration links in x/gamm. @@ -122,14 +127,138 @@ func CreateUpgradeHandler( feePool.CommunityPool = newPool keepers.DistrKeeper.SetFeePool(ctx, feePool) + // Set ibc-hooks params + keepers.IBCHooksKeeper.SetParams(ctx, ibchookstypes.DefaultParams()) + // Reset the pool weights upon upgrade. This will add support for CW pools on ProtoRev. - keepers.ProtoRevKeeper.SetPoolWeights(ctx, types.PoolWeights{ - BalancerWeight: 1, - StableWeight: 4, - ConcentratedWeight: 300, - CosmwasmWeight: 300, - }) + keepers.ProtoRevKeeper.SetInfoByPoolType(ctx, types.DefaultPoolTypeInfo) return migrations, nil } } + +// createCLPoolWithCommunityPoolPosition creates a CL pool for a given balancer pool and adds a full range position with the community pool. +func createCLPoolWithCommunityPoolPosition(ctx sdk.Context, keepers *keepers.AppKeepers, assetPair AssetPair, communityPoolAddress sdk.AccAddress) (clPoolDenom string, clPoolId uint64, poolLink gammmigration.BalancerToConcentratedPoolLink, coinsUsed sdk.Coins, err error) { + // Determine if base or quote asset is OSMO and save the non-OSMO asset. + osmoIn := sdk.NewCoin(OSMO, sdk.NewInt(100000)) + nonOsmoAsset := "" + if assetPair.BaseAsset != OSMO { + nonOsmoAsset = assetPair.BaseAsset + } else { + nonOsmoAsset = assetPair.QuoteAsset + } + + // Check if classic pool has enough liquidity to support a 0.1 OSMO swap before creating a CL pool. + // If not, skip the pool. + linkedClassicPool, err := keepers.PoolManagerKeeper.GetPool(ctx, assetPair.LinkedClassicPool) + if err != nil { + return "", 0, gammmigration.BalancerToConcentratedPoolLink{}, nil, err + } + _, err = keepers.GAMMKeeper.CalcOutAmtGivenIn(ctx, linkedClassicPool, osmoIn, nonOsmoAsset, assetPair.SpreadFactor) + if err != nil { + return "", 0, gammmigration.BalancerToConcentratedPoolLink{}, nil, err + } + + // Create a concentrated liquidity pool for asset pair. + ctx.Logger().Info(fmt.Sprintf("Creating CL pool from poolID (%d), baseAsset (%s), quoteAsset (%s) spreadFactor (%s), tickSpacing (%d)", assetPair.LinkedClassicPool, assetPair.BaseAsset, assetPair.QuoteAsset, assetPair.SpreadFactor, TickSpacing)) + clPool, err := keepers.GAMMKeeper.CreateConcentratedPoolFromCFMM(ctx, assetPair.LinkedClassicPool, assetPair.BaseAsset, assetPair.SpreadFactor, TickSpacing) + if err != nil { + return "", 0, gammmigration.BalancerToConcentratedPoolLink{}, nil, err + } + clPoolId = clPool.GetId() + clPoolDenom = cltypes.GetConcentratedLockupDenomFromPoolId(clPoolId) + + // Create pool link object. + poolLink = gammmigration.BalancerToConcentratedPoolLink{ + BalancerPoolId: assetPair.LinkedClassicPool, + ClPoolId: clPoolId, + } + + // Get community pool balance before swap and position creation + commPoolBalanceBaseAssetPre := keepers.BankKeeper.GetBalance(ctx, communityPoolAddress, assetPair.BaseAsset) + commPoolBalanceQuoteAssetPre := keepers.BankKeeper.GetBalance(ctx, communityPoolAddress, assetPair.QuoteAsset) + commPoolBalancePre := sdk.NewCoins(commPoolBalanceBaseAssetPre, commPoolBalanceQuoteAssetPre) + + // Swap 0.1 OSMO for nonOsmoAsset from the community pool. + respectiveNonOsmoAssetInt, err := keepers.GAMMKeeper.SwapExactAmountIn(ctx, communityPoolAddress, linkedClassicPool, osmoIn, nonOsmoAsset, sdk.ZeroInt(), linkedClassicPool.GetSpreadFactor(ctx)) + if err != nil { + return "", 0, gammmigration.BalancerToConcentratedPoolLink{}, nil, err + } + ctx.Logger().Info(fmt.Sprintf("Swapped %s for %s%s from the community pool", osmoIn.String(), respectiveNonOsmoAssetInt.String(), nonOsmoAsset)) + + respectiveNonOsmoAsset := sdk.NewCoin(nonOsmoAsset, respectiveNonOsmoAssetInt) + + // Create a full range position via the community pool with the funds we calculated above. + fullRangeCoins := sdk.NewCoins(respectiveNonOsmoAsset, osmoIn) + _, err = keepers.ConcentratedLiquidityKeeper.CreateFullRangePosition(ctx, clPoolId, communityPoolAddress, fullRangeCoins) + if err != nil { + return "", 0, gammmigration.BalancerToConcentratedPoolLink{}, nil, err + } + + // Get community pool balance after swap and position creation + commPoolBalanceBaseAssetPost := keepers.BankKeeper.GetBalance(ctx, communityPoolAddress, assetPair.BaseAsset) + commPoolBalanceQuoteAssetPost := keepers.BankKeeper.GetBalance(ctx, communityPoolAddress, assetPair.QuoteAsset) + commPoolBalancePost := sdk.NewCoins(commPoolBalanceBaseAssetPost, commPoolBalanceQuoteAssetPost) + + // While we can be fairly certain the diff between these two is 0.2 OSMO, if for whatever reason + // some baseAsset dust remains in the community pool and we don't account for it, when updating the + // fee pool balance later, we will be off by that amount and will cause a panic. + coinsUsed = commPoolBalancePre.Sub(commPoolBalancePost) + + return clPoolDenom, clPoolId, poolLink, coinsUsed, nil +} + +// authorizeSuperfluid authorizes superfluid for the provided CL pool. +func authorizeSuperfluid(ctx sdk.Context, keepers *keepers.AppKeepers, clPoolDenom string) (err error) { + superfluidAsset := superfluidtypes.SuperfluidAsset{ + Denom: clPoolDenom, + AssetType: superfluidtypes.SuperfluidAssetTypeConcentratedShare, + } + return keepers.SuperfluidKeeper.AddNewSuperfluidAsset(ctx, superfluidAsset) +} + +// manuallySetTWAPRecords manually sets the TWAP records for a CL pool. This prevents a panic when the CL pool is first used. +func manuallySetTWAPRecords(ctx sdk.Context, keepers *keepers.AppKeepers, clPoolId uint64) error { + ctx.Logger().Info(fmt.Sprintf("manually setting twap record for newly created CL poolID %d", clPoolId)) + clPoolTwapRecords, err := keepers.TwapKeeper.GetAllMostRecentRecordsForPool(ctx, clPoolId) + if err != nil { + return err + } + + for _, twapRecord := range clPoolTwapRecords { + twapRecord.LastErrorTime = time.Time{} + keepers.TwapKeeper.StoreNewRecord(ctx, twapRecord) + } + return nil +} + +// flipTwapSpotPriceRecords flips the denoms and spot price of twap record of a given pool. +func flipTwapSpotPriceRecords(ctx sdk.Context, pools []poolmanagertypes.PoolI, keepers *keepers.AppKeepers) error { + for _, pool := range pools { + poolId := pool.GetId() + twapRecordHistoricalPoolIndexed, err := keepers.TwapKeeper.GetAllHistoricalPoolIndexedTWAPsForPoolId(ctx, poolId) + if err != nil { + return err + } + + for _, historicalTwapRecord := range twapRecordHistoricalPoolIndexed { + oldRecord := historicalTwapRecord + historicalTwapRecord.P0LastSpotPrice, historicalTwapRecord.P1LastSpotPrice = oldRecord.P1LastSpotPrice, oldRecord.P0LastSpotPrice + + keepers.TwapKeeper.StoreHistoricalTWAP(ctx, historicalTwapRecord) + } + + clPoolTwapRecords, err := keepers.TwapKeeper.GetAllMostRecentRecordsForPool(ctx, poolId) + if err != nil { + return err + } + + for _, twapRecord := range clPoolTwapRecords { + oldRecord := twapRecord + twapRecord.P0LastSpotPrice, twapRecord.P1LastSpotPrice = oldRecord.P1LastSpotPrice, oldRecord.P0LastSpotPrice + + keepers.TwapKeeper.StoreNewRecord(ctx, twapRecord) + } + } + return nil +} diff --git a/app/upgrades/v17/upgrades_test.go b/app/upgrades/v17/upgrades_test.go index 179eaa1e26b..2e29ea6ea74 100644 --- a/app/upgrades/v17/upgrades_test.go +++ b/app/upgrades/v17/upgrades_test.go @@ -4,10 +4,10 @@ import ( "fmt" "sort" "testing" + "time" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/store/prefix" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/stretchr/testify/suite" @@ -19,6 +19,8 @@ import ( v17 "github.com/osmosis-labs/osmosis/v17/app/upgrades/v17" cltypes "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" poolmanagertypes "github.com/osmosis-labs/osmosis/v17/x/poolmanager/types" + superfluidtypes "github.com/osmosis-labs/osmosis/v17/x/superfluid/types" + "github.com/osmosis-labs/osmosis/v17/x/twap/types" ) type UpgradeTestSuite struct { @@ -54,15 +56,33 @@ func dummyUpgrade(suite *UpgradeTestSuite) { suite.Ctx = suite.Ctx.WithBlockHeight(dummyUpgradeHeight) } -func (suite *UpgradeTestSuite) TestUpgrade() { - upgradeSetup := func() { - // This is done to ensure that we run the InitGenesis() logic for the new modules - upgradeStoreKey := suite.App.AppKeepers.GetKey(upgradetypes.StoreKey) - store := suite.Ctx.KVStore(upgradeStoreKey) - versionStore := prefix.NewStore(store, []byte{upgradetypes.VersionMapByte}) - versionStore.Delete([]byte(cltypes.ModuleName)) +func dummyTwapRecord(poolId uint64, t time.Time, asset0 string, asset1 string, sp0, accum0, accum1, geomAccum sdk.Dec) types.TwapRecord { + return types.TwapRecord{ + PoolId: poolId, + Time: t, + Asset0Denom: asset0, + Asset1Denom: asset1, + + P0LastSpotPrice: sp0, + P1LastSpotPrice: sdk.OneDec().Quo(sp0), + P0ArithmeticTwapAccumulator: accum0, + P1ArithmeticTwapAccumulator: accum1, + GeometricTwapAccumulator: geomAccum, } +} + +func assertTwapFlipped(suite *UpgradeTestSuite, pre, post types.TwapRecord) { + suite.Require().Equal(pre.Asset0Denom, post.Asset0Denom) + suite.Require().Equal(pre.Asset1Denom, post.Asset1Denom) + suite.Require().Equal(pre.P0LastSpotPrice, post.P1LastSpotPrice) + suite.Require().Equal(pre.P1LastSpotPrice, post.P0LastSpotPrice) +} + +func assertEqual(suite *UpgradeTestSuite, pre, post interface{}) { + suite.Require().Equal(pre, post) +} +func (suite *UpgradeTestSuite) TestUpgrade() { // Allow 0.1% margin of error. multiplicativeTolerance := osmomath.ErrTolerance{ MultiplicativeTolerance: sdk.MustNewDecFromStr("0.001"), @@ -70,14 +90,12 @@ func (suite *UpgradeTestSuite) TestUpgrade() { testCases := []struct { name string - pre_upgrade func(sdk.Context, *keepers.AppKeepers) (sdk.Coins, uint64) - upgrade func(sdk.Context, *keepers.AppKeepers, sdk.Coins, uint64) + pre_upgrade func(*keepers.AppKeepers) (sdk.Coins, uint64) + upgrade func(*keepers.AppKeepers, sdk.Coins, uint64) }{ { - "Test that the upgrade succeeds", - func(ctx sdk.Context, keepers *keepers.AppKeepers) (sdk.Coins, uint64) { - upgradeSetup() - + "Test that the upgrade succeeds: mainnet", + func(keepers *keepers.AppKeepers) (sdk.Coins, uint64) { var lastPoolID uint64 // To keep track of the last assigned pool ID // Sort AssetPairs based on LinkedClassicPool values. @@ -95,39 +113,89 @@ func (suite *UpgradeTestSuite) TestUpgrade() { // If LinkedClassicPool is specified, but it's smaller than the current pool ID, // create dummy pools to fill the gap. for lastPoolID+1 < poolID { - poolCoins := sdk.NewCoins(sdk.NewCoin(assetPair.BaseAsset, sdk.NewInt(10000000000)), sdk.NewCoin(v17.QuoteAsset, sdk.NewInt(10000000000))) + poolCoins := sdk.NewCoins(sdk.NewCoin(assetPair.BaseAsset, sdk.NewInt(10000000000)), sdk.NewCoin(assetPair.QuoteAsset, sdk.NewInt(10000000000))) suite.PrepareBalancerPoolWithCoins(poolCoins...) lastPoolID++ } // Now create the pool with the correct pool ID. - poolCoins := sdk.NewCoins(sdk.NewCoin(assetPair.BaseAsset, sdk.NewInt(10000000000)), sdk.NewCoin(v17.QuoteAsset, sdk.NewInt(10000000000))) - poolId := suite.PrepareBalancerPoolWithCoins(poolCoins...) + poolCoins := sdk.NewCoins(sdk.NewCoin(assetPair.BaseAsset, sdk.NewInt(10000000000)), sdk.NewCoin(assetPair.QuoteAsset, sdk.NewInt(10000000000))) + suite.PrepareBalancerPoolWithCoins(poolCoins...) - // Send two of the base asset to the community pool. - twoBaseAsset := sdk.NewCoins(sdk.NewCoin(assetPair.BaseAsset, sdk.NewInt(2000000))) - suite.FundAcc(suite.TestAccs[0], twoBaseAsset) + // 0.1 OSMO used to get the respective base asset amount, 0.1 OSMO used to create the position + osmoIn := sdk.NewCoin(v17.OSMO, sdk.NewInt(100000).MulRaw(2)) - err := suite.App.DistrKeeper.FundCommunityPool(suite.Ctx, twoBaseAsset, suite.TestAccs[0]) - suite.Require().NoError(err) + // Add the amount of osmo that will be used to the expectedCoinsUsedInUpgradeHandler. + expectedCoinsUsedInUpgradeHandler = expectedCoinsUsedInUpgradeHandler.Add(osmoIn) - // Determine approx how much baseAsset will be used from community pool when 1 OSMO used. - oneOsmo := sdk.NewCoin(v17.QuoteAsset, sdk.NewInt(1000000)) - pool, err := suite.App.PoolManagerKeeper.GetPool(suite.Ctx, poolId) - suite.Require().NoError(err) - respectiveBaseAsset, err := suite.App.GAMMKeeper.CalcOutAmtGivenIn(suite.Ctx, pool, oneOsmo, assetPair.BaseAsset, sdk.ZeroDec()) - suite.Require().NoError(err) - - // Add the amount of baseAsset that will be used to the expectedCoinsUsedInUpgradeHandler. - expectedCoinsUsedInUpgradeHandler = expectedCoinsUsedInUpgradeHandler.Add(respectiveBaseAsset) + // Enable the GAMM pool for superfluid if the record says so. + if assetPair.Superfluid { + poolShareDenom := fmt.Sprintf("gamm/pool/%d", assetPair.LinkedClassicPool) + superfluidAsset := superfluidtypes.SuperfluidAsset{ + Denom: poolShareDenom, + AssetType: superfluidtypes.SuperfluidAssetTypeLPShare, + } + suite.App.SuperfluidKeeper.SetSuperfluidAsset(suite.Ctx, superfluidAsset) + } // Update the lastPoolID to the current pool ID. lastPoolID = poolID } - return expectedCoinsUsedInUpgradeHandler, lastPoolID + existingPool := suite.PrepareConcentratedPoolWithCoins("ibc/1480B8FD20AD5FCAE81EA87584D269547DD4D436843C1D20F15E00EB64743EF4", "uosmo") + existingPool2 := suite.PrepareConcentratedPoolWithCoins("akash", "uosmo") + existingBalancerPoolId := suite.PrepareBalancerPoolWithCoins(sdk.NewCoin("atom", sdk.NewInt(10000000000)), sdk.NewCoin("uosmo", sdk.NewInt(10000000000))) + + // create few TWAP records for the pools + t1 := dummyTwapRecord(existingPool.GetId(), time.Now().Add(-time.Hour*24), "ibc/1480B8FD20AD5FCAE81EA87584D269547DD4D436843C1D20F15E00EB64743EF4", "uosmo", sdk.NewDec(10), + sdk.OneDec().MulInt64(10*10), + sdk.OneDec().MulInt64(3), + sdk.ZeroDec()) + + t2 := dummyTwapRecord(existingPool.GetId(), time.Now().Add(-time.Hour*10), "ibc/1480B8FD20AD5FCAE81EA87584D269547DD4D436843C1D20F15E00EB64743EF4", "uosmo", sdk.NewDec(30), + sdk.OneDec().MulInt64(10*10+10), + sdk.OneDec().MulInt64(5), + sdk.ZeroDec()) + + t3 := dummyTwapRecord(existingPool.GetId(), time.Now().Add(-time.Hour), "ibc/1480B8FD20AD5FCAE81EA87584D269547DD4D436843C1D20F15E00EB64743EF4", "uosmo", sdk.NewDec(20), + sdk.OneDec().MulInt64(10*10+10*5), + sdk.OneDec().MulInt64(10), + sdk.ZeroDec()) + + t4 := dummyTwapRecord(existingPool2.GetId(), time.Now().Add(-time.Hour*24), "akash", "uosmo", sdk.NewDec(10), + sdk.OneDec().MulInt64(10*10*10), + sdk.OneDec().MulInt64(5), + sdk.ZeroDec()) + + t5 := dummyTwapRecord(existingPool2.GetId(), time.Now().Add(-time.Hour), "akash", "uosmo", sdk.NewDec(20), + sdk.OneDec().MulInt64(10), + sdk.OneDec().MulInt64(2), + sdk.ZeroDec()) + + t6 := dummyTwapRecord(existingBalancerPoolId, time.Now().Add(-time.Hour), "atom", "uosmo", sdk.NewDec(10), + sdk.OneDec().MulInt64(10), + sdk.OneDec().MulInt64(10), + sdk.ZeroDec()) + + t7 := dummyTwapRecord(existingBalancerPoolId, time.Now().Add(-time.Minute*20), "atom", "uosmo", sdk.NewDec(50), + sdk.OneDec().MulInt64(10*5), + sdk.OneDec().MulInt64(5), + sdk.ZeroDec()) + + // store TWAP records + suite.App.TwapKeeper.StoreNewRecord(suite.Ctx, t1) + suite.App.TwapKeeper.StoreNewRecord(suite.Ctx, t2) + suite.App.TwapKeeper.StoreNewRecord(suite.Ctx, t3) + suite.App.TwapKeeper.StoreNewRecord(suite.Ctx, t4) + suite.App.TwapKeeper.StoreNewRecord(suite.Ctx, t5) + suite.App.TwapKeeper.StoreNewRecord(suite.Ctx, t6) + suite.App.TwapKeeper.StoreNewRecord(suite.Ctx, t7) + + return expectedCoinsUsedInUpgradeHandler, existingBalancerPoolId }, - func(ctx sdk.Context, keepers *keepers.AppKeepers, expectedCoinsUsedInUpgradeHandler sdk.Coins, lastPoolID uint64) { + func(keepers *keepers.AppKeepers, expectedCoinsUsedInUpgradeHandler sdk.Coins, lastPoolID uint64) { + lastPoolIdMinusOne := lastPoolID - 1 + lastPoolIdMinusTwo := lastPoolID - 2 stakingParams := suite.App.StakingKeeper.GetParams(suite.Ctx) stakingParams.BondDenom = "uosmo" suite.App.StakingKeeper.SetParams(suite.Ctx, stakingParams) @@ -136,27 +204,82 @@ func (suite *UpgradeTestSuite) TestUpgrade() { communityPoolAddress := suite.App.AccountKeeper.GetModuleAddress(distrtypes.ModuleName) communityPoolBalancePre := suite.App.BankKeeper.GetAllBalances(suite.Ctx, communityPoolAddress) + numPoolPreUpgrade := suite.App.PoolManagerKeeper.GetNextPoolId(suite.Ctx) - 1 + clPool1TwapRecordPreUpgrade, err := keepers.TwapKeeper.GetAllMostRecentRecordsForPool(suite.Ctx, lastPoolIdMinusTwo) + suite.Require().NoError(err) + + clPool1TwapRecordHistoricalPoolIndexPreUpgrade, err := keepers.TwapKeeper.GetAllHistoricalPoolIndexedTWAPsForPoolId(suite.Ctx, lastPoolIdMinusTwo) + suite.Require().NoError(err) + + clPool2TwapRecordPreUpgrade, err := keepers.TwapKeeper.GetAllMostRecentRecordsForPool(suite.Ctx, lastPoolIdMinusOne) + suite.Require().NoError(err) + + clPool2TwapRecordHistoricalPoolIndexPreUpgrade, err := keepers.TwapKeeper.GetAllHistoricalPoolIndexedTWAPsForPoolId(suite.Ctx, lastPoolIdMinusOne) + suite.Require().NoError(err) + + clPoolsTwapRecordHistoricalTimeIndexPreUpgrade, err := keepers.TwapKeeper.GetAllHistoricalTimeIndexedTWAPs(suite.Ctx) + suite.Require().NoError(err) + // Run upgrade handler. dummyUpgrade(suite) suite.Require().NotPanics(func() { suite.App.BeginBlocker(suite.Ctx, abci.RequestBeginBlock{}) }) + clPool1TwapRecordPostUpgrade, err := keepers.TwapKeeper.GetAllMostRecentRecordsForPool(suite.Ctx, lastPoolIdMinusTwo) + suite.Require().NoError(err) + + clPool1TwapRecordHistoricalPoolIndexPostUpgrade, err := keepers.TwapKeeper.GetAllHistoricalPoolIndexedTWAPsForPoolId(suite.Ctx, lastPoolIdMinusTwo) + suite.Require().NoError(err) + + clPool2TwapRecordPostUpgrade, err := keepers.TwapKeeper.GetAllMostRecentRecordsForPool(suite.Ctx, lastPoolIdMinusOne) + suite.Require().NoError(err) + + clPool2TwapRecordHistoricalPoolIndexPostUpgrade, err := keepers.TwapKeeper.GetAllHistoricalPoolIndexedTWAPsForPoolId(suite.Ctx, lastPoolIdMinusOne) + suite.Require().NoError(err) + + clPoolsTwapRecordHistoricalTimeIndexPostUpgrade, err := keepers.TwapKeeper.GetAllHistoricalTimeIndexedTWAPs(suite.Ctx) + suite.Require().NoError(err) + + // check that all TWAP records aren't empty + suite.Require().NotEmpty(clPool1TwapRecordPostUpgrade) + suite.Require().NotEmpty(clPool1TwapRecordHistoricalPoolIndexPostUpgrade) + suite.Require().NotEmpty(clPool2TwapRecordPostUpgrade) + suite.Require().NotEmpty(clPool2TwapRecordHistoricalPoolIndexPostUpgrade) + suite.Require().NotEmpty(clPoolsTwapRecordHistoricalTimeIndexPostUpgrade) + + for _, data := range []struct { + pre, post []types.TwapRecord + }{ + {clPool1TwapRecordPreUpgrade, clPool1TwapRecordPostUpgrade}, + {clPool1TwapRecordHistoricalPoolIndexPreUpgrade, clPool1TwapRecordHistoricalPoolIndexPostUpgrade}, + {clPool2TwapRecordPreUpgrade, clPool2TwapRecordPostUpgrade}, + {clPool2TwapRecordHistoricalPoolIndexPreUpgrade, clPool2TwapRecordHistoricalPoolIndexPostUpgrade}, + } { + for i := range data.post { + assertTwapFlipped(suite, data.pre[i], data.post[i]) + } + } + + for i := range clPoolsTwapRecordHistoricalTimeIndexPostUpgrade { + record := clPoolsTwapRecordHistoricalTimeIndexPostUpgrade[i] + if record.PoolId == lastPoolIdMinusOne || record.PoolId == lastPoolIdMinusTwo { + assertTwapFlipped(suite, clPoolsTwapRecordHistoricalTimeIndexPreUpgrade[i], record) + } else if record.PoolId == lastPoolID { + assertEqual(suite, clPoolsTwapRecordHistoricalTimeIndexPreUpgrade[i], record) + } + } + // Retrieve the community pool balance (and the feePool balance) after the upgrade communityPoolBalancePost := suite.App.BankKeeper.GetAllBalances(suite.Ctx, communityPoolAddress) feePoolCommunityPoolPost := suite.App.DistrKeeper.GetFeePool(suite.Ctx).CommunityPool - assetPairs := v17.InitializeAssetPairs(ctx, keepers) + assetPairs, err := v17.InitializeAssetPairs(suite.Ctx, keepers) + suite.Require().NoError(err) for i, assetPair := range assetPairs { - // Validate that the community pool balance has been reduced by the amount of baseAsset that was used to create the pool. - suite.Require().Equal(communityPoolBalancePre.AmountOf(assetPair.BaseAsset).Sub(expectedCoinsUsedInUpgradeHandler.AmountOf(assetPair.BaseAsset)).String(), communityPoolBalancePost.AmountOf(assetPair.BaseAsset).String()) - - // Validate that the fee pool community pool balance has been decreased by the amount of baseAsset that was used to create the pool. - suite.Require().Equal(communityPoolBalancePost.AmountOf(assetPair.BaseAsset).String(), feePoolCommunityPoolPost.AmountOf(assetPair.BaseAsset).TruncateInt().String()) - // Get balancer pool's spot price. - balancerSpotPrice, err := suite.App.GAMMKeeper.CalculateSpotPrice(suite.Ctx, assetPair.LinkedClassicPool, v17.QuoteAsset, assetPair.BaseAsset) + balancerSpotPrice, err := suite.App.GAMMKeeper.CalculateSpotPrice(suite.Ctx, assetPair.LinkedClassicPool, assetPair.QuoteAsset, assetPair.BaseAsset) suite.Require().NoError(err) // Validate CL pool was created. @@ -168,7 +291,7 @@ func (suite *UpgradeTestSuite) TestUpgrade() { concentratedTypePool, ok := concentratedPool.(cltypes.ConcentratedPoolExtension) suite.Require().True(ok) suite.Require().Equal(assetPair.BaseAsset, concentratedTypePool.GetToken0()) - suite.Require().Equal(v17.QuoteAsset, concentratedTypePool.GetToken1()) + suite.Require().Equal(assetPair.QuoteAsset, concentratedTypePool.GetToken1()) // Validate that the spot price of the CL pool is what we expect suite.Require().Equal(0, multiplicativeTolerance.CompareBigDec(concentratedTypePool.GetCurrentSqrtPrice().PowerInteger(2), osmomath.BigDecFromSDKDec(balancerSpotPrice))) @@ -192,9 +315,16 @@ func (suite *UpgradeTestSuite) TestUpgrade() { lastPoolID++ } - // Check osmo balance (was used in every pool creation) - suite.Require().Equal(0, multiplicativeTolerance.Compare(communityPoolBalancePre.AmountOf(v17.QuoteAsset), communityPoolBalancePost.AmountOf(v17.QuoteAsset).Sub(expectedCoinsUsedInUpgradeHandler.AmountOf(v17.QuoteAsset)))) - suite.Require().Equal(communityPoolBalancePost.AmountOf(v17.QuoteAsset).String(), feePoolCommunityPoolPost.AmountOf(v17.QuoteAsset).TruncateInt().String()) + // Validate that the community pool balance has been reduced by the amount of osmo that was used to create the pool. + suite.Require().Equal(communityPoolBalancePre.Sub(expectedCoinsUsedInUpgradeHandler).String(), communityPoolBalancePost.String()) + + // Validate that the fee pool community pool balance has been decreased by the amount of osmo that was used to create the pool. + suite.Require().Equal(sdk.NewDecCoinsFromCoins(communityPoolBalancePost...).String(), feePoolCommunityPoolPost.String()) + + numPoolPostUpgrade := suite.App.PoolManagerKeeper.GetNextPoolId(suite.Ctx) - 1 + + // Number of pools created should be equal to the number of records in the asset pairs. + suite.Require().Equal(len(assetPairs), int(numPoolPostUpgrade-numPoolPreUpgrade)) // Validate that all links were created. migrationInfo, err := suite.App.GAMMKeeper.GetAllMigrationInfo(suite.Ctx) @@ -202,13 +332,186 @@ func (suite *UpgradeTestSuite) TestUpgrade() { suite.Require().NoError(err) }, }, + { + "Test that the upgrade succeeds: testnet", + func(keepers *keepers.AppKeepers) (sdk.Coins, uint64) { + suite.Ctx = suite.Ctx.WithChainID("osmo-test-5") + + var lastPoolID uint64 // To keep track of the last assigned pool ID + + sort.Sort(ByLinkedClassicPool(v17.AssetPairsForTestsOnly)) + sort.Sort(ByLinkedClassicPool(v17.AssetPairs)) + + expectedCoinsUsedInUpgradeHandler := sdk.NewCoins() + + // Create earlier pools or dummy pools if needed + for _, assetPair := range v17.AssetPairsForTestsOnly { + poolID := assetPair.LinkedClassicPool + + // For testnet, we create a CL pool for ANY balancer pool. + // The only thing we use the assetPair list here for to select some pools to enable superfluid for. + for lastPoolID+1 < poolID { + poolCoins := sdk.NewCoins(sdk.NewCoin(assetPair.BaseAsset, sdk.NewInt(10000000000)), sdk.NewCoin(assetPair.QuoteAsset, sdk.NewInt(10000000000))) + suite.PrepareBalancerPoolWithCoins(poolCoins...) + + // 0.1 OSMO used to get the respective base asset amount, 0.1 OSMO used to create the position + osmoIn := sdk.NewCoin(v17.OSMO, sdk.NewInt(100000).MulRaw(2)) + + // Add the amount of osmo that will be used to the expectedCoinsUsedInUpgradeHandler. + expectedCoinsUsedInUpgradeHandler = expectedCoinsUsedInUpgradeHandler.Add(osmoIn) + + lastPoolID++ + } + + // Enable the GAMM pool for superfluid if the asset pair is marked as superfluid. + if assetPair.Superfluid { + poolShareDenom := fmt.Sprintf("gamm/pool/%d", assetPair.LinkedClassicPool) + superfluidAsset := superfluidtypes.SuperfluidAsset{ + Denom: poolShareDenom, + AssetType: superfluidtypes.SuperfluidAssetTypeLPShare, + } + suite.App.SuperfluidKeeper.SetSuperfluidAsset(suite.Ctx, superfluidAsset) + } + } + + // We now create various pools that are not balancer pools. + // This is to test if the testnet upgrade handler properly handles pools that are not of type balancer (i.e. should ignore them and move on). + + // Stableswap pool + suite.CreatePoolFromType(poolmanagertypes.Stableswap) + // Cosmwasm pool + suite.CreatePoolFromType(poolmanagertypes.CosmWasm) + // CL pool + suite.CreatePoolFromType(poolmanagertypes.Concentrated) + + lastPoolID += 3 + + return expectedCoinsUsedInUpgradeHandler, lastPoolID + }, + func(keepers *keepers.AppKeepers, expectedCoinsUsedInUpgradeHandler sdk.Coins, lastPoolID uint64) { + // Set the bond denom to uosmo + stakingParams := suite.App.StakingKeeper.GetParams(suite.Ctx) + stakingParams.BondDenom = "uosmo" + suite.App.StakingKeeper.SetParams(suite.Ctx, stakingParams) + + // Retrieve the community pool balance before the upgrade + communityPoolAddress := suite.App.AccountKeeper.GetModuleAddress(distrtypes.ModuleName) + communityPoolBalancePre := suite.App.BankKeeper.GetAllBalances(suite.Ctx, communityPoolAddress) + + numPoolPreUpgrade := suite.App.PoolManagerKeeper.GetNextPoolId(suite.Ctx) - 1 + + gammPoolsPreUpgrade, err := suite.App.GAMMKeeper.GetPools(suite.Ctx) + suite.Require().NoError(err) + + // Run upgrade handler. + dummyUpgrade(suite) + suite.Require().NotPanics(func() { + suite.App.BeginBlocker(suite.Ctx, abci.RequestBeginBlock{}) + }) + + // Retrieve the community pool balance (and the feePool balance) after the upgrade + communityPoolBalancePost := suite.App.BankKeeper.GetAllBalances(suite.Ctx, communityPoolAddress) + feePoolCommunityPoolPost := suite.App.DistrKeeper.GetFeePool(suite.Ctx).CommunityPool + + indexOffset := int(0) + assetListIndex := int(0) + + // For testnet, we run through all gamm pools (not just the asset list) + for i, pool := range gammPoolsPreUpgrade { + // Skip pools that are not balancer pools + if pool.GetType() != poolmanagertypes.Balancer { + indexOffset++ + continue + } + + gammPoolId := pool.GetId() + cfmmPool, err := keepers.GAMMKeeper.GetCFMMPool(suite.Ctx, gammPoolId) + suite.Require().NoError(err) + + poolCoins := cfmmPool.GetTotalPoolLiquidity(suite.Ctx) + + // Retrieve quoteAsset and baseAsset from the poolCoins + quoteAsset, baseAsset := "", "" + for _, coin := range poolCoins { + if coin.Denom == v17.OSMO { + quoteAsset = coin.Denom + } else { + baseAsset = coin.Denom + } + } + if quoteAsset == "" || baseAsset == "" { + indexOffset++ + continue + } + + // Get balancer pool's spot price. + balancerSpotPrice, err := suite.App.GAMMKeeper.CalculateSpotPrice(suite.Ctx, gammPoolId, quoteAsset, baseAsset) + suite.Require().NoError(err) + + // Validate CL pool was created. + concentratedPool, err := suite.App.PoolManagerKeeper.GetPool(suite.Ctx, lastPoolID+1) + suite.Require().NoError(err) + suite.Require().Equal(poolmanagertypes.Concentrated, concentratedPool.GetType()) + + // Validate that denom0 and denom1 were set correctly + concentratedTypePool, ok := concentratedPool.(cltypes.ConcentratedPoolExtension) + suite.Require().True(ok) + suite.Require().Equal(baseAsset, concentratedTypePool.GetToken0()) + suite.Require().Equal(quoteAsset, concentratedTypePool.GetToken1()) + + // Validate that the spot price of the CL pool is what we expect + suite.Require().Equal(0, multiplicativeTolerance.CompareBigDec(concentratedTypePool.GetCurrentSqrtPrice().PowerInteger(2), osmomath.BigDecFromSDKDec(balancerSpotPrice))) + + // Validate that the link is correct. + migrationInfo, err := suite.App.GAMMKeeper.GetAllMigrationInfo(suite.Ctx) + link := migrationInfo.BalancerToConcentratedPoolLinks[i-indexOffset] + suite.Require().Equal(gammPoolId, link.BalancerPoolId) + suite.Require().Equal(concentratedPool.GetId(), link.ClPoolId) + + // Validate the sfs status. + // If the poolId matches a poolId on that asset list that had superfluid enabled, this pool should also be superfluid enabled. + // Otherwise, it should not be superfluid enabled. + assetListPoolId := v17.AssetPairsForTestsOnly[assetListIndex].LinkedClassicPool + clPoolDenom := cltypes.GetConcentratedLockupDenomFromPoolId(concentratedPool.GetId()) + _, err = suite.App.SuperfluidKeeper.GetSuperfluidAsset(suite.Ctx, clPoolDenom) + if assetListPoolId == gammPoolId { + suite.Require().NoError(err) + assetListIndex++ + for assetListIndex < len(v17.AssetPairsForTestsOnly)-1 && v17.AssetPairsForTestsOnly[assetListIndex].Superfluid == false { + assetListIndex++ + } + } else { + suite.Require().Error(err) + } + + lastPoolID++ + } + + // Validate that the community pool balance has been reduced by the amount of osmo that was used to create the pool. + suite.Require().Equal(communityPoolBalancePre.Sub(expectedCoinsUsedInUpgradeHandler).String(), communityPoolBalancePost.String()) + + // Validate that the fee pool community pool balance has been decreased by the amount of osmo that was used to create the pool. + suite.Require().Equal(sdk.NewDecCoinsFromCoins(communityPoolBalancePost...).String(), feePoolCommunityPoolPost.String()) + + numPoolPostUpgrade := suite.App.PoolManagerKeeper.GetNextPoolId(suite.Ctx) - 1 + numPoolsCreated := numPoolPostUpgrade - numPoolPreUpgrade + + // Number of pools created should be equal to the number of pools preUpgrade minus the number of pools that were not eligible for migration. + numPoolsEligibleForMigration := numPoolPreUpgrade - 3 + suite.Require().Equal(int(numPoolsEligibleForMigration), int(numPoolsCreated)) + + // Validate that all links were created. + migrationInfo, err := suite.App.GAMMKeeper.GetAllMigrationInfo(suite.Ctx) + suite.Require().Equal(int(numPoolsEligibleForMigration), len(migrationInfo.BalancerToConcentratedPoolLinks)) + suite.Require().NoError(err) + }, + }, { "Fails because CFMM pool is not found", - func(ctx sdk.Context, keepers *keepers.AppKeepers) (sdk.Coins, uint64) { - upgradeSetup() + func(keepers *keepers.AppKeepers) (sdk.Coins, uint64) { return sdk.NewCoins(), 0 }, - func(ctx sdk.Context, keepers *keepers.AppKeepers, expectedCoinsUsedInUpgradeHandler sdk.Coins, lastPoolID uint64) { + func(keepers *keepers.AppKeepers, expectedCoinsUsedInUpgradeHandler sdk.Coins, lastPoolID uint64) { dummyUpgrade(suite) suite.Require().Panics(func() { suite.App.BeginBlocker(suite.Ctx, abci.RequestBeginBlock{}) @@ -221,8 +524,8 @@ func (suite *UpgradeTestSuite) TestUpgrade() { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { suite.SetupTest() // reset - expectedCoinsUsedInUpgradeHandler, lastPoolID := tc.pre_upgrade(suite.Ctx, &suite.App.AppKeepers) - tc.upgrade(suite.Ctx, &suite.App.AppKeepers, expectedCoinsUsedInUpgradeHandler, lastPoolID) + expectedCoinsUsedInUpgradeHandler, lastPoolID := tc.pre_upgrade(&suite.App.AppKeepers) + tc.upgrade(&suite.App.AppKeepers, expectedCoinsUsedInUpgradeHandler, lastPoolID) }) } } diff --git a/app/upgrades/v18/constants.go b/app/upgrades/v18/constants.go new file mode 100644 index 00000000000..73c9b440825 --- /dev/null +++ b/app/upgrades/v18/constants.go @@ -0,0 +1,19 @@ +package v18 + +import ( + "github.com/osmosis-labs/osmosis/v17/app/upgrades" + + store "github.com/cosmos/cosmos-sdk/store/types" +) + +// UpgradeName defines the on-chain upgrade name for the Osmosis v18 upgrade. +const UpgradeName = "v18" + +var Upgrade = upgrades.Upgrade{ + UpgradeName: UpgradeName, + CreateUpgradeHandler: CreateUpgradeHandler, + StoreUpgrades: store.StoreUpgrades{ + Added: []string{}, + Deleted: []string{}, + }, +} diff --git a/app/upgrades/v18/upgrades.go b/app/upgrades/v18/upgrades.go new file mode 100644 index 00000000000..b35ce162a23 --- /dev/null +++ b/app/upgrades/v18/upgrades.go @@ -0,0 +1,28 @@ +package v18 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + "github.com/osmosis-labs/osmosis/v17/app/keepers" + "github.com/osmosis-labs/osmosis/v17/app/upgrades" +) + +func CreateUpgradeHandler( + mm *module.Manager, + configurator module.Configurator, + bpm upgrades.BaseAppParamManager, + keepers *keepers.AppKeepers, +) upgradetypes.UpgradeHandler { + return func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + // Run migrations before applying any other state changes. + // NOTE: DO NOT PUT ANY STATE CHANGES BEFORE RunMigrations(). + migrations, err := mm.RunMigrations(ctx, configurator, fromVM) + if err != nil { + return nil, err + } + + return migrations, nil + } +} diff --git a/cmd/osmosisd/cmd/change_environment.go b/cmd/osmosisd/cmd/change_environment.go index 56c51d18389..6fa28173ebf 100644 --- a/cmd/osmosisd/cmd/change_environment.go +++ b/cmd/osmosisd/cmd/change_environment.go @@ -1,10 +1,13 @@ package cmd import ( + "errors" "fmt" "os" "path/filepath" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/joho/godotenv" "github.com/spf13/cobra" @@ -14,7 +17,8 @@ import ( const ( EnvVariable = "OSMOSISD_ENVIRONMENT" EnvMainnet = "mainnet" - EnvLocalnet = "localosmosis" + EnvTestnet = "testnet" + EnvLocalnet = "localnet" ) // ExportAirdropSnapshotCmd generates a snapshot.json from a provided exported genesis.json. @@ -25,29 +29,22 @@ func ChangeEnvironmentCmd() *cobra.Command { Long: `Set home environment variables for commands Example: osmosisd set-env mainnet - osmosisd set-env localosmosis + osmosisd set-env testnet + osmosisd set-env localnet [optional-chain-id] osmosisd set-env $HOME/.custom-dir `, - Args: cobra.ExactArgs(1), + Args: customArgs, RunE: func(cmd *cobra.Command, args []string) error { + // Note: If we are calling this method, the environment file has already been set in + // NewRootCmd() when creating the rootCmd. We do this because order of operations + // dictates this as a requirement. If we changed the env file here, the osmosis + // daemon would not initialize the folder we are intending to set to. newEnv := args[0] - - currentEnvironment := getHomeEnvironment() - fmt.Println("Current environment: ", currentEnvironment) - - if _, err := environmentNameToPath(newEnv); err != nil { - return err - } - - fmt.Println("New environment: ", newEnv) - - envMap := make(map[string]string) - envMap[EnvVariable] = newEnv - err := godotenv.Write(envMap, filepath.Join(app.DefaultNodeHome, ".env")) - if err != nil { - return err + chainId := "" + if len(args) > 1 { + chainId = args[1] } - return nil + return clientSettingsFromEnv(cmd, newEnv, chainId) }, } return cmd @@ -64,8 +61,8 @@ Example: Returns one of: - mainnet implying $HOME/.osmosisd + - testnet implying $HOME/.osmosisd-test - localosmosis implying $HOME/.osmosisd-local - - localosmosis - custom path`, RunE: func(cmd *cobra.Command, args []string) error { environment := getHomeEnvironment() @@ -91,17 +88,141 @@ func environmentNameToPath(environmentName string) (string, error) { switch environmentName { case EnvMainnet: return app.DefaultNodeHome, nil + case EnvTestnet: + return filepath.Join(userHomeDir, ".osmosisd-test/"), nil case EnvLocalnet: return filepath.Join(userHomeDir, ".osmosisd-local/"), nil default: - osmosisdPath := filepath.Join(userHomeDir, environmentName) - _, err := os.Stat(osmosisdPath) + _, err := os.Stat(environmentName) if os.IsNotExist(err) { // Creating new environment directory - if err := os.Mkdir(osmosisdPath, os.ModePerm); err != nil { + if err := os.Mkdir(environmentName, os.ModePerm); err != nil { return "", err } } - return osmosisdPath, nil + return environmentName, nil + } +} + +// clientSettingsFromEnv takes the env name (mainnet, testnet, localnet, etc) and sets the +// client.toml settings to commonly used values for that environment. +func clientSettingsFromEnv(cmd *cobra.Command, environmentName, chainId string) error { + envConfigs := map[string]map[string]string{ + EnvMainnet: { + flags.FlagChainID: "osmosis-1", + flags.FlagNode: "https://rpc.osmosis.zone:443", + flags.FlagBroadcastMode: "block", + }, + EnvTestnet: { + flags.FlagChainID: "osmo-test-5", + flags.FlagNode: "https://rpc.testnet.osmosis.zone:443", + flags.FlagBroadcastMode: "block", + }, + EnvLocalnet: { + flags.FlagChainID: "localosmosis", + flags.FlagBroadcastMode: "block", + }, + } + + configs, ok := envConfigs[environmentName] + if !ok { + return nil + } + + // Update the ChainID if environmentName is EnvLocalnet and chainId is provided + if environmentName == EnvLocalnet && chainId != "" { + configs[flags.FlagChainID] = chainId + } + + for flag, value := range configs { + if err := runConfigCmd(cmd, []string{flag, value}); err != nil { + return err + } + } + return nil +} + +// changeEnvironment takes the given environment name and changes the .env file to reflect it. +func changeEnvironment(args []string) error { + newEnv := args[0] + + currentEnvironment := getHomeEnvironment() + fmt.Println("Current environment: ", currentEnvironment) + + if _, err := environmentNameToPath(newEnv); err != nil { + return err + } + + fmt.Println("New environment: ", newEnv) + + envMap := make(map[string]string) + envMap[EnvVariable] = newEnv + err := godotenv.Write(envMap, filepath.Join(app.DefaultNodeHome, ".env")) + if err != nil { + return err + } + + return nil +} + +// createHomeDirIfNotExist creates the home directory if it does not exist and writes a blank +// .env file. This is used for the first time setup of the osmosisd home directory. +func createHomeDirIfNotExist(homeDir string) error { + if _, err := os.Stat(homeDir); os.IsNotExist(err) { + err := os.MkdirAll(homeDir, 0755) + if err != nil { + return err + } + } + + envFilePath := filepath.Join(homeDir, ".env") + if _, err := os.Stat(envFilePath); os.IsNotExist(err) { + file, err := os.Create(envFilePath) + if err != nil { + return err + } + file.Close() + } + + return nil +} + +// changeEnvPriorToSetup changes the env file to reflect the desired environment the user wants to change to. +// If this is not called in NewRootCmd(), the environment change will happen **after** all relevant setup actions +// happen (e.g., the .env will be read in as the previous value in the setup and not the new value). +func changeEnvPriorToSetup(cmd *cobra.Command, initClientCtx *client.Context, args []string, homeDir string) error { + if cmd.Name() == "set-env" { + err := createHomeDirIfNotExist(homeDir) + if err != nil { + return err + } + + err = changeEnvironment(args) + if err != nil { + return err + } + + homeEnvironment := getHomeEnvironment() + homeDir, err := environmentNameToPath(homeEnvironment) + if err != nil { + // Failed to convert home environment to home path, using default home + homeDir = app.DefaultNodeHome + } + *initClientCtx = initClientCtx.WithHomeDir(homeDir) + } + return nil +} + +// customArgs accepts one arg, but if the first arg is "localnet", then it accepts two args. +func customArgs(cmd *cobra.Command, args []string) error { + if len(args) < 1 || len(args) > 2 { + return errors.New("set-env requires 1 or 2 arguments") + } + if args[0] == "localnet" { + return nil + } + if len(args) == 2 { + return errors.New("only 'set-env localnet' accepts a second argument") } + return nil } diff --git a/cmd/osmosisd/cmd/config.go b/cmd/osmosisd/cmd/config.go index 19542a2f465..5bed196185a 100644 --- a/cmd/osmosisd/cmd/config.go +++ b/cmd/osmosisd/cmd/config.go @@ -171,19 +171,31 @@ const defaultConfigTemplate = `# This is a TOML config file. # The network chain ID chain-id = "{{ .ChainID }}" + # The keyring's backend, where the keys are stored (os|file|kwallet|pass|test|memory) keyring-backend = "{{ .KeyringBackend }}" + # CLI output format (text|json) output = "{{ .Output }}" + # : to Tendermint RPC interface for this chain node = "{{ .Node }}" + # Transaction broadcasting mode (sync|async) broadcast-mode = "{{ .BroadcastMode }}" -# Human-readable denoms -# If enabled, when using CLI, user can input base denoms (baseatom, basescrt, baseweth, basewbtc, basewbtc.grv etc.) instead of their ibc equivalents. + +# Human-readable denoms: Input +# If enabled, when using CLI, user can input 0 exponent denoms (atom, scrt, avax, wbtc, etc.) instead of their ibc equivalents. +# Note, this will also change the coin's value to it's base value if the input or flag is a coin. +# Example: +# * 10.45atom input will automatically change to 10450000ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2 +# * uakt will change to ibc/1480B8FD20AD5FCAE81EA87584D269547DD4D436843C1D20F15E00EB64743EF4 +# * 12000000uscrt will change to 12000000ibc/0954E1C28EB7AF5B72D24F3BC2B47BBB2FDF91BDDFD57B74B99E133AED40972A # This feature isn't stable yet, and outputs will change in subsequent releases human-readable-denoms-input = {{ .HumanReadableDenomsInput }} -# If enabled, CLI response return base denoms (baseatom, basescrt, baseweth, basewbtc, basewbtc.grv etc.) instead of their ibc equivalents. + +# Human-readable denoms: Output +# If enabled, CLI response return base denoms (uatom, uscrt, wavax-wei, wbtc-satoshi, etc.) instead of their ibc equivalents. # This feature isn't stable yet, and outputs will change in subsequent releases human-readable-denoms-output = {{ .HumanReadableDenomsOutput }} diff --git a/cmd/osmosisd/cmd/root.go b/cmd/osmosisd/cmd/root.go index 5e9a8106822..7fc67884bca 100644 --- a/cmd/osmosisd/cmd/root.go +++ b/cmd/osmosisd/cmd/root.go @@ -67,8 +67,9 @@ type Asset struct { } type DenomUnit struct { - Denom string `json:"denom"` - Exponent uint64 `json:"exponent"` + Denom string `json:"denom"` + Exponent uint64 `json:"exponent"` + Aliases []string `json:"aliases"` } type Trace struct { @@ -79,8 +80,8 @@ type Trace struct { } type DenomUnitMap struct { - Base string - DenomUnits []DenomUnit `json:"denom_units"` + Base string + Exponent uint64 `json:"exponent"` } var ( @@ -143,16 +144,38 @@ func loadAssetList(initClientCtx client.Context, cmd *cobra.Command, basedenomTo if basedenomToIBC { for _, asset := range assetList.Assets { - DenomUnitMap := DenomUnitMap{ - Base: asset.Base, - DenomUnits: asset.DenomUnits, + // Each asset has a list of denom units. A majority of them have 2 entries, one being the base 0 exponent denom and the other being a larger exponent denom. + // An example for tether: + // * Exponent 0: uusdt + // * Exponent 6: usdt + // This implies that if a usdt value is given, in order to convert it to it's base denom (uusdt), we need to multiply the provided value by 10^6. + for i, denomUnit := range asset.DenomUnits { + DenomUnitMap := DenomUnitMap{ + Base: asset.Base, + Exponent: asset.DenomUnits[i].Exponent, + } + // The 0 exponent denom is the base denom. + if asset.DenomUnits[i].Exponent == 0 { + // To make everyone's life harder, some assets have multiple base denom aliases. For example, the asset list has the following base aliases for the asset "luna": + // * uluna + // * microluna + for _, alias := range denomUnit.Aliases { + baseMap[strings.ToLower(alias)] = DenomUnitMap + } + } else { + // Otherwise we just store the denom alias for that exponent. + baseMap[strings.ToLower(denomUnit.Denom)] = DenomUnitMap + } } - baseMap["base"+strings.ToLower(asset.Symbol)] = DenomUnitMap } } if IBCtoBasedenom { + // We just store a link from the first base denom alias to the IBC denom. This is just used for display purposes on the terminal's output. for _, asset := range assetList.Assets { - baseMapRev[asset.Base] = "base" + strings.ToLower(asset.Symbol) + if len(asset.DenomUnits) > 0 && asset.DenomUnits[0].Exponent == 0 && len(asset.DenomUnits[0].Aliases) > 0 { + baseDenom := asset.DenomUnits[0].Aliases[0] + baseMapRev[asset.Base] = strings.ToLower(baseDenom) + } } } return baseMap, baseMapRev @@ -264,6 +287,12 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) { Use: "osmosisd", Short: "Start osmosis app", PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + // If not calling the set-env command, this is a no-op. + err := changeEnvPriorToSetup(cmd, &initClientCtx, args, homeDir) + if err != nil { + return err + } + initClientCtx, err := client.ReadPersistentCommandFlags(initClientCtx, cmd.Flags()) if err != nil { return err @@ -293,27 +322,30 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) { if humanReadableDenomsInput { // Parse and replace denoms in args for i, arg := range args { - lowerCaseArg := strings.ToLower(arg) - lowerCaseArgArray := strings.Split(lowerCaseArg, ",") - re := regexp.MustCompile(`^(\d+)(.+)`) + argArray := strings.Split(arg, ",") + + re := regexp.MustCompile(`^([\d.]+)(\D+)$`) - for i, lowerCaseArg := range lowerCaseArgArray { + for i, singleArg := range argArray { + lowerCaseArg := strings.ToLower(singleArg) match := re.FindStringSubmatch(lowerCaseArg) if len(match) == 3 { + value, denom := match[1], match[2] // If the index has a length of 3 then it has a number and a denom (this is a coin object) // Note, index 0 is the entire string, index 1 is the number, and index 2 is the denom - if _, ok := assetMap[match[2]]; ok { - // In this case, we just need to replace the denom with the base denom and retain the number - lowerCaseArgArray[i] = match[1] + assetMap[match[2]].Base + transformedCoin, err := transformCoinValueToBaseInt(value, denom, assetMap) + if err != nil { + continue } + argArray[i] = transformedCoin } else { if _, ok := assetMap[lowerCaseArg]; ok { // In this case, we just need to replace the denom with the base denom - lowerCaseArgArray[i] = assetMap[lowerCaseArg].Base + argArray[i] = assetMap[lowerCaseArg].Base } } } - args[i] = strings.Join(lowerCaseArgArray, ",") + args[i] = strings.Join(argArray, ",") } // Parse and replace denoms in flags @@ -321,17 +353,19 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) { lowerCaseFlagValue := strings.ToLower(flag.Value.String()) lowerCaseFlagValueArray := strings.Split(lowerCaseFlagValue, ",") - re := regexp.MustCompile(`^(\d+)(.+)`) + re := regexp.MustCompile(`^([\d.]+)(\D+)$`) for i, lowerCaseFlagValue := range lowerCaseFlagValueArray { match := re.FindStringSubmatch(lowerCaseFlagValue) if len(match) == 3 { + value, denom := match[1], match[2] // If the index has a length of 3 then it has a number and a denom (this is a coin object) // Note, index 0 is the entire string, index 1 is the number, and index 2 is the denom - if _, ok := assetMap[match[2]]; ok { - // In this case, we just need to replace the denom with the base denom and retain the number - lowerCaseFlagValueArray[i] = strings.Replace(lowerCaseFlagValue, match[2], assetMap[match[2]].Base, -1) + transformedCoin, err := transformCoinValueToBaseInt(value, denom, assetMap) + if err != nil { + continue } + lowerCaseFlagValueArray[i] = transformedCoin } else { if _, ok := assetMap[lowerCaseFlagValue]; ok { // Otherwise, we just need to replace the denom with the base denom @@ -363,8 +397,9 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) { func getHomeEnvironment() string { envPath := filepath.Join(osmosis.DefaultNodeHome, ".env") - // Use default node home if can't get environment - err := godotenv.Load(envPath) + // Use default node home if can't get environment. + // Overload must be used here in the event that the .env gets updated. + err := godotenv.Overload(envPath) if err != nil { // Failed to load, using default home directory return EnvMainnet @@ -407,7 +442,7 @@ func initAppConfig() (string, interface{}) { ############################################################################### [osmosis-mempool] -# This is the max allowed gas any tx. +# This is the max allowed gas any tx. # This is only for local mempool purposes, and thus is only ran on check tx. max-gas-wanted-per-tx = "25000000" @@ -652,8 +687,8 @@ func genAutoCompleteCmd(rootCmd *cobra.Command) { Long: `To configure your shell to load completions for each session, add to your profile: # bash example -echo '. <(osmosisd enable-cli-autocomplete bash)' >> ~/.profile -source ~/.profile +echo '. <(osmosisd enable-cli-autocomplete bash)' >> ~/.bash_profile +source ~/.bash_profile # zsh example echo '. <(osmosisd enable-cli-autocomplete zsh)' >> ~/.zshrc @@ -676,3 +711,28 @@ source ~/.zshrc }, }) } + +// transformCoinValueToBaseInt transforms a cli input that has been split into a number and a denom into it's base int value and base denom. +// i.e. 10.7osmo -> 10700000uosmo +// 12atom -> 12000000uatom +// 15000000uakt -> 15000000uakt (does nothing since it's already in base denom format) +func transformCoinValueToBaseInt(coinValue, coinDenom string, assetMap map[string]DenomUnitMap) (string, error) { + // If the index has a length of 3 then it has a number and a denom (this is a coin object) + // Note, index 0 is the entire string, index 1 is the number, and index 2 is the denom + if denomUnitMap, ok := assetMap[coinDenom]; ok { + // In this case, we just need to replace the denom with the base denom and retain the number + if denomUnitMap.Exponent != 0 { + coinDec, err := sdk.NewDecFromStr(coinValue) + if err != nil { + return "", err + } + transformedCoinValue := coinDec.Mul(sdk.MustNewDecFromStr("10").Power(denomUnitMap.Exponent)) + transformedCoinValueInt := transformedCoinValue.TruncateInt() + transformedCoinValueStr := transformedCoinValueInt.String() + return transformedCoinValueStr + assetMap[coinDenom].Base, nil + } else { + return coinValue + assetMap[coinDenom].Base, nil + } + } + return "", fmt.Errorf("denom %s not found in asset map", coinDenom) +} diff --git a/cosmwasm/contracts/crosschain-registry/src/contract.rs b/cosmwasm/contracts/crosschain-registry/src/contract.rs index 3931b08a8ff..30a9a28a88f 100644 --- a/cosmwasm/contracts/crosschain-registry/src/contract.rs +++ b/cosmwasm/contracts/crosschain-registry/src/contract.rs @@ -4,15 +4,21 @@ use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, use cw2::set_contract_version; use crate::error::ContractError; -use crate::msg::{ExecuteMsg, GetAddressFromAliasResponse, InstantiateMsg, QueryMsg}; -use crate::state::{Config, CONFIG, CONTRACT_ALIAS_MAP}; -use crate::{execute, query}; +use crate::msg::{ + ExecuteMsg, GetAddressFromAliasResponse, IBCLifecycleComplete, InstantiateMsg, QueryMsg, + SudoMsg, +}; +use crate::state::{ChainPFM, Config, CHAIN_PFM_MAP, CONFIG, CONTRACT_ALIAS_MAP}; +use crate::{execute, ibc_lifecycle, query}; use registry::Registry; // version info for migration const CONTRACT_NAME: &str = "crates.io:crosschain-registry"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +// The name of the chain on which this contract is instantiated +pub const CONTRACT_CHAIN: &str = "osmosis"; + #[cfg_attr(not(feature = "imported"), entry_point)] pub fn instantiate( deps: DepsMut, @@ -27,6 +33,15 @@ pub fn instantiate( let state = Config { owner }; CONFIG.save(deps.storage, &state)?; + CHAIN_PFM_MAP.save( + deps.storage, + CONTRACT_CHAIN, + &ChainPFM { + acknowledged: true, + validated: true, + }, + )?; + Ok(Response::new().add_attribute("method", "instantiate")) } @@ -38,6 +53,10 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { + ExecuteMsg::ModifyDenomAlias { operations } => { + execute::denom_alias_operations(deps, info.sender, operations) + } + // Contract aliases ExecuteMsg::ModifyContractAlias { operations } => { execute::contract_alias_operations(deps, info.sender, operations) @@ -73,12 +92,16 @@ pub fn execute( env.block.time, with_memo, None, + false, )?; deps.api.debug(&format!("transfer_msg: {transfer_msg:?}")); Ok(Response::new() .add_message(transfer_msg) .add_attribute("method", "unwrap_coin")) } + + ExecuteMsg::ProposePFM { chain } => execute::propose_pfm((deps, env, info), chain), + ExecuteMsg::ValidatePFM { chain } => execute::validate_pfm((deps, env, info), chain), } } @@ -120,6 +143,30 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { QueryMsg::GetChainNameFromBech32Prefix { prefix } => { to_binary(&query::query_chain_name_from_bech32_prefix(deps, prefix)?) } + QueryMsg::HasPacketForwarding { chain } => { + to_binary(&query::query_chain_has_pfm(deps, chain)) + } + QueryMsg::GetAliasForDenomPath { denom_path } => { + to_binary(&query::query_alias_for_denom_path(deps, &denom_path)?) + } + QueryMsg::GetDenomPathForAlias { alias } => { + to_binary(&query::query_denom_path_for_alias(deps, &alias)?) + } + } +} + +#[cfg_attr(not(feature = "imported"), entry_point)] +pub fn sudo(deps: DepsMut, _env: Env, msg: SudoMsg) -> Result { + match msg { + SudoMsg::IBCLifecycleComplete(IBCLifecycleComplete::IBCAck { + channel, + sequence, + ack, + success, + }) => ibc_lifecycle::receive_ack(deps, channel, sequence, ack, success), + SudoMsg::IBCLifecycleComplete(IBCLifecycleComplete::IBCTimeout { channel, sequence }) => { + ibc_lifecycle::receive_timeout(deps, channel, sequence) + } } } @@ -211,7 +258,7 @@ mod test { deps.as_ref(), mock_env(), QueryMsg::GetChannelFromChainPair { - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "juno".to_string(), }, ) @@ -224,7 +271,7 @@ mod test { deps.as_ref(), mock_env(), QueryMsg::GetDestinationChainFromSourceChainViaChannel { - on_chain: "osmosis".to_string(), + on_chain: CONTRACT_CHAIN.to_string(), via_channel: "channel-42".to_string(), }, ) @@ -237,7 +284,7 @@ mod test { deps.as_ref(), mock_env(), QueryMsg::GetChannelFromChainPair { - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "stargaze".to_string(), }, ) @@ -250,7 +297,7 @@ mod test { deps.as_ref(), mock_env(), QueryMsg::GetDestinationChainFromSourceChainViaChannel { - on_chain: "osmosis".to_string(), + on_chain: CONTRACT_CHAIN.to_string(), via_channel: "channel-75".to_string(), }, ) @@ -264,7 +311,7 @@ mod test { mock_env(), QueryMsg::GetChannelFromChainPair { source_chain: "stargaze".to_string(), - destination_chain: "osmosis".to_string(), + destination_chain: CONTRACT_CHAIN.to_string(), }, ) .unwrap(); @@ -282,14 +329,14 @@ mod test { ) .unwrap(); let destination_chain: String = from_binary(&destination_chain).unwrap(); - assert_eq!("osmosis", destination_chain); + assert_eq!(CONTRACT_CHAIN, destination_chain); // Attempt to retrieve a link that doesn't exist and check that we get an error let channel_binary = query( deps.as_ref(), mock_env(), QueryMsg::GetChannelFromChainPair { - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "cerberus".to_string(), }, ); @@ -299,7 +346,7 @@ mod test { let msg = ExecuteMsg::ModifyChainChannelLinks { operations: vec![ConnectionInput { operation: execute::FullOperation::Disable, - source_chain: "OSMOSIS".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "JUNO".to_string(), channel_id: Some("CHANNEL-42".to_string()), new_source_chain: None, @@ -316,7 +363,7 @@ mod test { deps.as_ref(), mock_env(), QueryMsg::GetChannelFromChainPair { - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "juno".to_string(), }, ); @@ -326,7 +373,7 @@ mod test { let msg = ExecuteMsg::ModifyChainChannelLinks { operations: vec![ConnectionInput { operation: execute::FullOperation::Enable, - source_chain: "OSMOSIS".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "JUNO".to_string(), channel_id: Some("CHANNEL-42".to_string()), new_source_chain: None, @@ -342,7 +389,7 @@ mod test { deps.as_ref(), mock_env(), QueryMsg::GetChannelFromChainPair { - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "juno".to_string(), }, ) diff --git a/cosmwasm/contracts/crosschain-registry/src/error.rs b/cosmwasm/contracts/crosschain-registry/src/error.rs index 18ad07f9b56..87892544745 100644 --- a/cosmwasm/contracts/crosschain-registry/src/error.rs +++ b/cosmwasm/contracts/crosschain-registry/src/error.rs @@ -13,12 +13,51 @@ pub enum ContractError { #[error("{0}")] Payment(#[from] cw_utils::PaymentError), - #[error("Unauthorized")] + #[error("unauthorized")] Unauthorized {}, - #[error("contract alias already exists: {alias:?}")] + #[error("chain validation not started for {chain}")] + ValidationNotFound { chain: String }, + + #[error("coin from invalid chain. It belongs to {supplied_chain} and should be from {expected_chain}")] + CoinFromInvalidChain { + supplied_chain: String, + expected_chain: String, + }, + + // This is only used for pfm validation. Uncomment when re-activating pfm validation + // + // #[error( + // "only messages initialized by the address of this contract in another chain are allowed. Expected {expected_sender} but got {actual_sender}" + // )] + // InvalidSender { + // expected_sender: String, + // actual_sender: String, + // }, + // + #[error("alias already exists: {alias:?}")] AliasAlreadyExists { alias: String }, + #[error("alias already exists for: {base:?}")] + AliasAlreadyExistsFor { base: String }, + + #[error("alias does not exist: {alias:?}")] + AliasDoesNotExist { alias: String }, + + #[error("alias does not exist for: {base:?}")] + AliasDoesNotExistFor { base: String }, + + #[error("existing alias {existing} does not match supplied alias: {expected}")] + AliasDoesNotMatch { existing: String, expected: String }, + + #[error("invalid alias {alias}. Must be alphanumeric")] + InvalidAlias { alias: String }, + + #[error( + "PFM validation already in progress for {chain:?}. Wait for the ibc lifecycle to complete" + )] + PFMValidationAlreadyInProgress { chain: String }, + #[error("authorized address already exists for source chain: {source_chain:?}")] ChainAuthorizedAddressAlreadyExists { source_chain: String }, diff --git a/cosmwasm/contracts/crosschain-registry/src/execute.rs b/cosmwasm/contracts/crosschain-registry/src/execute.rs index 84829b9720e..5323857660a 100644 --- a/cosmwasm/contracts/crosschain-registry/src/execute.rs +++ b/cosmwasm/contracts/crosschain-registry/src/execute.rs @@ -1,13 +1,14 @@ use crate::helpers::*; use crate::state::{ - CHAIN_ADMIN_MAP, CHAIN_MAINTAINER_MAP, CHAIN_TO_BECH32_PREFIX_MAP, + ChainPFM, CHAIN_ADMIN_MAP, CHAIN_MAINTAINER_MAP, CHAIN_PFM_MAP, CHAIN_TO_BECH32_PREFIX_MAP, CHAIN_TO_BECH32_PREFIX_REVERSE_MAP, CHAIN_TO_CHAIN_CHANNEL_MAP, CHANNEL_ON_CHAIN_CHAIN_MAP, - CONTRACT_ALIAS_MAP, GLOBAL_ADMIN_MAP, + CONTRACT_ALIAS_MAP, DENOM_ALIAS_MAP, DENOM_ALIAS_REVERSE_MAP, GLOBAL_ADMIN_MAP, }; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, DepsMut, Response}; +use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response}; use cw_storage_plus::Map; -use registry::RegistryError; +use registry::msg::Callback; +use registry::{Registry, RegistryError}; use crate::ContractError; @@ -40,6 +41,118 @@ pub struct ContractAliasInput { pub new_alias: Option, } +// Struct for input data for a denom alias +#[cw_serde] +pub struct DenomAliasInput { + pub operation: FullOperation, + pub alias: String, + pub full_denom_path: String, +} + +pub fn propose_pfm( + ctx: (DepsMut, Env, MessageInfo), + chain: String, +) -> Result { + let (deps, env, info) = ctx; + + // enforce lowercase + let chain = chain.to_lowercase(); + + // validation + let registry = Registry::default(deps.as_ref()); + let coin = cw_utils::one_coin(&info)?; + let native_chain = registry.get_native_chain(&coin.denom)?; + + if native_chain.as_ref() != chain { + return Err(ContractError::CoinFromInvalidChain { + supplied_chain: native_chain.as_ref().to_string(), + expected_chain: chain, + }); + } + + // Temporarily check that only the global admin can propose a PFM. This is + // due to different versions of PFM having different senders. Once all + // chains are on the latest PFM, we can remove this check and uncomment the + // code in validate_pfm + check_action_permission(FullOperation::Set, Permission::GlobalAdmin)?; + + // check if the chain is already registered or is in progress + if let Some(chain_pfm) = CHAIN_PFM_MAP.may_load(deps.storage, &chain)? { + if chain_pfm.is_validated() { + // Only authorized addresses can ask for a validated PFM to be re-checked + // If sender is the contract governor, then they are authorized to do do this to any chain + // Otherwise, they must be authorized to do manage the chain they are attempting to modify + let user_permission = + check_is_authorized(deps.as_ref(), info.sender, Some(chain.clone()))?; + check_action_permission(FullOperation::Change, user_permission)?; + } else { + return Err(ContractError::PFMValidationAlreadyInProgress { + chain: chain.clone(), + }); + } + }; + + // Store the chain to validate + CHAIN_PFM_MAP.save(deps.storage, &chain, &ChainPFM::default())?; + + let own_addr = env.contract.address; + + // redeclaring (shadowing) registry to avoid issues with the borrow checker + let registry = Registry::default(deps.as_ref()); + let ibc_transfer = registry.unwrap_coin_into( + coin, + own_addr.to_string(), + None, + own_addr.to_string(), + env.block.time, + format!(r#"{{"ibc_callback":"{own_addr}"}}"#), + Some(Callback { + contract: own_addr, + msg: format!(r#"{{"validate_pfm": {{"chain": "{chain}"}} }}"#).try_into()?, + }), + true, + )?; + + Ok(Response::default().add_message(ibc_transfer)) +} + +pub fn validate_pfm( + ctx: (DepsMut, Env, MessageInfo), + chain: String, +) -> Result { + let (deps, _env, _info) = ctx; + + let chain = chain.to_lowercase(); + + // TODO: Uncomment this once all chains are on the latest PFM and we can + // properly verify the sender. We will also need to modify how + // derive_wasmhooks_sender works at that point + // + // let registry = Registry::default(deps.as_ref()); + // let channel = registry.get_channel(&chain, CONTRACT_CHAIN)?; + // let own_addr = env.contract.address.as_str(); + // let original_sender = registry.encode_addr_for_chain(own_addr, &chain)?; + // let expected_sender = registry::derive_wasmhooks_sender(&channel, &original_sender, "osmo")?; + // if expected_sender != info.sender { + // return Err(ContractError::InvalidSender { + // expected_sender, + // actual_sender: info.sender.into_string(), + // }); + // } + + let mut chain_pfm = CHAIN_PFM_MAP.load(deps.storage, &chain).map_err(|_| { + ContractError::ValidationNotFound { + chain: chain.clone(), + } + })?; + + chain_pfm.validated = true; + + CHAIN_PFM_MAP.save(deps.storage, &chain, &chain_pfm)?; + + Ok(Response::default()) +} + // Set, change, or remove a contract alias to an address pub fn contract_alias_operations( deps: DepsMut, @@ -98,6 +211,126 @@ pub fn contract_alias_operations( Ok(response) } +// Set, Change, Enable, or Disable a denom alias +pub fn denom_alias_operations( + deps: DepsMut, + sender: Addr, + operations: Vec, +) -> Result { + // Only contract governor can call denom alias CRUD operations + let is_owner = is_owner(deps.as_ref(), &sender); + let is_global_admin = is_global_admin(deps.as_ref(), &sender); + + if !is_owner && !is_global_admin { + return Err(ContractError::Unauthorized {}); + } + + let mut response = Response::new(); + for operation in operations { + let denom_alias = normalize_alias(&operation.alias)?; + let path = operation.full_denom_path; + + match operation.operation { + FullOperation::Set => { + if DENOM_ALIAS_MAP.has(deps.storage, &path) { + return Err(ContractError::AliasAlreadyExistsFor { base: path }); + } + // TODO: This check is not enough, as disabled aliases could be + // re-set. We need to keep track of enabled/disabled in the + // reverse map as well + if DENOM_ALIAS_REVERSE_MAP.has(deps.storage, &operation.alias) { + return Err(ContractError::AliasAlreadyExists { alias: denom_alias }); + } + + DENOM_ALIAS_MAP.save(deps.storage, &path, &(denom_alias.clone(), true).into())?; + DENOM_ALIAS_REVERSE_MAP.save(deps.storage, &denom_alias, &(&path, true).into())?; + + response = + response.add_attribute("set_denom_alias", format!("{denom_alias} <=> {path}")); + } + FullOperation::Change => { + if !is_owner { + // Only the owner can change for security reasons + return Err(ContractError::Unauthorized {}); + } + + // Ensure the alias exists + let map_entry = DENOM_ALIAS_MAP + .load(deps.storage, &path) + .map_err(|_| ContractError::AliasDoesNotExistFor { base: path.clone() })?; + + let is_enabled = map_entry.enabled; + let new_alias = normalize_alias(&operation.alias)?; + + if DENOM_ALIAS_REVERSE_MAP.has(deps.storage, &new_alias) { + return Err(ContractError::AliasAlreadyExists { alias: new_alias }); + } + + DENOM_ALIAS_MAP.save(deps.storage, &path, &(&new_alias, is_enabled).into())?; + DENOM_ALIAS_REVERSE_MAP.remove(deps.storage, &map_entry.value); + DENOM_ALIAS_REVERSE_MAP.save(deps.storage, &new_alias, &(&path, true).into())?; + + response = + response.add_attribute("change_denom_alias", format!("{new_alias} <=> {path}")); + } + FullOperation::Remove => { + if !is_owner { + // Only the owner can remove for security reasons + return Err(ContractError::Unauthorized {}); + } + let map_entry = DENOM_ALIAS_MAP + .load(deps.storage, &path) + .map_err(|_| ContractError::AliasDoesNotExistFor { base: path.clone() })?; + DENOM_ALIAS_MAP.remove(deps.storage, &path); + DENOM_ALIAS_REVERSE_MAP.remove(deps.storage, &map_entry.value); + + response = response.add_attribute("remove_denom_alias", map_entry.value); + } + FullOperation::Enable => { + let map_entry = DENOM_ALIAS_MAP + .load(deps.storage, &path) + .map_err(|_| ContractError::AliasDoesNotExistFor { base: path.clone() })?; + DENOM_ALIAS_MAP.save( + deps.storage, + &path, + &(map_entry.value.clone(), true).into(), + )?; + // Add to the enabled alias to the reverse map + DENOM_ALIAS_REVERSE_MAP.save( + deps.storage, + &map_entry.value, + &(&path, true).into(), + )?; + + response = response.add_attribute( + "enable_denom_alias", + format!("{} <=> {path}", map_entry.value), + ); + } + FullOperation::Disable => { + let map_entry = DENOM_ALIAS_MAP + .load(deps.storage, &path) + .map_err(|_| ContractError::AliasDoesNotExistFor { base: path.clone() })?; + DENOM_ALIAS_MAP.save( + deps.storage, + &path, + &(map_entry.value.clone(), false).into(), + )?; + // Disable the alias on the reverse map + DENOM_ALIAS_REVERSE_MAP.save( + deps.storage, + &map_entry.value, + &(&path, false).into(), + )?; + + response = response + .add_attribute("disable_denom_alias", format!("{denom_alias} <=> {path}")); + } + } + } + Ok(response) +} + // Chain Channel Registry // Struct for input data for a single connection @@ -176,7 +409,7 @@ pub fn connection_operations( })?; let channel_on_chain_map = CHANNEL_ON_CHAIN_CHAIN_MAP .load(deps.storage, (&chain_to_chain_map.value, &source_chain)) - .map_err(|_| RegistryError::ChannelChainLinkDoesNotExist { + .map_err(|_| RegistryError::ChannelDoesNotExistOnChain { channel_id: chain_to_chain_map.value.clone(), source_chain: source_chain.clone(), })?; @@ -273,7 +506,7 @@ pub fn connection_operations( })?; let channel_on_chain_map = CHANNEL_ON_CHAIN_CHAIN_MAP .load(deps.storage, (&chain_to_chain_map.value, &source_chain)) - .map_err(|_| RegistryError::ChannelChainLinkDoesNotExist { + .map_err(|_| RegistryError::ChannelDoesNotExistOnChain { channel_id: chain_to_chain_map.value.clone(), source_chain: source_chain.clone(), })?; @@ -301,7 +534,7 @@ pub fn connection_operations( })?; let channel_on_chain_map = CHANNEL_ON_CHAIN_CHAIN_MAP .load(deps.storage, (&chain_to_chain_map.value, &source_chain)) - .map_err(|_| RegistryError::ChannelChainLinkDoesNotExist { + .map_err(|_| RegistryError::ChannelDoesNotExistOnChain { channel_id: chain_to_chain_map.value.clone(), source_chain: source_chain.clone(), })?; @@ -584,12 +817,14 @@ pub fn authorized_address_operations( mod tests { use super::*; use crate::msg::ExecuteMsg; + use crate::query::{query_alias_for_denom_path, query_denom_path_for_alias}; use crate::{contract, helpers::test::initialize_contract}; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; static CREATOR_ADDRESS: &str = "creator"; static CHAIN_ADMIN: &str = "chain_admin"; static CHAIN_MAINTAINER: &str = "chain_maintainer"; static UNAUTHORIZED_ADDRESS: &str = "unauthorized_address"; + use crate::contract::CONTRACT_CHAIN; #[test] fn test_set_contract_alias() { @@ -828,7 +1063,7 @@ mod tests { let unauthorized_remove_msg = ExecuteMsg::ModifyContractAlias { operations: vec![ContractAliasInput { operation: Operation::Remove, - alias: alias, + alias, address: Some(address), new_alias: None, }], @@ -854,7 +1089,7 @@ mod tests { let msg = ExecuteMsg::ModifyChainChannelLinks { operations: vec![ConnectionInput { operation: FullOperation::Set, - source_chain: "OSMOSIS".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "COSMOS".to_string(), channel_id: Some("CHANNEL-0".to_string()), new_source_chain: None, @@ -867,7 +1102,7 @@ mod tests { assert_eq!( CHAIN_TO_CHAIN_CHANNEL_MAP - .load(&deps.storage, ("osmosis", "cosmos")) + .load(&deps.storage, (CONTRACT_CHAIN, "cosmos")) .unwrap(), ("channel-0", true).into() ); @@ -875,7 +1110,7 @@ mod tests { // Verify that channel-0 on osmosis is linked to cosmos assert_eq!( CHANNEL_ON_CHAIN_CHAIN_MAP - .load(&deps.storage, ("channel-0", "osmosis")) + .load(&deps.storage, ("channel-0", CONTRACT_CHAIN)) .unwrap(), ("cosmos", true).into() ); @@ -885,7 +1120,7 @@ mod tests { let msg = ExecuteMsg::ModifyChainChannelLinks { operations: vec![ConnectionInput { operation: FullOperation::Set, - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "cosmos".to_string(), channel_id: Some("channel-150".to_string()), new_source_chain: None, @@ -898,19 +1133,19 @@ mod tests { assert!(result.is_err()); let expected_error = ContractError::ChainToChainChannelLinkAlreadyExists { - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "cosmos".to_string(), }; assert_eq!(result.unwrap_err(), expected_error); assert_eq!( CHAIN_TO_CHAIN_CHANNEL_MAP - .load(&deps.storage, ("osmosis", "cosmos")) + .load(&deps.storage, (CONTRACT_CHAIN, "cosmos")) .unwrap(), ("channel-0", true).into() ); assert_eq!( CHANNEL_ON_CHAIN_CHAIN_MAP - .load(&deps.storage, ("channel-0", "osmosis")) + .load(&deps.storage, ("channel-0", CONTRACT_CHAIN)) .unwrap(), ("cosmos", true).into() ); @@ -920,7 +1155,7 @@ mod tests { operations: vec![ConnectionInput { operation: FullOperation::Set, source_chain: "mars".to_string(), - destination_chain: "osmosis".to_string(), + destination_chain: CONTRACT_CHAIN.to_string(), channel_id: Some("channel-1".to_string()), new_source_chain: None, new_destination_chain: None, @@ -933,14 +1168,14 @@ mod tests { let expected_error = ContractError::Unauthorized {}; assert_eq!(result.unwrap_err(), expected_error); - assert!(!CHAIN_TO_CHAIN_CHANNEL_MAP.has(&deps.storage, ("mars", "osmosis"))); + assert!(!CHAIN_TO_CHAIN_CHANNEL_MAP.has(&deps.storage, ("mars", CONTRACT_CHAIN))); // Set the canonical channel link between mars and osmosis to channel-1 with a mars chain admin address let chain_admin_info = mock_info(CHAIN_ADMIN, &[]); contract::execute(deps.as_mut(), mock_env(), chain_admin_info.clone(), msg).unwrap(); assert_eq!( CHAIN_TO_CHAIN_CHANNEL_MAP - .load(&deps.storage, ("mars", "osmosis")) + .load(&deps.storage, ("mars", CONTRACT_CHAIN)) .unwrap(), ("channel-1", true).into() ); @@ -948,7 +1183,7 @@ mod tests { CHANNEL_ON_CHAIN_CHAIN_MAP .load(&deps.storage, ("channel-1", "mars")) .unwrap(), - ("osmosis", true).into() + (CONTRACT_CHAIN, true).into() ); // Set the canonical channel link between juno and mars to channel-2 with a juno chain maintainer address @@ -1040,7 +1275,7 @@ mod tests { let msg = ExecuteMsg::ModifyChainChannelLinks { operations: vec![ConnectionInput { operation: FullOperation::Set, - source_chain: "OSMOSIS".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "COSMOS".to_string(), channel_id: Some("CHANNEL-0".to_string()), new_source_chain: None, @@ -1056,7 +1291,7 @@ mod tests { let msg = ExecuteMsg::ModifyChainChannelLinks { operations: vec![ConnectionInput { operation: FullOperation::Change, - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "cosmos".to_string(), channel_id: None, new_source_chain: None, @@ -1070,7 +1305,7 @@ mod tests { // Verify that the channel between osmosis and cosmos has changed from channel-0 to channel-150 assert_eq!( CHAIN_TO_CHAIN_CHANNEL_MAP - .load(&deps.storage, ("osmosis", "cosmos")) + .load(&deps.storage, (CONTRACT_CHAIN, "cosmos")) .unwrap(), ("channel-150", true).into() ); @@ -1079,7 +1314,7 @@ mod tests { let msg = ExecuteMsg::ModifyChainChannelLinks { operations: vec![ConnectionInput { operation: FullOperation::Change, - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "regen".to_string(), channel_id: None, new_source_chain: None, @@ -1091,7 +1326,7 @@ mod tests { assert!(result.is_err()); let expected_error = ContractError::from(RegistryError::ChainChannelLinkDoesNotExist { - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "regen".to_string(), }); assert_eq!(result.unwrap_err(), expected_error); @@ -1100,7 +1335,7 @@ mod tests { let msg = ExecuteMsg::ModifyChainChannelLinks { operations: vec![ConnectionInput { operation: FullOperation::Change, - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "cosmos".to_string(), channel_id: None, new_source_chain: None, @@ -1114,7 +1349,7 @@ mod tests { // Verify that channel-150 on osmosis is linked to regen assert_eq!( CHANNEL_ON_CHAIN_CHAIN_MAP - .load(&deps.storage, ("channel-150", "osmosis")) + .load(&deps.storage, ("channel-150", CONTRACT_CHAIN)) .unwrap(), ("regen", true).into() ); @@ -1123,7 +1358,7 @@ mod tests { let msg = ExecuteMsg::ModifyChainChannelLinks { operations: vec![ConnectionInput { operation: FullOperation::Change, - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "regen".to_string(), channel_id: None, new_source_chain: None, @@ -1143,7 +1378,7 @@ mod tests { contract::execute(deps.as_mut(), mock_env(), info_chain_admin, msg).unwrap(); assert_eq!( CHAIN_TO_CHAIN_CHANNEL_MAP - .load(&deps.storage, ("osmosis", "regen")) + .load(&deps.storage, (CONTRACT_CHAIN, "regen")) .unwrap(), ("channel-2", true).into() ); @@ -1152,7 +1387,7 @@ mod tests { let msg = ExecuteMsg::ModifyChainChannelLinks { operations: vec![ConnectionInput { operation: FullOperation::Change, - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "cosmos".to_string(), channel_id: None, new_source_chain: None, @@ -1164,7 +1399,7 @@ mod tests { let result = contract::execute(deps.as_mut(), mock_env(), info, msg); let expected_error = ContractError::from(RegistryError::ChainChannelLinkDoesNotExist { - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "cosmos".to_string(), }); assert_eq!(result.unwrap_err(), expected_error); @@ -1175,7 +1410,7 @@ mod tests { let msg = ExecuteMsg::ModifyChainChannelLinks { operations: vec![ConnectionInput { operation: FullOperation::Change, - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "regen".to_string(), channel_id: None, new_source_chain: None, @@ -1200,7 +1435,7 @@ mod tests { operations: vec![ ConnectionInput { operation: FullOperation::Set, - source_chain: "OSMOSIS".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "COSMOS".to_string(), channel_id: Some("CHANNEL-0".to_string()), new_source_chain: None, @@ -1209,7 +1444,7 @@ mod tests { }, ConnectionInput { operation: FullOperation::Set, - source_chain: "OSMOSIS".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "REGEN".to_string(), channel_id: Some("CHANNEL-1".to_string()), new_source_chain: None, @@ -1225,7 +1460,7 @@ mod tests { let msg = ExecuteMsg::ModifyChainChannelLinks { operations: vec![ConnectionInput { operation: FullOperation::Remove, - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "cosmos".to_string(), channel_id: None, new_source_chain: None, @@ -1237,13 +1472,13 @@ mod tests { contract::execute(deps.as_mut(), mock_env(), info, msg.clone()).unwrap(); // Verify that the link no longer exists - assert!(!CHAIN_TO_CHAIN_CHANNEL_MAP.has(&deps.storage, ("osmosis", "cosmos"))); + assert!(!CHAIN_TO_CHAIN_CHANNEL_MAP.has(&deps.storage, (CONTRACT_CHAIN, "cosmos"))); let info = mock_info(CREATOR_ADDRESS, &[]); let result = contract::execute(deps.as_mut(), mock_env(), info, msg); let expected_error = ContractError::from(RegistryError::ChainChannelLinkDoesNotExist { - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "cosmos".to_string(), }); assert_eq!(result.unwrap_err(), expected_error); @@ -1254,7 +1489,7 @@ mod tests { let msg = ExecuteMsg::ModifyChainChannelLinks { operations: vec![ConnectionInput { operation: FullOperation::Remove, - source_chain: "osmosis".to_string(), + source_chain: CONTRACT_CHAIN.to_string(), destination_chain: "regen".to_string(), channel_id: None, new_source_chain: None, @@ -1278,7 +1513,7 @@ mod tests { let msg = ExecuteMsg::ModifyBech32Prefixes { operations: vec![ChainToBech32PrefixInput { operation: FullOperation::Set, - chain_name: "OSMOSIS".to_string(), + chain_name: CONTRACT_CHAIN.to_string(), prefix: "OSMO".to_string(), new_prefix: None, }], @@ -1288,7 +1523,7 @@ mod tests { assert_eq!( CHAIN_TO_BECH32_PREFIX_MAP - .load(&deps.storage, "osmosis") + .load(&deps.storage, CONTRACT_CHAIN) .unwrap(), ("osmo", true).into() ); @@ -1296,7 +1531,7 @@ mod tests { CHAIN_TO_BECH32_PREFIX_REVERSE_MAP .load(&deps.storage, "osmo") .unwrap(), - vec!["osmosis"] + vec![CONTRACT_CHAIN] ); // Set another chain with the same prefix @@ -1320,14 +1555,14 @@ mod tests { CHAIN_TO_BECH32_PREFIX_REVERSE_MAP .load(&deps.storage, "osmo") .unwrap(), - vec!["osmosis", "ismisis"] + vec![CONTRACT_CHAIN, "ismisis"] ); // Set another chain with the same prefix let msg = ExecuteMsg::ModifyBech32Prefixes { operations: vec![ChainToBech32PrefixInput { operation: FullOperation::Disable, - chain_name: "OSMOSIS".to_string(), + chain_name: CONTRACT_CHAIN.to_string(), prefix: "OSMO".to_string(), new_prefix: None, }], @@ -1335,7 +1570,7 @@ mod tests { contract::execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap(); assert_eq!( CHAIN_TO_BECH32_PREFIX_MAP - .load(&deps.storage, "osmosis") + .load(&deps.storage, CONTRACT_CHAIN) .unwrap(), ("osmo", false).into() ); @@ -1350,7 +1585,7 @@ mod tests { let msg = ExecuteMsg::ModifyBech32Prefixes { operations: vec![ChainToBech32PrefixInput { operation: FullOperation::Enable, - chain_name: "OSMOSIS".to_string(), + chain_name: CONTRACT_CHAIN.to_string(), prefix: "OSMO".to_string(), new_prefix: None, }], @@ -1358,7 +1593,7 @@ mod tests { contract::execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap(); assert_eq!( CHAIN_TO_BECH32_PREFIX_MAP - .load(&deps.storage, "osmosis") + .load(&deps.storage, CONTRACT_CHAIN) .unwrap(), ("osmo", true).into() ); @@ -1366,14 +1601,14 @@ mod tests { CHAIN_TO_BECH32_PREFIX_REVERSE_MAP .load(&deps.storage, "osmo") .unwrap(), - vec!["ismisis", "osmosis"] + vec!["ismisis", CONTRACT_CHAIN] ); // Set another chain with the same prefix let msg = ExecuteMsg::ModifyBech32Prefixes { operations: vec![ChainToBech32PrefixInput { operation: FullOperation::Remove, - chain_name: "OSMOSIS".to_string(), + chain_name: CONTRACT_CHAIN.to_string(), prefix: "OSMO".to_string(), new_prefix: None, }], @@ -1393,7 +1628,222 @@ mod tests { ); CHAIN_TO_BECH32_PREFIX_MAP - .load(&deps.storage, "osmosis") + .load(&deps.storage, CONTRACT_CHAIN) .unwrap_err(); } + + #[test] + fn test_denom_alias_operations() { + let mut deps = mock_dependencies(); + + initialize_contract(deps.as_mut()); + + let path1 = "transfer/channel-0/1denom"; + + let msg = ExecuteMsg::ModifyDenomAlias { + operations: vec![DenomAliasInput { + operation: FullOperation::Set, + full_denom_path: path1.to_string(), + alias: "alias1".to_string(), + }], + }; + + // Test case: Set an alias + let info = mock_info(CREATOR_ADDRESS, &[]); + let res = contract::execute(deps.as_mut(), mock_env(), info, msg).unwrap(); + + assert_eq!( + DENOM_ALIAS_MAP + .may_load(deps.as_ref().storage, path1) + .unwrap(), + Some(("alias1".to_string(), true).into()) + ); + assert_eq!( + DENOM_ALIAS_REVERSE_MAP + .may_load(deps.as_ref().storage, "alias1") + .unwrap(), + Some((path1.to_string(), true).into()) + ); + assert_eq!( + res.attributes, + vec![("set_denom_alias".to_string(), format!("alias1 <=> {path1}"))] + ); + + // Check queries + assert_eq!( + query_denom_path_for_alias(deps.as_ref(), "alias1").unwrap(), + path1 + ); + assert_eq!( + query_alias_for_denom_path(deps.as_ref(), path1).unwrap(), + "alias1" + ); + + // Test case: Change an alias + let change_msg = ExecuteMsg::ModifyDenomAlias { + operations: vec![DenomAliasInput { + operation: FullOperation::Change, + full_denom_path: path1.to_string(), + alias: "newalias1".to_string(), + }], + }; + + let change_info = mock_info(CREATOR_ADDRESS, &[]); + + let change_res = + contract::execute(deps.as_mut(), mock_env(), change_info, change_msg).unwrap(); + + assert_eq!( + DENOM_ALIAS_MAP + .may_load(deps.as_ref().storage, path1) + .unwrap(), + Some(("newalias1".to_string(), true).into()) + ); + assert_eq!( + DENOM_ALIAS_REVERSE_MAP + .may_load(deps.as_ref().storage, "alias1") + .unwrap(), + None + ); + assert_eq!( + DENOM_ALIAS_REVERSE_MAP + .may_load(deps.as_ref().storage, "newalias1") + .unwrap(), + Some((path1.to_string(), true).into()) + ); + + assert_eq!( + change_res.attributes, + vec![( + "change_denom_alias".to_string(), + format!("newalias1 <=> {path1}") + )] + ); + + // Check queries + query_denom_path_for_alias(deps.as_ref(), "alias1").unwrap_err(); + assert_eq!( + query_denom_path_for_alias(deps.as_ref(), "newalias1").unwrap(), + path1 + ); + assert_eq!( + query_alias_for_denom_path(deps.as_ref(), path1).unwrap(), + "newalias1" + ); + + // Test case: Disable an alias + let disable_msg = ExecuteMsg::ModifyDenomAlias { + operations: vec![DenomAliasInput { + operation: FullOperation::Disable, + full_denom_path: path1.to_string(), + alias: "newalias1".to_string(), + }], + }; + + let disable_info = mock_info(CREATOR_ADDRESS, &[]); + let disable_res = + contract::execute(deps.as_mut(), mock_env(), disable_info, disable_msg).unwrap(); + + assert_eq!( + DENOM_ALIAS_MAP + .may_load(deps.as_ref().storage, path1) + .unwrap(), + Some(("newalias1".to_string(), false).into()) + ); + assert_eq!( + DENOM_ALIAS_REVERSE_MAP + .may_load(deps.as_ref().storage, "newalias1") + .unwrap(), + Some((path1.to_string(), false).into()) + ); + + assert_eq!( + disable_res.attributes, + vec![( + "disable_denom_alias".to_string(), + format!("newalias1 <=> {path1}") + )] + ); + + // Check queries + query_denom_path_for_alias(deps.as_ref(), "newalias1").unwrap_err(); + query_alias_for_denom_path(deps.as_ref(), path1).unwrap_err(); + + // Re-enable the alias + let enable_msg = ExecuteMsg::ModifyDenomAlias { + operations: vec![DenomAliasInput { + operation: FullOperation::Enable, + full_denom_path: path1.to_string(), + alias: "doesntmatter".to_string(), + }], + }; + + let enable_info = mock_info(CREATOR_ADDRESS, &[]); + let enable_res = + contract::execute(deps.as_mut(), mock_env(), enable_info, enable_msg).unwrap(); + + assert_eq!( + DENOM_ALIAS_MAP + .may_load(deps.as_ref().storage, path1) + .unwrap(), + Some(("newalias1", true).into()) + ); + assert_eq!( + DENOM_ALIAS_REVERSE_MAP + .may_load(deps.as_ref().storage, "newalias1") + .unwrap(), + Some((path1.to_string(), true).into()) + ); + + assert_eq!( + enable_res.attributes, + vec![( + "enable_denom_alias".to_string(), + format!("newalias1 <=> {path1}") + )] + ); + + assert_eq!( + query_denom_path_for_alias(deps.as_ref(), "newalias1").unwrap(), + path1 + ); + assert_eq!( + query_alias_for_denom_path(deps.as_ref(), path1).unwrap(), + "newalias1" + ); + + // Test case: Remove an alias + let remove_msg = ExecuteMsg::ModifyDenomAlias { + operations: vec![DenomAliasInput { + operation: FullOperation::Remove, + full_denom_path: path1.to_string(), + alias: "unusedaliascanbeanything".to_string(), + }], + }; + + let remove_info = mock_info(CREATOR_ADDRESS, &[]); + let remove_res = + contract::execute(deps.as_mut(), mock_env(), remove_info, remove_msg).unwrap(); + + assert_eq!( + DENOM_ALIAS_MAP + .may_load(deps.as_ref().storage, path1) + .unwrap(), + None + ); + assert_eq!( + DENOM_ALIAS_REVERSE_MAP + .may_load(deps.as_ref().storage, "new_alias1") + .unwrap(), + None + ); + + assert_eq!( + remove_res.attributes, + vec![("remove_denom_alias".to_string(), "newalias1".to_string())] + ); + + query_denom_path_for_alias(deps.as_ref(), "newalias1").unwrap_err(); + query_alias_for_denom_path(deps.as_ref(), path1).unwrap_err(); + } } diff --git a/cosmwasm/contracts/crosschain-registry/src/helpers.rs b/cosmwasm/contracts/crosschain-registry/src/helpers.rs index 9b3a6052583..536851b9a12 100644 --- a/cosmwasm/contracts/crosschain-registry/src/helpers.rs +++ b/cosmwasm/contracts/crosschain-registry/src/helpers.rs @@ -55,6 +55,22 @@ pub fn check_action_permission( Err(ContractError::Unauthorized {}) } +pub fn is_owner(deps: Deps, sender: &Addr) -> bool { + CONFIG + .load(deps.storage) + .map(|config| &config.owner == sender) + .map_err(|_| false) + .unwrap_or(false) +} + +pub fn is_global_admin(deps: Deps, sender: &Addr) -> bool { + GLOBAL_ADMIN_MAP + .load(deps.storage, "osmosis") + .map(|global_admin| &global_admin == sender) + .map_err(|_| false) + .unwrap_or(false) +} + pub fn check_is_authorized( deps: Deps, sender: Addr, @@ -127,6 +143,24 @@ pub fn check_is_chain_maintainer( Err(ContractError::Unauthorized {}) } +fn is_alphanumeric(string: &str) -> bool { + string.chars().all(|c| c.is_alphanumeric()) +} + +pub fn normalize_alias(alias: &str) -> Result { + if !is_alphanumeric(alias) { + return Err(ContractError::InvalidAlias { + alias: alias.into(), + }); + } + if alias.is_empty() { + return Err(ContractError::MissingField { + field: "new_alias".to_string(), + }); + } + Ok(alias.to_lowercase()) +} + // Helper functions to deal with Vec values in cosmwasm maps pub fn push_to_map_value<'a, K, T>( storage: &mut dyn cosmwasm_std::Storage, diff --git a/cosmwasm/contracts/crosschain-registry/src/ibc_lifecycle.rs b/cosmwasm/contracts/crosschain-registry/src/ibc_lifecycle.rs new file mode 100644 index 00000000000..23e4029e131 --- /dev/null +++ b/cosmwasm/contracts/crosschain-registry/src/ibc_lifecycle.rs @@ -0,0 +1,41 @@ +use cosmwasm_std::{DepsMut, Response}; +use registry::Registry; + +use crate::{contract::CONTRACT_CHAIN, state::CHAIN_PFM_MAP, ContractError}; + +pub fn receive_ack( + deps: DepsMut, + source_channel: String, + _sequence: u64, + _ack: String, + success: bool, +) -> Result { + let registry = Registry::default(deps.as_ref()); + let chain = registry.get_connected_chain(CONTRACT_CHAIN, source_channel.as_str())?; + let mut chain_pfm = CHAIN_PFM_MAP.load(deps.storage, &chain).map_err(|_| { + ContractError::ValidationNotFound { + chain: chain.clone(), + } + })?; + + if success { + chain_pfm.acknowledged = true; + CHAIN_PFM_MAP.save(deps.storage, &chain, &chain_pfm)?; + } else { + CHAIN_PFM_MAP.remove(deps.storage, &chain); + } + + Ok(Response::default()) +} + +pub fn receive_timeout( + deps: DepsMut, + source_channel: String, + _sequence: u64, +) -> Result { + let registry = Registry::default(deps.as_ref()); + let chain = registry.get_connected_chain(CONTRACT_CHAIN, source_channel.as_str())?; + CHAIN_PFM_MAP.remove(deps.storage, &chain); + + Ok(Response::default()) +} diff --git a/cosmwasm/contracts/crosschain-registry/src/lib.rs b/cosmwasm/contracts/crosschain-registry/src/lib.rs index ac94946c4d1..c00aae18bb8 100644 --- a/cosmwasm/contracts/crosschain-registry/src/lib.rs +++ b/cosmwasm/contracts/crosschain-registry/src/lib.rs @@ -3,6 +3,7 @@ mod error; pub mod execute; mod exports; pub mod helpers; +mod ibc_lifecycle; pub mod msg; pub mod query; pub mod state; diff --git a/cosmwasm/contracts/crosschain-registry/src/msg.rs b/cosmwasm/contracts/crosschain-registry/src/msg.rs index c7de423f2ee..e0ef72e580a 100644 --- a/cosmwasm/contracts/crosschain-registry/src/msg.rs +++ b/cosmwasm/contracts/crosschain-registry/src/msg.rs @@ -10,6 +10,10 @@ pub struct InstantiateMsg { #[cw_serde] pub enum ExecuteMsg { // Contract Registry + ModifyDenomAlias { + operations: Vec, + }, + ModifyContractAlias { operations: Vec, }, @@ -29,6 +33,12 @@ pub enum ExecuteMsg { operations: Vec, }, + // Add PFM to the registry + #[serde(rename = "propose_pfm")] + ProposePFM { chain: String }, + #[serde(rename = "validate_pfm")] + ValidatePFM { chain: String }, + UnwrapCoin { receiver: String, into_chain: Option, @@ -44,3 +54,32 @@ pub use registry::msg::{ GetDestinationChainFromSourceChainViaChannelResponse, QueryGetBech32PrefixFromChainNameResponse, }; + +#[cw_serde] +pub enum IBCLifecycleComplete { + #[serde(rename = "ibc_ack")] + IBCAck { + /// The source channel (osmosis side) of the IBC packet + channel: String, + /// The sequence number that the packet was sent with + sequence: u64, + /// String encoded version of the ack as seen by OnAcknowledgementPacket(..) + ack: String, + /// Whether an ack is a success of failure according to the transfer spec + success: bool, + }, + #[serde(rename = "ibc_timeout")] + IBCTimeout { + /// The source channel (osmosis side) of the IBC packet + channel: String, + /// The sequence number that the packet was sent with + sequence: u64, + }, +} + +/// Message type for `sudo` entry_point +#[cw_serde] +pub enum SudoMsg { + #[serde(rename = "ibc_lifecycle_complete")] + IBCLifecycleComplete(IBCLifecycleComplete), +} diff --git a/cosmwasm/contracts/crosschain-registry/src/query.rs b/cosmwasm/contracts/crosschain-registry/src/query.rs index d507d62837c..0d58aad323d 100644 --- a/cosmwasm/contracts/crosschain-registry/src/query.rs +++ b/cosmwasm/contracts/crosschain-registry/src/query.rs @@ -1,6 +1,7 @@ use crate::state::{ - CHAIN_TO_BECH32_PREFIX_MAP, CHAIN_TO_BECH32_PREFIX_REVERSE_MAP, CHAIN_TO_CHAIN_CHANNEL_MAP, - CHANNEL_ON_CHAIN_CHAIN_MAP, + CHAIN_PFM_MAP, CHAIN_TO_BECH32_PREFIX_MAP, CHAIN_TO_BECH32_PREFIX_REVERSE_MAP, + CHAIN_TO_CHAIN_CHANNEL_MAP, CHANNEL_ON_CHAIN_CHAIN_MAP, DENOM_ALIAS_MAP, + DENOM_ALIAS_REVERSE_MAP, }; use cosmwasm_std::{Deps, StdError}; @@ -89,3 +90,41 @@ pub fn query_chain_from_channel_chain_pair( Ok(chain.value) } + +pub fn query_chain_has_pfm(deps: Deps, chain: String) -> bool { + let chain = chain.to_lowercase(); + if let Ok(chain_pfm) = CHAIN_PFM_MAP.load(deps.storage, &chain) { + deps.api.debug(&format!("{chain_pfm:?}")); + chain_pfm.is_validated() + } else { + false + } +} + +pub fn query_denom_path_for_alias(deps: Deps, alias: &str) -> Result { + let path = DENOM_ALIAS_REVERSE_MAP + .load(deps.storage, alias) + .map_err(|_| StdError::GenericErr { + msg: format!("alias {alias} cannot be found"), + })?; + + if !path.enabled { + return Err(StdError::GenericErr { + msg: format!("alias {alias} is disabled"), + }); + } + + Ok(path.value) +} + +pub fn query_alias_for_denom_path(deps: Deps, denom_path: &str) -> Result { + let alias = DENOM_ALIAS_MAP.load(deps.storage, denom_path)?; + + if !alias.enabled { + return Err(StdError::generic_err(format!( + "alias for path {denom_path} is disabled" + ))); + } + + Ok(alias.value) +} diff --git a/cosmwasm/contracts/crosschain-registry/src/state.rs b/cosmwasm/contracts/crosschain-registry/src/state.rs index 1fafa24192c..3f1ccb6a65a 100644 --- a/cosmwasm/contracts/crosschain-registry/src/state.rs +++ b/cosmwasm/contracts/crosschain-registry/src/state.rs @@ -13,6 +13,9 @@ enum StorageKey { GlobalAdminMap, ChainAdminMap, ChainMaintainerMap, + HasPacketForwardMiddleware, + DenomAliasMap, + DenomAliasReverseMap, } // Implement the `StorageKey` enum to a string conversion. @@ -28,6 +31,9 @@ impl StorageKey { StorageKey::GlobalAdminMap => "gam", StorageKey::ChainAdminMap => "cam", StorageKey::ChainMaintainerMap => "cmm", + StorageKey::HasPacketForwardMiddleware => "hpfm", + StorageKey::DenomAliasMap => "dam", + StorageKey::DenomAliasReverseMap => "darm", } } } @@ -47,6 +53,27 @@ impl> From<(T, bool)> for RegistryValue { } } +/// ChainPFM stores the state of the packet forward middleware for a chain. Anyone can request +/// to enable the packet forward middleware for a chain, but the contract will verify that +/// packets can properly be forwarded by the chain +#[cw_serde] +#[derive(Default)] +pub struct ChainPFM { + /// The verification packet has been received by the chain, forwarded, and the ack has been received + pub acknowledged: bool, + /// The contract has validated that the received packet is as expected + pub validated: bool, +} + +impl ChainPFM { + /// Both acknowledged and validated must be true for the pfm to be enabled. This is to avoid + /// situations in which the chain calls the contract to set validated to true but that call is + /// not from the same packet that was forwarded by this contract. + pub fn is_validated(&self) -> bool { + self.acknowledged && self.validated + } +} + // CONTRACT_ALIAS_MAP is a map from a contract alias to a contract address pub const CONTRACT_ALIAS_MAP: Map<&str, String> = Map::new(StorageKey::ContractAliasMap.to_string()); @@ -69,9 +96,7 @@ pub const CHANNEL_ON_CHAIN_CHAIN_MAP: Map<(&str, &str), RegistryValue> = pub const CHAIN_TO_BECH32_PREFIX_MAP: Map<&str, RegistryValue> = Map::new(StorageKey::ChainToBech32PrefixMap.to_string()); -// CHAIN_TO_BECH32_PREFIX_MAP is a map from a chain id to its respective bech32 prefix. -// The boolean value indicates whether the mapping is enabled or not. -// CHAIN_ID -> (BECH32_PREFIX, ENABLED) +// CHAIN_TO_BECH32_PREFIX_REVERSE_MAP is a map from a bech32 prefix to the chains that use that prefix pub const CHAIN_TO_BECH32_PREFIX_REVERSE_MAP: Map<&str, Vec> = Map::new(StorageKey::ChainToBech32PrefixReverseMap.to_string()); @@ -79,6 +104,7 @@ pub const CHAIN_TO_BECH32_PREFIX_REVERSE_MAP: Map<&str, Vec> = pub const CONFIG: Item = Item::new(StorageKey::Config.to_string()); // CHAIN_ADMIN_MAP is a map from a source chain to the address that is authorized to add, update, or remove channels for that source chain +// TODO: why isn't this an item? pub const GLOBAL_ADMIN_MAP: Map<&str, Addr> = Map::new(StorageKey::GlobalAdminMap.to_string()); // CHAIN_ADMIN_MAP is a map from a source chain to the address that is authorized to add, update, or remove channels for that source chain @@ -88,6 +114,18 @@ pub const CHAIN_ADMIN_MAP: Map<&str, Addr> = Map::new(StorageKey::ChainAdminMap. pub const CHAIN_MAINTAINER_MAP: Map<&str, Addr> = Map::new(StorageKey::ChainMaintainerMap.to_string()); +// CHAIN_PFM_MAP stores whether a chain supports the Packet Forward Middleware interface for forwarding IBC packets +pub const CHAIN_PFM_MAP: Map<&str, ChainPFM> = + Map::new(StorageKey::HasPacketForwardMiddleware.to_string()); + +// DENOM_ALIAS_MAP is a map from a denom path to a denom alias +pub const DENOM_ALIAS_MAP: Map<&str, RegistryValue> = + Map::new(StorageKey::DenomAliasMap.to_string()); + +// DENOM_ALIAS_REVERSE_MAP is a map from a denom alias to a denom path +pub const DENOM_ALIAS_REVERSE_MAP: Map<&str, RegistryValue> = + Map::new(StorageKey::DenomAliasReverseMap.to_string()); + #[cw_serde] pub struct Config { pub owner: Addr, diff --git a/cosmwasm/contracts/crosschain-swaps/src/error.rs b/cosmwasm/contracts/crosschain-swaps/src/error.rs index cff9bdf9691..eed6b3a3c5e 100644 --- a/cosmwasm/contracts/crosschain-swaps/src/error.rs +++ b/cosmwasm/contracts/crosschain-swaps/src/error.rs @@ -2,6 +2,8 @@ use cosmwasm_std::StdError; use registry::RegistryError; use thiserror::Error; +use crate::FailedDeliveryAction; + #[derive(Error, Debug)] pub enum ContractError { #[error("{0}")] @@ -31,6 +33,11 @@ pub enum ContractError { #[error("invalid receiver: {receiver}")] InvalidReceiver { receiver: String }, + #[error("invalid recovery action: {recovery_action:?}")] + InvalidRecoveryAction { + recovery_action: FailedDeliveryAction, + }, + #[error("invalid json: {error}. Got: {json}")] InvalidJson { error: String, json: String }, diff --git a/cosmwasm/contracts/crosschain-swaps/src/execute.rs b/cosmwasm/contracts/crosschain-swaps/src/execute.rs index 8cd393d5ca2..953e8df273e 100644 --- a/cosmwasm/contracts/crosschain-swaps/src/execute.rs +++ b/cosmwasm/contracts/crosschain-swaps/src/execute.rs @@ -17,6 +17,15 @@ use crate::utils::{build_memo, parse_swaprouter_reply}; use crate::ContractError; use crate::{state, ExecuteMsg}; +use std::fmt::Debug; + +// Helper to add consistent events on ibc message submission +fn ibc_message_event(context: &str, message: T) -> cosmwasm_std::Event { + cosmwasm_std::Event::new("ibc_message_added") + .add_attribute("context", context) + .add_attribute("ibc_message", format!("{message:?}")) +} + /// This function takes any token. If it's already something we can work with /// (either native to osmosis or native to a chain connected to osmosis via a /// valid channel), it will just proceed to swap and forward. If it's not, then @@ -30,8 +39,8 @@ pub fn unwrap_or_swap_and_forward( next_memo: Option, failed_delivery_action: FailedDeliveryAction, ) -> Result { - let (ref deps, ref env, ref info) = ctx; - let swap_coin = cw_utils::one_coin(info)?; + let (deps, env, info) = ctx; + let swap_coin = cw_utils::one_coin(&info)?; deps.api .debug(&format!("executing unwrap or swap and forward")); @@ -48,6 +57,8 @@ pub fn unwrap_or_swap_and_forward( .into()); } + let amount: u128 = swap_coin.amount.into(); + // If the path is larger than 2, we need to unwrap this token first if path.len() > 2 { let registry = Registry::default(deps.as_ref()); @@ -57,7 +68,7 @@ pub fn unwrap_or_swap_and_forward( None, env.contract.address.to_string(), env.block.time, - String::new(), + build_memo(None, env.contract.address.as_str())?, Some(Callback { contract: env.contract.address.clone(), msg: serde_cw_value::to_value(&ExecuteMsg::OsmosisSwap { @@ -65,17 +76,45 @@ pub fn unwrap_or_swap_and_forward( receiver: receiver.to_string(), slippage, next_memo, - on_failed_delivery: failed_delivery_action, + on_failed_delivery: failed_delivery_action.clone(), })? .into(), }), + false, + )?; + + // Ensure the state is properly setup to handle a reply from the ibc_message + save_forward_reply_state( + deps, + ForwardMsgReplyState { + channel_id: ibc_transfer.source_channel.clone(), + to_address: env.contract.address.to_string(), + amount, + denom: String::new(), + on_failed_delivery: failed_delivery_action, + is_swap: false, + }, )?; - return Ok(Response::new().add_message(ibc_transfer)); + + // Here we should add a response for the sender with the packet + // sequence, but that would require habdling the reply. This will be + // unncecessary once async acks lands, so we should wait for that + return Ok(Response::new() + //.set_data(data) + .add_attribute("action", "unwrap_before_swap") + .add_event(ibc_message_event( + "pre-swap unwinding ibc message created", + &ibc_transfer, + )) + .add_submessage(SubMsg::reply_on_success( + ibc_transfer, + MsgReplyID::Forward.repr(), + ))); } // If the denom is either native or only one hop, we swap it directly swap_and_forward( - ctx, + (deps, env, info), swap_coin, output_denom, slippage, @@ -127,6 +166,7 @@ pub fn swap_and_forward( env.block.time, memo, None, + false, )?; // Message to swap tokens in the underlying swaprouter contract @@ -164,7 +204,29 @@ pub fn swap_and_forward( }, )?; - Ok(Response::new().add_submessage(SubMsg::reply_on_success(msg, MsgReplyID::Swap.repr()))) + Ok(Response::new() + .add_attribute("action", "swap_and_forward") + .add_submessage(SubMsg::reply_on_success(msg, MsgReplyID::Swap.repr()))) +} + +fn save_forward_reply_state( + deps: DepsMut, + forward_reply_state: ForwardMsgReplyState, +) -> Result<(), ContractError> { + // Check that there isn't anything stored in FORWARD_REPLY_STATES. If there + // is, it means that the contract is already waiting for a reply and should + // not override the stored state. This should never happen here, but adding + // the check for safety. If this happens there is likely a malicious attempt + // modify the contract's state before it has replied. + if FORWARD_REPLY_STATE.may_load(deps.storage)?.is_some() { + return Err(ContractError::ContractLocked { + msg: "Already waiting for a reply".to_string(), + }); + } + // Store the ibc send information and the user's failed delivery preference + // so that it can be handled by the response + FORWARD_REPLY_STATE.save(deps.storage, &forward_reply_state)?; + Ok(()) } // The swap has succeeded and we need to generate the forward IBC transfer @@ -173,7 +235,6 @@ pub fn handle_swap_reply( env: Env, msg: cosmwasm_std::Reply, ) -> Result { - deps.api.debug(&format!("handle_swap_reply")); let swap_msg_state = SWAP_REPLY_STATE.load(deps.storage)?; SWAP_REPLY_STATE.remove(deps.storage); @@ -200,34 +261,28 @@ pub fn handle_swap_reply( env.block.time, memo, None, + false, )?; deps.api.debug(&format!("IBC transfer: {ibc_transfer:?}")); // Base response let response = Response::new() - .add_attribute("status", "ibc_message_created") - .add_attribute("ibc_message", format!("{ibc_transfer:?}")); - - // Check that there isn't anything stored in FORWARD_REPLY_STATES. If there - // is, it means that the contract is already waiting for a reply and should - // not override the stored state. This should never happen here, but adding - // the check for safety. If this happens there is likely a malicious attempt - // modify the contract's state before it has replied. - if FORWARD_REPLY_STATE.may_load(deps.storage)?.is_some() { - return Err(ContractError::ContractLocked { - msg: "Already waiting for a reply".to_string(), - }); - } - // Store the ibc send information and the user's failed delivery preference - // so that it can be handled by the response - FORWARD_REPLY_STATE.save( - deps.storage, - &ForwardMsgReplyState { + .add_attribute("action", "handle_swap_reply") + .add_event(ibc_message_event( + "forward ibc message added", + &ibc_transfer, + )); + + // Ensure the state is properly setup to handle a reply from the ibc_message + save_forward_reply_state( + deps, + ForwardMsgReplyState { channel_id: ibc_transfer.source_channel.clone(), to_address: swap_msg_state.forward_to.receiver.into(), amount: swap_response.amount.into(), denom: swap_response.token_out_denom, on_failed_delivery: swap_msg_state.forward_to.on_failed_delivery, + is_swap: true, }, )?; @@ -247,6 +302,7 @@ pub fn handle_forward_reply( deps: DepsMut, msg: cosmwasm_std::Reply, ) -> Result { + deps.api.debug(&format!("handle_forward_reply")); // Parse the result from the underlying chain call (IBC send) let SubMsgResult::Ok(SubMsgResponse { data: Some(b), .. }) = msg.result else { return Err(ContractError::FailedIBCTransfer { msg: format!("failed reply: {:?}", msg.result) }) @@ -255,7 +311,7 @@ pub fn handle_forward_reply( // The response contains the packet sequence. This is needed to be able to // ensure that, if there is a delivery failure, the packet that failed is // the same one that we stored recovery information for - let response = + let transfer_response = MsgTransferResponse::decode(&b[..]).map_err(|_e| ContractError::FailedIBCTransfer { msg: format!("could not decode response: {b}"), })?; @@ -266,6 +322,7 @@ pub fn handle_forward_reply( amount, denom, on_failed_delivery: failed_delivery_action, + is_swap, } = FORWARD_REPLY_STATE.load(deps.storage)?; FORWARD_REPLY_STATE.remove(deps.storage); @@ -277,28 +334,48 @@ pub fn handle_forward_reply( let recovery = state::ibc::IBCTransfer { recovery_addr, channel_id: channel_id.clone(), - sequence: response.sequence, + sequence: transfer_response.sequence, amount, denom: denom.clone(), status: state::ibc::PacketLifecycleStatus::Sent, }; // Save as in-flight to be able to manipulate when the ack/timeout is received - INFLIGHT_PACKETS.save(deps.storage, (&channel_id, response.sequence), &recovery)?; + INFLIGHT_PACKETS.save( + deps.storage, + (&channel_id, transfer_response.sequence), + &recovery, + )?; } } - // The response data - let response_data = - CrosschainSwapResponse::new(amount, &denom, &channel_id, &to_address, response.sequence); + let response = Response::new() + .add_attribute("action", "handle_forward_reply") + .add_attribute("status", "ibc_message_successfully_submitted") + .add_attribute("channel", &channel_id) + .add_attribute("receiver", &to_address) + .add_attribute( + "packet_sequence", + format!("{:?}", transfer_response.sequence), + ); + + if !is_swap { + return Ok(response); + } - Ok(Response::new() + // Add the information about the swap + let response_data = CrosschainSwapResponse::new( + amount, + &denom, + &channel_id, + &to_address, + transfer_response.sequence, + ); + + Ok(response .set_data(to_binary(&response_data)?) - .add_attribute("status", "ibc_message_created") .add_attribute("amount", amount.to_string()) - .add_attribute("denom", denom) - .add_attribute("channel", channel_id) - .add_attribute("receiver", to_address)) + .add_attribute("denom", denom)) } /// Transfers any tokens stored in RECOVERY_STATES[sender] to the sender. @@ -307,10 +384,12 @@ pub fn recover(deps: DepsMut, sender: Addr) -> Result { // Remove the recoveries from the store. If the sends fail, the whole tx should be reverted. RECOVERY_STATES.remove(deps.storage, &sender); let msgs = recoveries.into_iter().map(|r| BankMsg::Send { - to_address: r.recovery_addr.into(), + to_address: r.recovery_addr.into_string(), amount: coins(r.amount, r.denom), }); - Ok(Response::new().add_messages(msgs)) + Ok(Response::new() + .add_attribute("action", "recover") + .add_messages(msgs)) } // Transfer ownership of this contract @@ -351,7 +430,7 @@ pub fn set_swap_contract( }, )?; - Ok(Response::new().add_attribute("method", "set_swaps_contract")) + Ok(Response::new().add_attribute("action", "set_swaps_contract")) } #[cfg(test)] diff --git a/cosmwasm/contracts/crosschain-swaps/src/ibc_lifecycle.rs b/cosmwasm/contracts/crosschain-swaps/src/ibc_lifecycle.rs index 7ac33dcd6ab..7d54edc898d 100644 --- a/cosmwasm/contracts/crosschain-swaps/src/ibc_lifecycle.rs +++ b/cosmwasm/contracts/crosschain-swaps/src/ibc_lifecycle.rs @@ -55,7 +55,7 @@ pub fn receive_ack( success: bool, ) -> Result { // deps.api.debug(&format!( - // "received ack for packet {channel:?} {sequence:?}: {ack:?}, {success:?}" + // "received ack for packet {source_channel:?} {sequence:?}: {ack:?}, {success:?}" // )); let response = Response::new() .add_attribute("contract", "crosschain_swaps") diff --git a/cosmwasm/contracts/crosschain-swaps/src/msg.rs b/cosmwasm/contracts/crosschain-swaps/src/msg.rs index f0181ba1af1..3a9aabde591 100644 --- a/cosmwasm/contracts/crosschain-swaps/src/msg.rs +++ b/cosmwasm/contracts/crosschain-swaps/src/msg.rs @@ -21,8 +21,18 @@ pub enum FailedDeliveryAction { /// An osmosis addres used to recover any tokens that get stuck in the /// contract due to IBC failures LocalRecoveryAddr(Addr), + // // Here we could potentially add new actions in the future // example: SendBackToSender, SwapBackAndReturn, etc + // + // If a failure occures, any tokens belonging (either before or after a + // swap) to the user will be sent to `SendTo.address` on `SendTo.chain`. + // If that send fails, the tokens will be recoverable by `SendTo.emergency_recover_addr` + // SendTo { + // chain: String, + // address: String, + // emergency_recovery_addr: String, + // }, } /// message type for `execute` entry_point diff --git a/cosmwasm/contracts/crosschain-swaps/src/state.rs b/cosmwasm/contracts/crosschain-swaps/src/state.rs index 4114bb281d6..614ed4cd6f9 100644 --- a/cosmwasm/contracts/crosschain-swaps/src/state.rs +++ b/cosmwasm/contracts/crosschain-swaps/src/state.rs @@ -35,6 +35,7 @@ pub struct ForwardMsgReplyState { pub amount: u128, pub denom: String, pub on_failed_delivery: FailedDeliveryAction, + pub is_swap: bool, } pub mod ibc { diff --git a/cosmwasm/contracts/crosschain-swaps/src/utils.rs b/cosmwasm/contracts/crosschain-swaps/src/utils.rs index 78d674bab64..7390f9b74e5 100644 --- a/cosmwasm/contracts/crosschain-swaps/src/utils.rs +++ b/cosmwasm/contracts/crosschain-swaps/src/utils.rs @@ -6,10 +6,15 @@ use crate::{consts::CALLBACK_KEY, ContractError}; /// Extract the relevant response from the swaprouter reply pub fn parse_swaprouter_reply(msg: Reply) -> Result { - // If the swaprouter swap failed, return an error + // If the swaprouter faiuled with a known error, return that + if let SubMsgResult::Err(e) = msg.result { + return Err(ContractError::FailedSwap { msg: e }); + }; + + // If the swaprouter swap failed otherwise (no data), return an error let SubMsgResult::Ok(SubMsgResponse { data: Some(b), .. }) = msg.result else { return Err(ContractError::FailedSwap { - msg: format!("No data"), + msg: format!("No data in swaprouter reply"), }) }; diff --git a/cosmwasm/packages/registry/src/error.rs b/cosmwasm/packages/registry/src/error.rs index e926975766b..1471b063c19 100644 --- a/cosmwasm/packages/registry/src/error.rs +++ b/cosmwasm/packages/registry/src/error.rs @@ -15,6 +15,9 @@ pub enum RegistryError { #[error("{0}")] ValueSerialization(ValueSerError), + #[error("{0}")] + Bech32ErrorRaw(#[from] bech32::Error), + // Validation errors #[error("Invalid channel id: {0}")] InvalidChannelId(String), @@ -30,6 +33,9 @@ pub enum RegistryError { #[error("serialization error: {error}")] SerialiaztionError { error: String }, + #[error("registry improperly configured")] + ImproperlyConfigured {}, + #[error("denom {denom:?} is not an IBC denom")] InvalidIBCDenom { denom: String }, @@ -69,20 +75,14 @@ pub enum RegistryError { #[error("no authorized address found for source chain: {source_chain:?}")] ChainAuthorizedAddressDoesNotExist { source_chain: String }, - #[error("chain channel link does not exist: {source_chain:?} -> {destination_chain:?}")] + #[error("channel between chains not registered: {source_chain:?} -> {destination_chain:?}")] ChainChannelLinkDoesNotExist { source_chain: String, destination_chain: String, }, - #[error("channel chain link does not exist: {channel_id:?} on {source_chain:?} -> chain")] - ChannelChainLinkDoesNotExist { - channel_id: String, - source_chain: String, - }, - - #[error("channel chain link does not exist: {channel_id:?} on {source_chain:?} -> chain")] - ChannelToChainChainLinkDoesNotExist { + #[error("channel to chain link not registered: {channel_id:?} on {source_chain:?}")] + ChannelDoesNotExistOnChain { channel_id: String, source_chain: String, }, @@ -92,6 +92,9 @@ pub enum RegistryError { #[error("bech32 prefix does not exist for chain: {chain}")] Bech32PrefixDoesNotExist { chain: String }, + + #[error("Chain {chain} does not support forwarding")] + ForwardingUnsopported { chain: String }, } impl From for StdError { diff --git a/cosmwasm/packages/registry/src/lib.rs b/cosmwasm/packages/registry/src/lib.rs index 35d6aba584c..0568bb6d0e7 100644 --- a/cosmwasm/packages/registry/src/lib.rs +++ b/cosmwasm/packages/registry/src/lib.rs @@ -1,7 +1,9 @@ mod error; mod registry; +pub use crate::registry::derive_wasmhooks_sender; pub use crate::registry::Registry; + pub use error::RegistryError; pub mod msg; diff --git a/cosmwasm/packages/registry/src/msg.rs b/cosmwasm/packages/registry/src/msg.rs index 5d71a48d58b..608af12fe1d 100644 --- a/cosmwasm/packages/registry/src/msg.rs +++ b/cosmwasm/packages/registry/src/msg.rs @@ -1,6 +1,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::Addr; use schemars::JsonSchema; +use serde_json_wasm::from_str; use crate::RegistryError; @@ -30,6 +31,15 @@ pub enum QueryMsg { #[returns(crate::proto::QueryDenomTraceResponse)] GetDenomTrace { ibc_denom: String }, + + #[returns(bool)] + HasPacketForwarding { chain: String }, + + #[returns(QueryAliasForDenomPathResponse)] + GetAliasForDenomPath { denom_path: String }, + + #[returns(QueryDenomPathForAliasResponse)] + GetDenomPathForAlias { alias: String }, } // Response for GetAddressFromAlias query @@ -62,6 +72,18 @@ pub struct QueryGetChainNameFromBech32PrefixResponse { pub chain_name: String, } +// Response for GetAliasForDenomPath query +#[cw_serde] +pub struct QueryAliasForDenomPathResponse { + pub alias: String, +} + +// Response for GetDenomPathForAlias query +#[cw_serde] +pub struct QueryDenomPathForAliasResponse { + pub denom_path: String, +} + // Value does not implement JsonSchema, so we wrap it here. This can be removed // if https://github.com/CosmWasm/serde-cw-value/pull/3 gets merged #[derive( @@ -96,6 +118,14 @@ impl From for SerializableJson { } } +impl TryFrom for SerializableJson { + type Error = RegistryError; + + fn try_from(value: String) -> Result { + Ok(Self(from_str(&value)?)) + } +} + /// Information about which contract to call when the crosschain swap finishes #[cw_serde] pub struct Callback { diff --git a/cosmwasm/packages/registry/src/registry.rs b/cosmwasm/packages/registry/src/registry.rs index 6ac40eae331..ff00933c4ef 100644 --- a/cosmwasm/packages/registry/src/registry.rs +++ b/cosmwasm/packages/registry/src/registry.rs @@ -19,6 +19,44 @@ pub fn hash_denom_trace(unwrapped: &str) -> String { format!("ibc/{}", hash.to_uppercase()) } +// When a contract is called using ibc callbacks, the addres is a combination of +// the channel and the original sender. This function lets us compute that. +pub fn derive_wasmhooks_sender( + channel: &str, + original_sender: &str, + bech32_prefix: &str, +) -> Result { + let sender = format!("{channel}/{original_sender}"); + + let mut hasher0 = Sha256::new(); + hasher0.update("ibc-wasm-hook-intermediary".as_bytes()); + let th = hasher0.finalize(); + + let mut hasher = Sha256::new(); + hasher.reset(); + hasher.update(th.as_slice()); + hasher.update(sender.as_bytes()); + + let result = hasher.finalize(); + + // The bech32 crate requires a Vec as input, so we need to convert the bytes. + let result_u5 = bech32::convert_bits(result.as_slice(), 8, 5, true)?; + // result_u5 contains the bytes as a u5 but in an u8 type, so we need to explicitly + // do the type conversion + let result_u5: Vec = result_u5 + .iter() + .filter_map(|&x| bech32::u5::try_from_u8(x).ok()) + .collect(); + + bech32::encode(bech32_prefix, result_u5, bech32::Variant::Bech32).map_err(|e| { + RegistryError::Bech32Error { + action: "encoding".into(), + addr: original_sender.into(), + source: e, + } + }) +} + // IBC transfer port const TRANSFER_PORT: &str = "transfer"; // IBC timeout @@ -101,6 +139,9 @@ pub struct MultiHopDenom { pub via: Option, // This is optional because native tokens have no channel } +// The name of the chain on which the contract using this lib is instantiated +pub const CONTRACT_CHAIN: &str = "osmosis"; + pub struct Registry<'a> { pub deps: Deps<'a>, pub registry_contract: String, @@ -115,7 +156,6 @@ impl<'a> Registry<'a> { }) } - #[allow(dead_code)] pub fn default(deps: Deps<'a>) -> Self { Self { deps, @@ -163,7 +203,7 @@ impl<'a> Registry<'a> { via_channel: via_channel.to_string(), }, ) - .map_err(|_e| RegistryError::ChannelToChainChainLinkDoesNotExist { + .map_err(|_e| RegistryError::ChannelDoesNotExistOnChain { channel_id: via_channel.to_string(), source_chain: on_chain.to_string(), }) @@ -188,6 +228,20 @@ impl<'a> Registry<'a> { }) } + /// Returns a boolean specifying if a chain supports forwarding + /// Example: supports_forwarding("gaia") -> true + pub fn supports_forwarding(&self, chain: &str) -> Result { + self.deps + .querier + .query_wasm_smart( + &self.registry_contract, + &QueryMsg::HasPacketForwarding { + chain: chain.to_string(), + }, + ) + .map_err(|_e| RegistryError::ImproperlyConfigured {}) + } + /// Re-encodes the bech32 address for the receiving chain /// Example: encode_addr_for_chain("osmo1...", "juno") -> "juno1..." pub fn encode_addr_for_chain(&self, addr: &str, chain: &str) -> Result { @@ -262,7 +316,7 @@ impl<'a> Registry<'a> { pub fn unwrap_denom_path(&self, denom: &str) -> Result, RegistryError> { self.debug(format!("Unwrapping denom {denom}")); - let mut current_chain = "osmosis".to_string(); // The initial chain is always osmosis + let mut current_chain = CONTRACT_CHAIN.to_string(); // The initial chain is always the contract chain // Check that the denom is an IBC denom if !denom.starts_with("ibc/") { @@ -289,9 +343,7 @@ impl<'a> Registry<'a> { }), }?; - self.deps - .api - .debug(&format!("procesing denom trace {path}")); + self.debug(format!("procesing denom trace {path}")); // Let's iterate over the parts of the denom trace and extract the // chain/channels into a more useful structure: MultiHopDenom let mut hops: Vec = vec![]; @@ -331,6 +383,21 @@ impl<'a> Registry<'a> { Ok(hops) } + pub fn get_native_chain(&self, denom: &str) -> Result { + let hops = self.unwrap_denom_path(denom)?; + self.debug(format!("hops: {hops:?}")); + // verify that the last hop is native + let last_hop = hops.last().ok_or(RegistryError::NoDenomTrace { + denom: denom.into(), + })?; + if last_hop.via.is_some() { + return Err(RegistryError::InvalidDenomTrace { + error: format!("Path {hops:?} is not properly formatted"), + }); + } + Ok(hops.last().unwrap().on.clone()) + } + /// Returns an IBC MsgTransfer that with a packet forward middleware memo /// that will send the coin back to its original chain and then to the /// receiver in `into_chain`. @@ -354,6 +421,7 @@ impl<'a> Registry<'a> { block_time: Timestamp, first_transfer_memo: String, receiver_callback: Option, + skip_forwarding_check: bool, ) -> Result { // Calculate the path that this coin took to get to the current chain. // Each element in the path is an IBC hop. @@ -470,6 +538,17 @@ impl<'a> Registry<'a> { None => ChannelId(self.get_channel(prev_chain, hop.on.as_ref())?), }; + self.debug(format!( + "checking that: {:?} supports pfm (into {:?})", + hop.on.as_ref(), + prev_chain + )); + if !skip_forwarding_check && !self.supports_forwarding(hop.on.as_ref())? { + return Err(RegistryError::ForwardingUnsopported { + chain: hop.on.as_ref().into(), + }); + } + // The next memo wraps the previous one next = Some(Box::new(Memo { forward: Some(ForwardingMemo { @@ -577,4 +656,39 @@ mod test { r#"{"forward":{"receiver":"receiver","port":"port","channel":"channel-0","next":{"forward":{"receiver":"receiver2","port":"port2","channel":"channel-1"}}}}"# ) } + + #[test] + fn test_derive_wasmhooks_sender() { + let test_cases = vec![ + ( + "channel-0", + "cosmos1tfejvgp5yzd8ypvn9t0e2uv2kcjf2laa8upya8", + "osmo", + "osmo1sguz3gtyl2tjsdulwxmtprd68xtd43yyep6g5c554utz642sr8rqcgw0q6", + ), + ( + "channel-1", + "cosmos1tfejvgp5yzd8ypvn9t0e2uv2kcjf2laa8upya8", + "osmo", + "osmo1svnare87kluww5hnltv24m4dg72hst0qqwm5xslsvnwd22gftcussaz5l7", + ), + ( + "channel-0", + "osmo12smx2wdlyttvyzvzg54y2vnqwq2qjateuf7thj", + "osmo", + "osmo1vz8evs4ek3vnz4f8wy86nw9ayzn67y28vtxzjgxv6achc4pa8gesqldfz0", + ), + ( + "channel-0", + "osmo12smx2wdlyttvyzvzg54y2vnqwq2qjateuf7thj", + "cosmos", + "cosmos1vz8evs4ek3vnz4f8wy86nw9ayzn67y28vtxzjgxv6achc4pa8ges4z434f", + ), + ]; + + for tc in test_cases { + assert!(derive_wasmhooks_sender(tc.0, tc.1, tc.2).is_ok()); + assert_eq!(derive_wasmhooks_sender(tc.0, tc.1, tc.2).unwrap(), tc.3); + } + } } diff --git a/docs/protocol/book.toml b/docs/protocol/book.toml new file mode 100644 index 00000000000..c94f58c7ffe --- /dev/null +++ b/docs/protocol/book.toml @@ -0,0 +1,16 @@ +[book] +authors = ["Osmosis Labs"] +language = "en" +multilingual = false +src = "src" +title = "The Osmosis Protocol" + +[preprocessor.katex] + +[preprocessor.mermaid] +command = "mdbook-mermaid" + +[output.html] +curly-quotes = true +fold = { enable = true, level = 1 } +git-repository-url = "https://github.com/osmosis-labs/osmosis" diff --git a/docs/protocol/src/SUMMARY.md b/docs/protocol/src/SUMMARY.md new file mode 100644 index 00000000000..88f1c283c13 --- /dev/null +++ b/docs/protocol/src/SUMMARY.md @@ -0,0 +1,12 @@ +# Summary + +[Osmosis](./osmosis.md) +- [Decentralized Exchange](./core_dex.md) + - [Weighted Pool](./dex/weighted.md) + - [Stableswap Pool](./dex/stableswap.md) + - [Concentrated Pool](./dex/concentrated.md) + - [Cosmwasm Pool](./dex/cosmwasm.md) +- [Governance](./governance.md) +- [Staking](./staking.md) +- [Smart Contracts](./smartcontracts.md) +- [Ecosystem](./ecosystem.md) diff --git a/docs/protocol/src/core_dex.md b/docs/protocol/src/core_dex.md new file mode 100644 index 00000000000..8a32582c463 --- /dev/null +++ b/docs/protocol/src/core_dex.md @@ -0,0 +1,12 @@ +# Decentralized Exchange + +Osmosis is a decentralized automated market maker (AMM) protocol built using Cosmos SDK that represents a flexible building block for programmable liquidity. + +By separating the AMM curve logic and math from the core swapping functionality, Osmosis becomes an extensible AMM that can incorporate any number of swap curves and pool types. This includes: + +- Traditional 50/50 weighted pools +- Custom weights like 80/20 for controlled exposure +- Solidly-style Stableswap curve +- Concentrated Liquidity pools +- CosmWasm pools + diff --git a/docs/protocol/src/dex/concentrated.md b/docs/protocol/src/dex/concentrated.md new file mode 100644 index 00000000000..d319e9e23c8 --- /dev/null +++ b/docs/protocol/src/dex/concentrated.md @@ -0,0 +1 @@ +# Concentrated Pool diff --git a/docs/protocol/src/dex/cosmwams.md b/docs/protocol/src/dex/cosmwams.md new file mode 100644 index 00000000000..a0361e071aa --- /dev/null +++ b/docs/protocol/src/dex/cosmwams.md @@ -0,0 +1 @@ +# CosmWasm Pool diff --git a/docs/protocol/src/dex/cosmwasm.md b/docs/protocol/src/dex/cosmwasm.md new file mode 100644 index 00000000000..8f9da51e6be --- /dev/null +++ b/docs/protocol/src/dex/cosmwasm.md @@ -0,0 +1 @@ +# Cosmwasm Pool diff --git a/docs/protocol/src/dex/stableswap.md b/docs/protocol/src/dex/stableswap.md new file mode 100644 index 00000000000..76fe3b47606 --- /dev/null +++ b/docs/protocol/src/dex/stableswap.md @@ -0,0 +1 @@ +# Stableswap Pool diff --git a/docs/protocol/src/dex/weighted.md b/docs/protocol/src/dex/weighted.md new file mode 100644 index 00000000000..cfdde988e32 --- /dev/null +++ b/docs/protocol/src/dex/weighted.md @@ -0,0 +1,7 @@ +# Weighted Pool + +Liquidity pools are clusters of tokens with pre-determined weights. A token's weight is how much its value accounts for the total value within the pool. For example, Uniswap pools involve two tokens with 50-50 weights. The total value of Asset A must remain equal to the total value of Asset B. Other token weights are possible, such as 90-10. It is also possible to have a liquidity pool with more than two assets. + +In Osmosis, pool creators are allowed to choose the tokens within the pool and their respective weights. The parameters chosen by the pool creator cannot be changed. Other users can create separate pools with different parameters. + +Weighted Pools are an extension of the classical AMM pools popularized by Uniswap v1. Weighted Pools are great for general cases, including tokens that don't necessarily have any price correlation (ex. DAI/WETH). Unlike pools in other AMMs that only provide 50/50 weightings, Osmosis Weighted Pools enable users to build pools with more than two tokens and custom weightings, such as pools with 80/20 or 60/20/20 weightings. diff --git a/docs/protocol/src/ecosystem.md b/docs/protocol/src/ecosystem.md new file mode 100644 index 00000000000..e46e912ed82 --- /dev/null +++ b/docs/protocol/src/ecosystem.md @@ -0,0 +1 @@ +# Ecosystem diff --git a/docs/protocol/src/governance.md b/docs/protocol/src/governance.md new file mode 100644 index 00000000000..66109a0f3d8 --- /dev/null +++ b/docs/protocol/src/governance.md @@ -0,0 +1,17 @@ +# Governance + +Osmosis is a sovereign delegated Proof-of-Stake chain with its own validator set. +Users delegate their stake to validators to secure the network while keeping the +opportunity to overwrite validator's decision with their own vote. + +The decisions are made through the governance process consisting of proposals. + +A proposal can be created by anyone. Once created with the required deposit, the +voting period begins. + +The proposal is either accepted or rejected. + +There are two types of proposals that determine the length of the voting period, +deposit amount and quorum: +1. Standard proposal +2. Expedited proposal diff --git a/docs/protocol/src/osmosis.md b/docs/protocol/src/osmosis.md new file mode 100644 index 00000000000..d558c222eb7 --- /dev/null +++ b/docs/protocol/src/osmosis.md @@ -0,0 +1,31 @@ +# Introduction to Osmosis + +Osmosis is a fair-launched, customizable automated market maker for +interchain assets that allows the creation and management of +non-custodial, self-balancing, interchain token index similar to one of +Balancer. + +Inspired by [Balancer](http://balancer.finance/whitepaper) and Sunny +Aggarwal's '[DAOfying Uniswap Automated Market Maker +Pools](https://www.sunnya97.com/blog/daoifying-uniswap-automated-market-maker-pools)', +the goal for Osmosis is to provide the best-in-class tools that extend +the use of AMMs within the Cosmos ecosystem beyond traditional token +swap-type use cases. Bonding curves, while have found its primary use +case in decentralized exchange mechanisms, its potential use case can be +further extended through the customizability that Osmosis offers. +Through the customizability offered by Osmosis such as custom-curve AMMs, +dynamic adjustments of spread factors, multi-token liquidity pools--the AMM +can offer decentralized formation of token fundraisers, interchain +staking, options market, and more for the Cosmos ecosystem. + +Whereas most Cosmos zones have focused their incentive scheme on the +delegators, Osmosis attempts to align the interests of multiple +stakeholders of the ecosystem such as LPs, DAO members, as well as +delegators. One mechanism that is introduced is how staked liquidity +providers have sovereign ownership over their pools, and through the +pool governance process allow them to adjust the parameters depending on +the pool's competition and market conditions. Osmosis is a sovereign +Cosmos zone that derives its sovereignty not only from its +application-specific blockchain architecture but also the collective +sovereignty of the LPs that has aligned interest to different tokens +that they are providing liquidity for. diff --git a/docs/protocol/src/smartcontracts.md b/docs/protocol/src/smartcontracts.md new file mode 100644 index 00000000000..95badcb05e1 --- /dev/null +++ b/docs/protocol/src/smartcontracts.md @@ -0,0 +1 @@ +# Smart Contracts (CosmWasm) diff --git a/docs/protocol/src/staking.md b/docs/protocol/src/staking.md new file mode 100644 index 00000000000..abe5cd84eb4 --- /dev/null +++ b/docs/protocol/src/staking.md @@ -0,0 +1 @@ +# Staking diff --git a/go.mod b/go.mod index dfc50e9c9ea..e7d58394416 100644 --- a/go.mod +++ b/go.mod @@ -5,39 +5,39 @@ go 1.20 require ( cosmossdk.io/errors v1.0.0 github.com/CosmWasm/wasmd v0.31.0 - github.com/cosmos/cosmos-proto v1.0.0-beta.2 + github.com/cosmos/cosmos-proto v1.0.0-beta.3 github.com/cosmos/cosmos-sdk v0.47.4 github.com/cosmos/go-bip39 v1.0.0 - github.com/cosmos/ibc-apps/modules/async-icq/v4 v4.0.0-20230524151648-c02fa46c2860 + github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v4 v4.1.0 + github.com/cosmos/ibc-apps/modules/async-icq/v4 v4.1.0 github.com/cosmos/ibc-go/v4 v4.3.1 github.com/gogo/protobuf v1.3.3 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.3 - github.com/golangci/golangci-lint v1.52.2 + github.com/golangci/golangci-lint v1.54.1 github.com/gorilla/mux v1.8.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/iancoleman/orderedmap v0.3.0 github.com/mattn/go-sqlite3 v1.14.17 github.com/ory/dockertest/v3 v3.10.0 github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3 - github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230629191111-f375469de8b6 - github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230801224523-e85e9a9cf445 + github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230804142026-a81cfe3ddde7 + github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230807183608-16c217dedde5 github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304 - github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.0-20230602130523-f9a94d8bbd10 + github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.0-20230807183608-16c217dedde5 github.com/pkg/errors v0.9.1 github.com/rakyll/statik v0.1.7 github.com/spf13/cast v1.5.1 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.16.0 - github.com/strangelove-ventures/packet-forward-middleware/v4 v4.0.5 github.com/stretchr/testify v1.8.4 github.com/tendermint/tendermint v0.37.0-rc1 github.com/tendermint/tm-db v0.6.8-0.20220506192307-f628bb5dc95b github.com/tidwall/btree v1.6.0 - github.com/tidwall/gjson v1.14.4 + github.com/tidwall/gjson v1.16.0 go.uber.org/multierr v1.11.0 - golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 + golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 google.golang.org/grpc v1.56.2 gopkg.in/yaml.v2 v2.4.0 @@ -46,11 +46,16 @@ require ( require ( 4d63.com/gocheckcompilerdirectives v1.2.1 // indirect - github.com/Abirdcfly/dupword v0.0.11 // indirect + github.com/4meepo/tagalign v1.3.2 // indirect + github.com/Abirdcfly/dupword v0.0.12 // indirect github.com/Djarvur/go-err113 v0.1.0 // indirect + github.com/GaijinEntertainment/go-exhaustruct/v3 v3.1.0 // indirect + github.com/OpenPeeDeeP/depguard/v2 v2.1.0 // indirect + github.com/alexkohler/nakedret/v2 v2.0.2 // indirect github.com/alingse/asasalint v0.0.11 // indirect github.com/benbjohnson/clock v1.3.0 // indirect - github.com/bytedance/sonic v1.9.1 // indirect + github.com/butuzov/mirror v1.1.0 // indirect + github.com/bytedance/sonic v1.10.0 // indirect github.com/cosmos/gogoproto v1.4.6 // indirect github.com/cosmos/iavl v0.19.5 // indirect github.com/creachadair/taskgroup v0.3.2 // indirect @@ -61,10 +66,9 @@ require ( github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/gateway v1.1.0 // indirect github.com/google/btree v1.1.2 // indirect - github.com/junk1tm/musttag v0.5.0 // indirect github.com/kkHAIKE/contextcheck v1.1.4 // indirect github.com/maratori/testableexamples v1.0.0 // indirect - github.com/nunnatsa/ginkgolinter v0.9.0 // indirect + github.com/nunnatsa/ginkgolinter v0.13.3 // indirect github.com/regen-network/cosmos-proto v0.3.1 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect github.com/sashamelentyev/usestdlibvars v1.23.0 // indirect @@ -74,12 +78,14 @@ require ( github.com/tidwall/pretty v1.2.0 // indirect github.com/timonwong/loggercheck v0.9.4 // indirect github.com/ugorji/go/codec v1.2.11 // indirect + github.com/xen0n/gosmopolitan v1.2.1 // indirect + github.com/ykadowak/zerologlint v0.1.3 // indirect github.com/zimmski/go-mutesting v0.0.0-20210610104036-6d9217011a00 // indirect github.com/zondax/ledger-go v0.14.1 // indirect + go.tmz.dev/musttag v0.7.1 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/goleak v1.1.12 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/arch v0.3.0 // indirect google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 // indirect ) @@ -89,31 +95,29 @@ require ( filippo.io/edwards25519 v1.0.0-rc.1 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect - github.com/Antonboom/errname v0.1.9 // indirect - github.com/Antonboom/nilnil v0.1.3 // indirect + github.com/Antonboom/errname v0.1.10 // indirect + github.com/Antonboom/nilnil v0.1.5 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/BurntSushi/toml v1.2.1 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect github.com/CosmWasm/wasmvm v1.2.1 - github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect - github.com/OpenPeeDeeP/depguard v1.1.1 // indirect github.com/Workiva/go-datastructures v1.0.53 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/ashanbrown/forbidigo v1.5.1 // indirect + github.com/ashanbrown/forbidigo v1.6.0 // indirect github.com/ashanbrown/makezero v1.1.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect - github.com/bkielbasa/cyclop v1.2.0 // indirect + github.com/bkielbasa/cyclop v1.2.1 // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect github.com/bombsimon/wsl/v3 v3.4.0 // indirect github.com/breml/bidichk v0.2.4 // indirect github.com/breml/errchkjson v0.3.1 // indirect github.com/btcsuite/btcd v0.22.3 // indirect - github.com/butuzov/ireturn v0.1.1 // indirect + github.com/butuzov/ireturn v0.2.0 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -125,7 +129,7 @@ require ( github.com/cosmos/btcutil v1.0.5 github.com/cosmos/gorocksdb v1.2.0 // indirect github.com/cosmos/ledger-cosmos-go v0.12.2 // indirect - github.com/daixiang0/gci v0.10.1 // indirect + github.com/daixiang0/gci v0.11.0 // 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 @@ -145,7 +149,7 @@ require ( github.com/firefart/nonamedreturns v1.0.4 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect - github.com/go-critic/go-critic v0.7.0 // indirect + github.com/go-critic/go-critic v0.9.0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect @@ -169,7 +173,7 @@ require ( github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 // indirect github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 // indirect github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca // indirect - github.com/golangci/misspell v0.4.0 // indirect + github.com/golangci/misspell v0.4.1 // indirect github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 // indirect github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect github.com/google/flatbuffers v2.0.8+incompatible // indirect @@ -177,7 +181,7 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28 // indirect + github.com/gordonklaus/ineffassign v0.0.0-20230610083614-0e73809eb601 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect @@ -210,12 +214,12 @@ require ( github.com/kisielk/gotool v1.0.0 // indirect github.com/klauspost/compress v1.15.11 // indirect github.com/kulti/thelper v0.6.3 // indirect - github.com/kunwardeep/paralleltest v1.0.6 // indirect + github.com/kunwardeep/paralleltest v1.0.8 // indirect github.com/kyoh86/exportloopref v0.1.11 // indirect github.com/ldez/gomoddirectives v0.2.3 // indirect - github.com/ldez/tagliatelle v0.4.0 // indirect + github.com/ldez/tagliatelle v0.5.0 // indirect github.com/leonklingele/grouper v1.1.1 // indirect - github.com/lib/pq v1.10.7 // indirect + github.com/lib/pq v1.10.9 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/lufeee/execinquery v1.2.1 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -226,7 +230,7 @@ require ( github.com/mattn/go-runewidth v0.0.10 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mbilski/exhaustivestruct v1.2.0 // indirect - github.com/mgechev/revive v1.3.1 // indirect + github.com/mgechev/revive v1.3.2 // 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 @@ -236,7 +240,7 @@ require ( github.com/mtibben/percent v0.2.1 // indirect github.com/nakabonne/nestif v0.3.1 // indirect github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect - github.com/nishanths/exhaustive v0.9.5 // indirect + github.com/nishanths/exhaustive v0.11.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -245,12 +249,12 @@ require ( github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/polyfloyd/go-errorlint v1.4.0 // indirect + github.com/polyfloyd/go-errorlint v1.4.3 // indirect github.com/prometheus/client_golang v1.16.0 github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect - github.com/quasilyte/go-ruleguard v0.3.19 // indirect + github.com/quasilyte/go-ruleguard v0.4.0 // indirect github.com/quasilyte/gogrep v0.5.0 // indirect github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect @@ -262,10 +266,10 @@ require ( github.com/ryanrolds/sqlclosecheck v0.4.0 // indirect github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect - github.com/securego/gosec/v2 v2.15.0 // indirect + github.com/securego/gosec/v2 v2.16.0 // indirect github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect - github.com/sirupsen/logrus v1.9.0 // indirect - github.com/sivchari/containedctx v1.0.2 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sivchari/containedctx v1.0.3 // indirect github.com/sivchari/tenv v1.7.1 // indirect github.com/sonatard/noctx v0.0.2 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect @@ -281,12 +285,12 @@ require ( github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 // indirect github.com/tendermint/go-amino v0.16.0 // indirect github.com/tetafro/godot v1.4.11 // indirect - github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e // indirect + github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect github.com/tomarrell/wrapcheck/v2 v2.8.1 // indirect github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect - github.com/ultraware/funlen v0.0.3 // indirect + github.com/ultraware/funlen v0.1.0 // indirect github.com/ultraware/whitespace v0.0.5 // indirect - github.com/uudashr/gocognit v1.0.6 // indirect + github.com/uudashr/gocognit v1.0.7 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect @@ -295,18 +299,18 @@ require ( github.com/zimmski/go-tool v0.0.0-20150119110811-2dfdc9ac8439 // indirect github.com/zimmski/osutil v0.0.0-20190128123334-0d0b3ca231ac // indirect github.com/zondax/hid v0.9.1 // indirect - gitlab.com/bosi/decorder v0.2.3 // indirect + gitlab.com/bosi/decorder v0.4.0 // indirect go.etcd.io/bbolt v1.3.6 // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/crypto v0.11.0 // indirect - golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2 // indirect - golang.org/x/mod v0.10.0 // indirect - golang.org/x/net v0.12.0 // indirect - golang.org/x/sync v0.2.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/term v0.10.0 // indirect - golang.org/x/text v0.11.0 // indirect - golang.org/x/tools v0.8.0 // indirect + golang.org/x/crypto v0.12.0 // indirect + golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/net v0.14.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/term v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect + golang.org/x/tools v0.12.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index fb3f5d58a65..18c33bac411 100644 --- a/go.sum +++ b/go.sum @@ -49,24 +49,26 @@ filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmG filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw= git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9/go.mod h1:BVJwbDfVjCjoFiKrhkei6NdGcZYpkDkdyCdg1ukytRA= +github.com/4meepo/tagalign v1.3.2 h1:1idD3yxlRGV18VjqtDbqYvQ5pXqQS0wO2dn6M3XstvI= +github.com/4meepo/tagalign v1.3.2/go.mod h1:Q9c1rYMZJc9dPRkbQPpcBNCLEmY2njbAsXhQOZFE2dE= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= -github.com/Abirdcfly/dupword v0.0.11 h1:z6v8rMETchZXUIuHxYNmlUAuKuB21PeaSymTed16wgU= -github.com/Abirdcfly/dupword v0.0.11/go.mod h1:wH8mVGuf3CP5fsBTkfWwwwKTjDnVVCxtU8d8rgeVYXA= -github.com/Antonboom/errname v0.1.9 h1:BZDX4r3l4TBZxZ2o2LNrlGxSHran4d1u4veZdoORTT4= -github.com/Antonboom/errname v0.1.9/go.mod h1:nLTcJzevREuAsgTbG85UsuiWpMpAqbKD1HNZ29OzE58= -github.com/Antonboom/nilnil v0.1.3 h1:6RTbx3d2mcEu3Zwq9TowQpQMVpP75zugwOtqY1RTtcE= -github.com/Antonboom/nilnil v0.1.3/go.mod h1:iOov/7gRcXkeEU+EMGpBu2ORih3iyVEiWjeste1SJm8= +github.com/Abirdcfly/dupword v0.0.12 h1:56NnOyrXzChj07BDFjeRA+IUzSz01jmzEq+G4kEgFhc= +github.com/Abirdcfly/dupword v0.0.12/go.mod h1:+us/TGct/nI9Ndcbcp3rgNcQzctTj68pq7TcgNpLfdI= +github.com/Antonboom/errname v0.1.10 h1:RZ7cYo/GuZqjr1nuJLNe8ZH+a+Jd9DaZzttWzak9Bls= +github.com/Antonboom/errname v0.1.10/go.mod h1:xLeiCIrvVNpUtsN0wxAh05bNIZpqE22/qDMnTBTttiA= +github.com/Antonboom/nilnil v0.1.5 h1:X2JAdEVcbPaOom2TUa1FxZ3uyuUlex0XMLGYMemu6l0= +github.com/Antonboom/nilnil v0.1.5/go.mod h1:I24toVuBKhfP5teihGWctrRiPbRKHwZIFOvc6v3HZXk= github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -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/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/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= @@ -77,8 +79,8 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3 github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Djarvur/go-err113 v0.1.0 h1:uCRZZOdMQ0TZPHYTdYpoC0bLYJKPEHPUJ8MeAa51lNU= github.com/Djarvur/go-err113 v0.1.0/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= -github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 h1:+r1rSv4gvYn0wmRjC8X7IAzX8QezqtFV9m0MUHFJgts= -github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0/go.mod h1:b3g59n2Y+T5xmcxJL+UEG2f8cQploZm1mR/v6BW0mU0= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.1.0 h1:3ZBs7LAezy8gh0uECsA6CGU43FF3zsx5f4eah5FxTMA= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.1.0/go.mod h1:rZLTje5A9kFBe0pzhpe2TdhRniBF++PRHQuRpR8esVc= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= @@ -88,8 +90,8 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEV github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OpenPeeDeeP/depguard v1.1.1 h1:TSUznLjvp/4IUP+OQ0t/4jF4QUyxIcVX8YnghZdunyA= -github.com/OpenPeeDeeP/depguard v1.1.1/go.mod h1:JtAMzWkmFEzDPyAd+W0NHl1lvpQKTvT9jnRVsohBKpc= +github.com/OpenPeeDeeP/depguard/v2 v2.1.0 h1:aQl70G173h/GZYhWf36aE5H0KaujXfVMnn/f1kSDVYY= +github.com/OpenPeeDeeP/depguard/v2 v2.1.0/go.mod h1:PUBgk35fX4i7JDmwzlJwJ+GMe6NfO1723wmJMgPThNQ= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= @@ -108,6 +110,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alexkohler/nakedret/v2 v2.0.2 h1:qnXuZNvv3/AxkAb22q/sEsEpcA99YxLFACDtEw9TPxE= +github.com/alexkohler/nakedret/v2 v2.0.2/go.mod h1:2b8Gkk0GsOrqQv/gPWjNLDSKwG8I5moSXG1K4VIBcTQ= github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= @@ -125,8 +129,8 @@ github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJ github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/ashanbrown/forbidigo v1.5.1 h1:WXhzLjOlnuDYPYQo/eFlcFMi8X/kLfvWLYu6CSoebis= -github.com/ashanbrown/forbidigo v1.5.1/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= +github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY= +github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s= github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= @@ -150,8 +154,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bkielbasa/cyclop v1.2.0 h1:7Jmnh0yL2DjKfw28p86YTd/B4lRGcNuu12sKE35sM7A= -github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= +github.com/bkielbasa/cyclop v1.2.1 h1:AeF71HZDob1P2/pRm1so9cd1alZnrpyc4q2uP2l0gJY= +github.com/bkielbasa/cyclop v1.2.1/go.mod h1:K/dT/M0FPAiYjBgQGau7tz+3TMh4FWAEqlMhzFWCrgM= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= @@ -186,12 +190,15 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY= -github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= +github.com/butuzov/ireturn v0.2.0 h1:kCHi+YzC150GE98WFuZQu9yrTn6GEydO2AuPLbTgnO4= +github.com/butuzov/ireturn v0.2.0/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= +github.com/butuzov/mirror v1.1.0 h1:ZqX54gBVMXu78QLoiqdwpl2mgmoOJTk7s4p4o+0avZI= +github.com/butuzov/mirror v1.1.0/go.mod h1:8Q0BdQU6rC6WILDiBM60DBfvV78OLJmMmixe7GF45AE= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.0 h1:qtNZduETEIWJVIyDl01BeNxur2rW9OwTQ/yBqFRkKEk= +github.com/bytedance/sonic v1.10.0/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= @@ -213,8 +220,11 @@ github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 h1:W9o46d2kbNL06lq github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8/go.mod h1:gakxgyXaaPkxvLw1XQxNGK4I37ys9iBRzNUx/B7pUCo= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -245,8 +255,8 @@ github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1 github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= -github.com/cosmos/cosmos-proto v1.0.0-beta.2 h1:X3OKvWgK9Gsejo0F1qs5l8Qn6xJV/AzgIWR2wZ8Nua8= -github.com/cosmos/cosmos-proto v1.0.0-beta.2/go.mod h1:+XRCLJ14pr5HFEHIUcn51IKXD1Fy3rkEQqt4WqmN4V0= +github.com/cosmos/cosmos-proto v1.0.0-beta.3 h1:VitvZ1lPORTVxkmF2fAp3IiA61xVwArQYKXTdEcpW6o= +github.com/cosmos/cosmos-proto v1.0.0-beta.3/go.mod h1:t8IASdLaAq+bbHbjq4p960BvcTqtwuAxid3b/2rOD6I= github.com/cosmos/cosmos-sdk/ics23/go v0.8.0 h1:iKclrn3YEOwk4jQHT2ulgzuXyxmzmPczUalMwW4XH9k= github.com/cosmos/cosmos-sdk/ics23/go v0.8.0/go.mod h1:2a4dBq88TUoqoWAU5eu0lGvpFP3wWDPgdHPargtyw30= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= @@ -258,8 +268,10 @@ github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4 github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= github.com/cosmos/iavl v0.19.4 h1:t82sN+Y0WeqxDLJRSpNd8YFX5URIrT+p8n6oJbJ2Dok= github.com/cosmos/iavl v0.19.4/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw= -github.com/cosmos/ibc-apps/modules/async-icq/v4 v4.0.0-20230524151648-c02fa46c2860 h1:25/KpA4WJqdFjKFsa3VEL0ctWRovkEsqIn2phCAi9v0= -github.com/cosmos/ibc-apps/modules/async-icq/v4 v4.0.0-20230524151648-c02fa46c2860/go.mod h1:X/dLZ6QxTImzno7qvD6huLhh6ZZBcRt2URn4YCLcXFY= +github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v4 v4.1.0 h1:96fDMIUSlSQ6dBcwhdYwTT+uTwFP+EWbSyLORcKSJZ8= +github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v4 v4.1.0/go.mod h1:Mn/jr9pIYr1ofFuptLEi9N6MjcshTT0cpoOY4ln1DeA= +github.com/cosmos/ibc-apps/modules/async-icq/v4 v4.1.0 h1:1iQ8/rJwkeGJe81fKyZC/ASSajoJP0jEi6IJFiKIr7Y= +github.com/cosmos/ibc-apps/modules/async-icq/v4 v4.1.0/go.mod h1:X/dLZ6QxTImzno7qvD6huLhh6ZZBcRt2URn4YCLcXFY= github.com/cosmos/ibc-go/v4 v4.3.1 h1:xbg0CaCdxK3lvgGvSaI91ROOLd7s30UqEcexH6Ba4Ys= github.com/cosmos/ibc-go/v4 v4.3.1/go.mod h1:89E+K9CxpkS/etLEcG026jPM/RSnVMcfesvRYp/0aKI= github.com/cosmos/interchain-accounts v0.2.6 h1:TV2M2g1/Rb9MCNw1YePdBKE0rcEczNj1RGHT+2iRYas= @@ -278,8 +290,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/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/daixiang0/gci v0.10.1 h1:eheNA3ljF6SxnPD/vE4lCBusVHmV3Rs3dkKvFrJ7MR0= -github.com/daixiang0/gci v0.10.1/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI= +github.com/daixiang0/gci v0.11.0 h1:XeQbFKkCRxvVyn06EOuNY6LPGBLVuB/W130c8FrnX6A= +github.com/daixiang0/gci v0.11.0/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI= 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/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= @@ -385,8 +397,8 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= -github.com/go-critic/go-critic v0.7.0 h1:tqbKzB8pqi0NsRZ+1pyU4aweAF7A7QN0Pi4Q02+rYnQ= -github.com/go-critic/go-critic v0.7.0/go.mod h1:moYzd7GdVXE2C2hYTwd7h0CPcqlUeclsyBRwMa38v64= +github.com/go-critic/go-critic v0.9.0 h1:Pmys9qvU3pSML/3GEQ2Xd9RZ/ip+aXHKILuxczKGV/U= +github.com/go-critic/go-critic v0.9.0/go.mod h1:5P8tdXL7m/6qnyG6oRAlYLORvoXH0WDypYgAEmagT40= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -403,7 +415,7 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= @@ -420,6 +432,7 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8= github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU= github.com/go-toolsmith/astcopy v1.1.0 h1:YGwBN0WM+ekI/6SS6+52zLDEf8Yvp3n2seZITCUBt5s= @@ -509,15 +522,15 @@ 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.52.2 h1:FrPElUUI5rrHXg1mQ7KxI1MXPAw5lBVskiz7U7a8a1A= -github.com/golangci/golangci-lint v1.52.2/go.mod h1:S5fhC5sHM5kE22/HcATKd1XLWQxX+y7mHj8B5H91Q/0= +github.com/golangci/golangci-lint v1.54.1 h1:0qMrH1gkeIBqCZaaAm5Fwq4xys9rO/lJofHfZURIFFk= +github.com/golangci/golangci-lint v1.54.1/go.mod h1:JK47+qksV/t2mAz9YvndwT0ZLW4A1rvDljOs3g9jblo= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= 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= github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= -github.com/golangci/misspell v0.4.0 h1:KtVB/hTK4bbL/S6bs64rYyk8adjmh1BygbBiaAiX+a0= -github.com/golangci/misspell v0.4.0/go.mod h1:W6O/bwV6lGDxUCChm2ykw9NQdd5bYd1Xkjo88UcWyJc= +github.com/golangci/misspell v0.4.1 h1:+y73iSicVy2PqyX7kmUefHusENlrP9YwuHZHPLGQj/g= +github.com/golangci/misspell v0.4.1/go.mod h1:9mAN1quEo3DlpbaIKKyEvRxK1pwqR9s/Sea1bJCtlNI= github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 h1:DIPQnGy2Gv2FSA4B/hh8Q7xx3B7AIDk3DAMeHclH1vQ= github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6/go.mod h1:0AKcRCkMoKvUvlf89F6O7H2LYdhr1zBh736mBItOdRs= github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= @@ -565,6 +578,7 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -576,8 +590,8 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28 h1:9alfqbrhuD+9fLZ4iaAVwhlp5PEhmnBt7yvK2Oy5C1U= -github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= +github.com/gordonklaus/ineffassign v0.0.0-20230610083614-0e73809eb601 h1:mrEEilTAUmaAORhssPPkxj84TsHrPMLBGW2Z4SoTxm8= +github.com/gordonklaus/ineffassign v0.0.0-20230610083614-0e73809eb601/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= @@ -729,8 +743,6 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/junk1tm/musttag v0.5.0 h1:bV1DTdi38Hi4pG4OVWa7Kap0hi0o7EczuK6wQt9zPOM= -github.com/junk1tm/musttag v0.5.0/go.mod h1:PcR7BA+oREQYvHwgjIDmw3exJeds5JzRcvEJTfjrA0M= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -754,6 +766,7 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -767,8 +780,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs= github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= -github.com/kunwardeep/paralleltest v1.0.6 h1:FCKYMF1OF2+RveWlABsdnmsvJrei5aoyZoaGS+Ugg8g= -github.com/kunwardeep/paralleltest v1.0.6/go.mod h1:Y0Y0XISdZM5IKm3TREQMZ6iteqn1YuwCsJO/0kL9Zes= +github.com/kunwardeep/paralleltest v1.0.8 h1:Ul2KsqtzFxTlSU7IP0JusWlLiNqQaloB9vguyjbE558= +github.com/kunwardeep/paralleltest v1.0.8/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ymEyhQ= github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA= @@ -776,16 +789,16 @@ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4F github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/ldez/gomoddirectives v0.2.3 h1:y7MBaisZVDYmKvt9/l1mjNCiSA1BVn34U0ObUcJwlhA= github.com/ldez/gomoddirectives v0.2.3/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= -github.com/ldez/tagliatelle v0.4.0 h1:sylp7d9kh6AdXN2DpVGHBRb5guTVAgOxqNGhbqc4b1c= -github.com/ldez/tagliatelle v0.4.0/go.mod h1:mNtTfrHy2haaBAw+VT7IBV6VXBThS7TCreYWbBcJ87I= +github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSioo= +github.com/ldez/tagliatelle v0.5.0/go.mod h1:rj1HmWiL1MiKQuOONhd09iySTEkUuE/8+5jtPYz9xa4= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leonklingele/grouper v1.1.1 h1:suWXRU57D4/Enn6pXR0QVqqWWrnJ9Osrz+5rjt8ivzU= github.com/leonklingele/grouper v1.1.1/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= @@ -839,8 +852,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo= github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= -github.com/mgechev/revive v1.3.1 h1:OlQkcH40IB2cGuprTPcjB0iIUddgVZgGmDX3IAMR8D4= -github.com/mgechev/revive v1.3.1/go.mod h1:YlD6TTWl2B8A103R9KWJSPVI9DrEf+oqr15q21Ld+5I= +github.com/mgechev/revive v1.3.2 h1:Wb8NQKBaALBJ3xrrj4zpwJwqwNA6nDpyJSEQWcCka6U= +github.com/mgechev/revive v1.3.2/go.mod h1:UCLtc7o5vg5aXCwdUTU1kEBQ1v+YXPAkYDIDXbrs5I0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 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= @@ -897,12 +910,12 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6Fx github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/neilotoole/errgroup v0.1.6/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nishanths/exhaustive v0.9.5 h1:TzssWan6orBiLYVqewCG8faud9qlFntJE30ACpzmGME= -github.com/nishanths/exhaustive v0.9.5/go.mod h1:IbwrGdVMizvDcIxPYGVdQn5BqWJaOwpCvg4RGb8r/TA= +github.com/nishanths/exhaustive v0.11.0 h1:T3I8nUGhl/Cwu5Z2hfc92l0e04D2GEW6e0l8pzda2l0= +github.com/nishanths/exhaustive v0.11.0/go.mod h1:RqwDsZ1xY0dNdqHho2z6X+bgzizwbLYOWnZbbl2wLB4= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= -github.com/nunnatsa/ginkgolinter v0.9.0 h1:Sm0zX5QfjJzkeCjEp+t6d3Ha0jwvoDjleP9XCsrEzOA= -github.com/nunnatsa/ginkgolinter v0.9.0/go.mod h1:FHaMLURXP7qImeH6bvxWJUpyH+2tuqe5j4rW1gxJRmI= +github.com/nunnatsa/ginkgolinter v0.13.3 h1:wEvjrzSMfDdnoWkctignX9QTf4rT9f4GkQ3uVoXBmiU= +github.com/nunnatsa/ginkgolinter v0.13.3/go.mod h1:aTKXo8WddENYxNEFT+4ZxEgWXqlD9uMD3w9Bfw/ABEc= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= @@ -917,12 +930,12 @@ github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= -github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI= +github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -948,16 +961,14 @@ github.com/osmosis-labs/cosmos-sdk v0.45.0-rc1.0.20230703010110-ed4eb883f2a6 h1: github.com/osmosis-labs/cosmos-sdk v0.45.0-rc1.0.20230703010110-ed4eb883f2a6/go.mod h1:9KGhMg+7ZWgZ50Wa/x8w/jN19O0TSqYLlqUj+2wwxLU= github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3 h1:YlmchqTmlwdWSmrRmXKR+PcU96ntOd8u10vTaTZdcNY= github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3/go.mod h1:lV6KnqXYD/ayTe7310MHtM3I2q8Z6bBfMAi+bhwPYtI= -github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230629191111-f375469de8b6 h1:Kmkx5Rh72+LB8AL6dc6fZA+IVR0INu0YIiMF2ScDhaQ= -github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230629191111-f375469de8b6/go.mod h1:JTym95/bqrSnG5MPcXr1YDhv43JdCeo3p+iDbazoX68= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230728163612-426afac90c44 h1:UOaBVxEMMv2FS1znU7kHBdtSeZQIjnmXL4r9r19XyBo= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230728163612-426afac90c44/go.mod h1:Pl8Nzx6O6ow/+aqfMoMSz4hX+zz6RrnDYsooptECGxM= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230801224523-e85e9a9cf445 h1:V942btb00oVXHox7hEN8FrPfEaMTiVuInM7Tr00eOMU= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230801224523-e85e9a9cf445/go.mod h1:Pl8Nzx6O6ow/+aqfMoMSz4hX+zz6RrnDYsooptECGxM= +github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230804142026-a81cfe3ddde7 h1:NTR4zfrPMP4pJ5T60zyZumBAnTWmTAQX/JSZLGrM9jI= +github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230804142026-a81cfe3ddde7/go.mod h1:UlftwozB+QObT3o0YfkuuyL9fsVdgoWt0dm6J7MLYnU= +github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230807183608-16c217dedde5 h1:j4ifxomFROGROHalqWwX7VPDgxOPotMB1GiAWdb03i4= +github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230807183608-16c217dedde5/go.mod h1:Pl8Nzx6O6ow/+aqfMoMSz4hX+zz6RrnDYsooptECGxM= github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304 h1:RIrWLzIiZN5Xd2JOfSOtGZaf6V3qEQYg6EaDTAkMnCo= github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304/go.mod h1:yPWoJTj5RKrXKUChAicp+G/4Ni/uVEpp27mi/FF/L9c= -github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.0-20230602130523-f9a94d8bbd10 h1:XrES5AHZMZ/Y78boW35PTignkhN9h8VvJ1sP8EJDIu8= -github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.0-20230602130523-f9a94d8bbd10/go.mod h1:Ln6CKcXg/CJLSBE6Fd96/MIKPyA4iHuQTKSbl9q7vYo= +github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.0-20230807183608-16c217dedde5 h1:clEegVniz0zTTBXKfg2oymKa63IYUxcrVXM+LtsvCpA= +github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.0-20230807183608-16c217dedde5/go.mod h1:sR0lpev9mcm9/9RY50T1og3UC3WpZAsINh/OmgrmFlg= github.com/osmosis-labs/wasmd v0.31.0-osmo-v16 h1:X747cZYdnqc/+RV48iPVeGprpVb/fUWSaKGsZUWrdbg= github.com/osmosis-labs/wasmd v0.31.0-osmo-v16/go.mod h1:Rf8zW/GgBQyFRRB4s62VQHWA6sTlMFSjoDQQpoq64iI= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= @@ -993,8 +1004,8 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polyfloyd/go-errorlint v1.4.0 h1:b+sQ5HibPIAjEZwtuwU8Wz/u0dMZ7YL+bk+9yWyHVJk= -github.com/polyfloyd/go-errorlint v1.4.0/go.mod h1:qJCkPeBn+0EXkdKTrUCcuFStM2xrDKfxI3MGLXPexUs= +github.com/polyfloyd/go-errorlint v1.4.3 h1:P6NALOLV8BrWhm6PsqOraUK05E5h8IZnpXYJ+CIg+0U= +github.com/polyfloyd/go-errorlint v1.4.3/go.mod h1:VPlWPh6hB/wruVG803SuNpLuTGNjLHYlvcdSy4RhdPA= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= @@ -1037,8 +1048,8 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/quasilyte/go-ruleguard v0.3.19 h1:tfMnabXle/HzOb5Xe9CUZYWXKfkS1KwRmZyPmD9nVcc= -github.com/quasilyte/go-ruleguard v0.3.19/go.mod h1:lHSn69Scl48I7Gt9cX3VrbsZYvYiBYszZOZW4A+oTEw= +github.com/quasilyte/go-ruleguard v0.4.0 h1:DyM6r+TKL+xbKB4Nm7Afd1IQh9kEUKQs2pboWGKtvQo= +github.com/quasilyte/go-ruleguard v0.4.0/go.mod h1:Eu76Z/R8IXtViWUIHkE3p8gdH3/PKk1eh3YGfaEof10= github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo= github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng= github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl980XxGFEZSS6KlBGIV0diGdySzxATTWoqaU= @@ -1088,8 +1099,8 @@ github.com/sashamelentyev/usestdlibvars v1.23.0 h1:01h+/2Kd+NblNItNeux0veSL5cBF1 github.com/sashamelentyev/usestdlibvars v1.23.0/go.mod h1:YPwr/Y1LATzHI93CqoPUN/2BzGQ/6N/cl/KwgR0B/aU= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/securego/gosec/v2 v2.15.0 h1:v4Ym7FF58/jlykYmmhZ7mTm7FQvN/setNm++0fgIAtw= -github.com/securego/gosec/v2 v2.15.0/go.mod h1:VOjTrZOkUtSDt2QLSJmQBMWnvwiQPEjg0l+5juIqGk8= +github.com/securego/gosec/v2 v2.16.0 h1:Pi0JKoasQQ3NnoRao/ww/N/XdynIB9NRYYZT5CyOs5U= +github.com/securego/gosec/v2 v2.16.0/go.mod h1:xvLcVZqUfo4aAQu56TNv7/Ltz6emAOQAEsrZrt7uGlI= github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= @@ -1105,10 +1116,10 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sivchari/containedctx v1.0.2 h1:0hLQKpgC53OVF1VT7CeoFHk9YKstur1XOgfYIc1yrHI= -github.com/sivchari/containedctx v1.0.2/go.mod h1:PwZOeqm4/DLoJOqMSIJs3aKqXRX4YO+uXww087KZ7Bw= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= +github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4= github.com/sivchari/nosnakecase v1.7.0 h1:7QkpWIRMe8x25gckkFd2A5Pi6Ymo0qgr4JrhGt95do8= github.com/sivchari/nosnakecase v1.7.0/go.mod h1:CwDzrzPea40/GB6uynrNLiorAlgFRvRbFSgJx2Gs+QY= github.com/sivchari/tenv v1.7.1 h1:PSpuD4bu6fSmtWMxSGWcvqUUgIn7k3yOJhOIzVWn8Ak= @@ -1149,8 +1160,6 @@ github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRk github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/stbenjam/no-sprintf-host-port v0.1.1 h1:tYugd/yrm1O0dV+ThCbaKZh195Dfm07ysF0U6JQXczc= github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I= -github.com/strangelove-ventures/packet-forward-middleware/v4 v4.0.5 h1:KKUqeGhVBK38+1LwThC8IeIcsJZ6COX5kvhiJroFqCM= -github.com/strangelove-ventures/packet-forward-middleware/v4 v4.0.5/go.mod h1:4zAtg449/JISRmf+sbmqolqSLP+QJBh+EtWkWtt/AKE= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -1201,15 +1210,15 @@ github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= -github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= +github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM= -github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e h1:MV6KaVu/hzByHP0UvJ4HcMGE/8a6A4Rggc/0wx2AvJo= -github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ= +github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 h1:quvGphlmUVU+nhpFa4gg4yJyTRJ13reZMDHrKwYw53M= +github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ= github.com/timonwong/loggercheck v0.9.4 h1:HKKhqrjcVj8sxL7K77beXh0adEm6DLjV/QOGeMXEVi4= github.com/timonwong/loggercheck v0.9.4/go.mod h1:caz4zlPcgvpEkXgVnAJGowHAMW2NwHaNlpS8xDbVhTg= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= @@ -1232,15 +1241,15 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iLA= -github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= +github.com/ultraware/funlen v0.1.0 h1:BuqclbkY6pO+cvxoq7OsktIXZpgBSkYTQtmwhAK81vI= +github.com/ultraware/funlen v0.1.0/go.mod h1:XJqmOQja6DpxarLj6Jj1U7JuoS8PvL4nEqDaQhy22p4= github.com/ultraware/whitespace v0.0.5 h1:hh+/cpIcopyMYbZNVov9iSxvJU3OYQg78Sfaqzi/CzI= github.com/ultraware/whitespace v0.0.5/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/uudashr/gocognit v1.0.6 h1:2Cgi6MweCsdB6kpcVQp7EW4U23iBFQWfTXiWlyp842Y= -github.com/uudashr/gocognit v1.0.6/go.mod h1:nAIUuVBnYU7pcninia3BHOvQkpQCeO76Uscky5BOwcY= +github.com/uudashr/gocognit v1.0.7 h1:e9aFXgKgUJrQ5+bs61zBigmj7bFJ/5cC6HmMahVzuDo= +github.com/uudashr/gocognit v1.0.7/go.mod h1:nAIUuVBnYU7pcninia3BHOvQkpQCeO76Uscky5BOwcY= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= @@ -1256,6 +1265,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xen0n/gosmopolitan v1.2.1 h1:3pttnTuFumELBRSh+KQs1zcz4fN6Zy7aB0xlnQSn1Iw= +github.com/xen0n/gosmopolitan v1.2.1/go.mod h1:JsHq/Brs1o050OOdmzHeOr0N7OtlnKRAGAsElF8xBQA= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -1264,6 +1275,8 @@ github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsT github.com/ybbus/jsonrpc v2.1.2+incompatible/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G35eRniyeGut1z+LSiE= github.com/yeya24/promlinter v0.2.0 h1:xFKDQ82orCU5jQujdaD8stOHiv8UN68BSdn2a8u8Y3o= github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA= +github.com/ykadowak/zerologlint v0.1.3 h1:TLy1dTW3Nuc+YE3bYRPToG1Q9Ej78b5UUN6bjbGdxPE= +github.com/ykadowak/zerologlint v0.1.3/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1281,8 +1294,9 @@ github.com/zondax/hid v0.9.1 h1:gQe66rtmyZ8VeGFcOpbuH3r7erYtNEAezCAYu8LdkJo= github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v0.14.1 h1:Pip65OOl4iJ84WTpA4BKChvOufMhhbxED3BaihoZN4c= github.com/zondax/ledger-go v0.14.1/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2feOQv8K320= -gitlab.com/bosi/decorder v0.2.3 h1:gX4/RgK16ijY8V+BRQHAySfQAb354T7/xQpDB2n10P0= -gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE= +gitlab.com/bosi/decorder v0.4.0 h1:HWuxAhSxIvsITcXeP+iIRg9d1cVfvVkmlF7M68GaoDY= +gitlab.com/bosi/decorder v0.4.0/go.mod h1:xarnteyUoJiOTEldDysquWKTVDCKo2TOIOIibSuWqOg= +go-simpler.org/assert v0.5.0 h1:+5L/lajuQtzmbtEfh69sr5cRf2/xZzyJhFjoOz/PPqs= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= @@ -1297,6 +1311,8 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.tmz.dev/musttag v0.7.1 h1:9lFmeSFnFfPuMq4IksHGomItE6NgKMNW2Nt2FPOhCfU= +go.tmz.dev/musttag v0.7.1/go.mod h1:oJLkpR56EsIryktZJk/B0IroSMi37YWver47fibGh5U= 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= @@ -1314,9 +1330,8 @@ 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.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 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= @@ -1341,8 +1356,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1357,12 +1372,12 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 h1:BEABXpNXLEz0WxtA+6CQIz2xkg80e+1zrhWyMcq8VzE= -golang.org/x/exp v0.0.0-20230131160201-f062dba9d201/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4= +golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2 h1:J74nGeMgeFnYQJN59eFwh06jX/V8g0lB7LWpjSLxtgU= -golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833 h1:jWGQJV4niP+CCmFW9ekjA9Zx8vYORzOUH2/Nl5WPuLQ= +golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1393,8 +1408,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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= @@ -1450,11 +1465,10 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= 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= @@ -1478,8 +1492,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1572,22 +1586,20 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= +golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= 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= @@ -1598,11 +1610,10 @@ 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/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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= @@ -1687,11 +1698,10 @@ golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= -golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= +golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= +golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= 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= @@ -1856,6 +1866,7 @@ mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d/go.mod h1:IeHQjmn6TOD+e4Z3RF nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= diff --git a/networks/osmosis-1/README.md b/networks/osmosis-1/README.md index f10caf577e8..d6a7b7204a1 100644 --- a/networks/osmosis-1/README.md +++ b/networks/osmosis-1/README.md @@ -17,6 +17,7 @@ Each version is identified by a specific id, name, tag, block height and softwar | `v14` | Neon | `v14.0.1` | 7937500 | [Release](https://github.com/osmosis-labs/osmosis/releases/tag/v14.0.1/) | [401](https://www.mintscan.io/osmosis/proposals/401) | | `v15` | Sodium | `v15.2.0` | 8732500 | [Release](https://github.com/osmosis-labs/osmosis/releases/tag/v15.2.0/) | [458](https://www.mintscan.io/osmosis/proposals/458) | | `v16` | Magnesium | `v16.1.1` | 10517000 | [Release](https://github.com/osmosis-labs/osmosis/releases/tag/v16.1.1/) | [556](https://www.mintscan.io/osmosis/proposals/556) | +| `v17` | Aluminium | `v17.0.0` | 11126100 | [Release](https://github.com/osmosis-labs/osmosis/releases/tag/v17.0.0/) | [586](https://www.mintscan.io/osmosis/proposals/586) | ## Upgrade Binaries @@ -145,6 +146,19 @@ Each version is identified by a specific id, name, tag, block height and softwar } ``` +### v17.0.0 + +```json +{ + "binaries": { + "darwin/arm64": "https://github.com/osmosis-labs/osmosis/releases/download/v17.0.0/osmosisd-17.0.0-darwin-arm64?checksum=sha256:5ca1b120a62ba473e7772682d89db949ae67aa10dc9bf4629b0022a95e7ff1df", + "darwin/amd64": "https://github.com/osmosis-labs/osmosis/releases/download/v17.0.0/osmosisd-17.0.0-darwin-amd64?checksum=sha256:b5e4deb0d659eeeaee791dab765433bdb8d6a7e37d909628e0f9becb7d1f154b", + "linux/arm64": "https://github.com/osmosis-labs/osmosis/releases/download/v17.0.0/osmosisd-17.0.0-linux-arm64?checksum=sha256:d5eeab6a15e2acd7e24e7caf4fe3336c35367ff376da6299d404defd09ce52f9", + "linux/amd64": "https://github.com/osmosis-labs/osmosis/releases/download/v17.0.0/osmosisd-17.0.0-linux-amd64?checksum=sha256:d7fe62ae33cf2f0b48a17eb8b02644dadd9924f15861ed622cd90cb1a038135b" + } +} +``` + ## Replay from Genesis using Cosmovisor Assuming that your osmosis home it's already initialized with the desired genesis and configuration, @@ -187,6 +201,9 @@ Alternatively, you can download the appropriate binary for your platform from ou ├── v16 │ └── bin │ └── osmosisd + ├── v17 + │ └── bin + │ └── osmosisd ├── v4 │ └── bin │ └── osmosisd @@ -220,6 +237,7 @@ versions_info=( "v14:https://github.com/osmosis-labs/osmosis/releases/download/v14.0.1/osmosisd-14.0.1-linux-amd64?checksum=sha256:2cc4172bcf000f0f06b30b16864d875a8de2ee12df994a593dfd52a506851bce" "v15:https://github.com/osmosis-labs/osmosis/releases/download/v15.2.0/osmosisd-15.2.0-linux-amd64?checksum=sha256:3aab2f2668cb5a713d5770e46a777ef01c433753378702d9ae941aa2d1ee5618" "v16:https://github.com/osmosis-labs/osmosis/releases/download/v16.1.1/osmosisd-16.1.1-linux-amd64?checksum=sha256:f838618633c1d42f593dc33d26b25842f5900961e987fc08570bb81a062e311d" + "v17:https://github.com/osmosis-labs/osmosis/releases/download/v17.0.0/osmosisd-17.0.0-linux-amd64?checksum=sha256:d7fe62ae33cf2f0b48a17eb8b02644dadd9924f15861ed622cd90cb1a038135b" ) # Create the cosmovisor directory diff --git a/networks/osmosis-1/upgrades/v17/mainnet/guide.md b/networks/osmosis-1/upgrades/v17/mainnet/guide.md new file mode 100644 index 00000000000..36389e2be3c --- /dev/null +++ b/networks/osmosis-1/upgrades/v17/mainnet/guide.md @@ -0,0 +1,127 @@ +# v16 to v17 Mainnet Upgrade Guide + +Osmosis v17 Gov Prop: + +Countdown: + +Height: TODO + +## Memory Requirements + +This upgrade will **not** be resource intensive. With that being said, we still recommend having 64GB of memory. If having 64GB of physical memory is not possible, the next best thing is to set up swap. + +Short version swap setup instructions: + +``` {.sh} +sudo swapoff -a +sudo fallocate -l 32G /swapfile +sudo chmod 600 /swapfile +sudo mkswap /swapfile +sudo swapon /swapfile +``` + +To persist swap after restart: + +``` {.sh} +sudo cp /etc/fstab /etc/fstab.bak +echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab +``` + +In depth swap setup instructions: + + +## First Time Cosmovisor Setup + +If you have never setup Cosmovisor before, follow the following instructions. + +If you have already setup Cosmovisor, skip to the next section. + +We highly recommend validators use cosmovisor to run their nodes. This +will make low-downtime upgrades smoother, as validators don't have to +manually upgrade binaries during the upgrade, and instead can +pre-install new binaries, and cosmovisor will automatically update them +based on on-chain SoftwareUpgrade proposals. + +You should review the docs for cosmovisor located here: + + +If you choose to use cosmovisor, please continue with these +instructions: + +To install Cosmovisor: + +``` {.sh} +go install github.com/cosmos/cosmos-sdk/cosmovisor/cmd/cosmovisor@v1.0.0 +``` + +After this, you must make the necessary folders for cosmosvisor in your +daemon home directory (\~/.osmosisd). + +``` {.sh} +mkdir -p ~/.osmosisd +mkdir -p ~/.osmosisd/cosmovisor +mkdir -p ~/.osmosisd/cosmovisor/genesis +mkdir -p ~/.osmosisd/cosmovisor/genesis/bin +mkdir -p ~/.osmosisd/cosmovisor/upgrades +``` + +Copy the current v16 osmosisd binary into the +cosmovisor/genesis folder and v16 folder. + +```{.sh} +cp $GOPATH/bin/osmosisd ~/.osmosisd/cosmovisor/genesis/bin +mkdir -p ~/.osmosisd/cosmovisor/upgrades/v16/bin +cp $GOPATH/bin/osmosisd ~/.osmosisd/cosmovisor/upgrades/v16/bin +``` + +Cosmovisor is now ready to be set up for v17. + +Set these environment variables: + +```{.sh} +echo "# Setup Cosmovisor" >> ~/.profile +echo "export DAEMON_NAME=osmosisd" >> ~/.profile +echo "export DAEMON_HOME=$HOME/.osmosisd" >> ~/.profile +echo "export DAEMON_ALLOW_DOWNLOAD_BINARIES=false" >> ~/.profile +echo "export DAEMON_LOG_BUFFER_SIZE=512" >> ~/.profile +echo "export DAEMON_RESTART_AFTER_UPGRADE=true" >> ~/.profile +echo "export UNSAFE_SKIP_BACKUP=true" >> ~/.profile +source ~/.profile +``` + +## Cosmovisor Upgrade + +Create the v17 folder, make the build, and copy the daemon over to that folder + +```{.sh} +mkdir -p ~/.osmosisd/cosmovisor/upgrades/v17/bin +cd $HOME/osmosis +git pull +git checkout v17.0.0 +make build +cp build/osmosisd ~/.osmosisd/cosmovisor/upgrades/v17/bin +``` + +Now, at the upgrade height, Cosmovisor will upgrade to the v17 binary + +## Manual Option + +1. Wait for Osmosis to reach the upgrade height TODO + +2. Look for a panic message, followed by endless peer logs. Stop the daemon + +3. Run the following commands: + +```{.sh} +cd $HOME/osmosis +git pull +git checkout v17.0.0 +make install +``` + +4. Start the osmosis daemon again, watch the upgrade happen, and then continue to hit blocks + +## Further Help + +If you need more help, please go to or join +our discord at . \ No newline at end of file diff --git a/networks/osmosis-1/upgrades/v17/testnet/guide.md b/networks/osmosis-1/upgrades/v17/testnet/guide.md new file mode 100644 index 00000000000..bc6c7eacdce --- /dev/null +++ b/networks/osmosis-1/upgrades/v17/testnet/guide.md @@ -0,0 +1,129 @@ +# v16 to v17 Testnet Upgrade Guide + +| | | +|-----------------|--------------------------------------------------------------| +| Chain-id | `osmo-test-5` | +| Upgrade Version | `v17.0.0-rc0` | +| Upgrade Height | 2200500 | +| Countdown | | + +## Memory Requirements + +This upgrade will **not** be resource intensive. With that being said, we still recommend having 64GB of memory. If having 64GB of physical memory is not possible, the next best thing is to set up swap. + +Short version swap setup instructions: + +``` {.sh} +sudo swapoff -a +sudo fallocate -l 32G /swapfile +sudo chmod 600 /swapfile +sudo mkswap /swapfile +sudo swapon /swapfile +``` + +To persist swap after restart: + +``` {.sh} +sudo cp /etc/fstab /etc/fstab.bak +echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab +``` + +In depth swap setup instructions: + + +## First Time Cosmovisor Setup + +If you have never setup Cosmovisor before, follow the following instructions. + +If you have already setup Cosmovisor, skip to the next section. + +We highly recommend validators use cosmovisor to run their nodes. This +will make low-downtime upgrades smoother, as validators don't have to +manually upgrade binaries during the upgrade, and instead can +pre-install new binaries, and cosmovisor will automatically update them +based on on-chain SoftwareUpgrade proposals. + +You should review the docs for cosmovisor located here: + + +If you choose to use cosmovisor, please continue with these +instructions: + +To install Cosmovisor: + +``` {.sh} +go install github.com/cosmos/cosmos-sdk/cosmovisor/cmd/cosmovisor@v1.0.0 +``` + +After this, you must make the necessary folders for cosmosvisor in your +daemon home directory (\~/.osmosisd). + +``` {.sh} +mkdir -p ~/.osmosisd +mkdir -p ~/.osmosisd/cosmovisor +mkdir -p ~/.osmosisd/cosmovisor/genesis +mkdir -p ~/.osmosisd/cosmovisor/genesis/bin +mkdir -p ~/.osmosisd/cosmovisor/upgrades +``` + +Copy the current v16 osmosisd binary into the +cosmovisor/genesis folder and v16 folder. + +```{.sh} +cp $GOPATH/bin/osmosisd ~/.osmosisd/cosmovisor/genesis/bin +mkdir -p ~/.osmosisd/cosmovisor/upgrades/v16/bin +cp $GOPATH/bin/osmosisd ~/.osmosisd/cosmovisor/upgrades/v16/bin +``` + +Cosmovisor is now ready to be set up for v17. + +Set these environment variables: + +```{.sh} +echo "# Setup Cosmovisor" >> ~/.profile +echo "export DAEMON_NAME=osmosisd" >> ~/.profile +echo "export DAEMON_HOME=$HOME/.osmosisd" >> ~/.profile +echo "export DAEMON_ALLOW_DOWNLOAD_BINARIES=false" >> ~/.profile +echo "export DAEMON_LOG_BUFFER_SIZE=512" >> ~/.profile +echo "export DAEMON_RESTART_AFTER_UPGRADE=true" >> ~/.profile +echo "export UNSAFE_SKIP_BACKUP=true" >> ~/.profile +source ~/.profile +``` + +## Cosmovisor Upgrade + +Create the v17 folder, make the build, and copy the daemon over to that folder + +```{.sh} +mkdir -p ~/.osmosisd/cosmovisor/upgrades/v17/bin +cd $HOME/osmosis +git pull +git checkout v17.0.0-rc0 +make build +cp build/osmosisd ~/.osmosisd/cosmovisor/upgrades/v17/bin +``` + +Now, at the upgrade height, Cosmovisor will upgrade to the v17 binary + +## Manual Option + +1. Wait for Osmosis to reach the upgrade height (2200500) + +2. Look for a panic message, followed by endless peer logs. Stop the daemon + +3. Run the following commands: + +```{.sh} +cd $HOME/osmosis +git pull +git checkout v17.0.0-rc0 +make install +``` + +4. Start the osmosis daemon again, watch the upgrade happen, and then continue to hit blocks + +## Further Help + +If you need more help, please: +- go to +- join our discord at . diff --git a/networks/osmosis-1/upgrades/v17/testnet/testnet_v17_rc0_binaries.json b/networks/osmosis-1/upgrades/v17/testnet/testnet_v17_rc0_binaries.json new file mode 100644 index 00000000000..f2fad71d61f --- /dev/null +++ b/networks/osmosis-1/upgrades/v17/testnet/testnet_v17_rc0_binaries.json @@ -0,0 +1,6 @@ +{ + "binaries": { + "linux/amd64": "https://github.com/osmosis-labs/osmosis/releases/download/v17.0.0-rc0/osmosisd-17.0.0-rc0-linux-amd64?checksum=sha256:47fca432d22d39390066e3a08ba055e4dbe8d15f98fdf282f1f83caddf9344a7", + "linux/arm64": "https://github.com/osmosis-labs/osmosis/releases/download/v17.0.0-rc0/osmosisd-17.0.0-rc0-linux-arm64?checksum=sha256:5cdd86e9148788a43e913a40ea1c896776a7d18420961db76a87f494e554a066" + } +} diff --git a/networks/osmosis-1/upgrades/v17/v17_binaries.json b/networks/osmosis-1/upgrades/v17/v17_binaries.json new file mode 100644 index 00000000000..4309c1c6ea6 --- /dev/null +++ b/networks/osmosis-1/upgrades/v17/v17_binaries.json @@ -0,0 +1,8 @@ +{ + "binaries": { + "darwin/arm64": "https://github.com/osmosis-labs/osmosis/releases/download/v17.0.0/osmosisd-17.0.0-darwin-arm64?checksum=sha256:5ca1b120a62ba473e7772682d89db949ae67aa10dc9bf4629b0022a95e7ff1df", + "darwin/amd64": "https://github.com/osmosis-labs/osmosis/releases/download/v17.0.0/osmosisd-17.0.0-darwin-amd64?checksum=sha256:b5e4deb0d659eeeaee791dab765433bdb8d6a7e37d909628e0f9becb7d1f154b", + "linux/arm64": "https://github.com/osmosis-labs/osmosis/releases/download/v17.0.0/osmosisd-17.0.0-linux-arm64?checksum=sha256:d5eeab6a15e2acd7e24e7caf4fe3336c35367ff376da6299d404defd09ce52f9", + "linux/amd64": "https://github.com/osmosis-labs/osmosis/releases/download/v17.0.0/osmosisd-17.0.0-linux-amd64?checksum=sha256:d7fe62ae33cf2f0b48a17eb8b02644dadd9924f15861ed622cd90cb1a038135b" + } +} \ No newline at end of file diff --git a/osmoutils/ibc.go b/osmoutils/ibc.go index cc2f30b5d64..310059d7c56 100644 --- a/osmoutils/ibc.go +++ b/osmoutils/ibc.go @@ -14,6 +14,22 @@ const IbcAcknowledgementErrorType = "ibc-acknowledgement-error" // NewEmitErrorAcknowledgement creates a new error acknowledgement after having emitted an event with the // details of the error. func NewEmitErrorAcknowledgement(ctx sdk.Context, err error, errorContexts ...string) channeltypes.Acknowledgement { + EmitIBCErrorEvents(ctx, err, errorContexts) + + return channeltypes.NewErrorAcknowledgement(err) +} + +// NewSuccessAckRepresentingAnError creates a new success acknowledgement that represents an error. +// This is useful for notifying the sender that an error has occurred in a way that does not allow +// the received tokens to be reverted (which means they shouldn't be released by the sender's ics20 escrow) +func NewSuccessAckRepresentingAnError(ctx sdk.Context, err error, errorContent []byte, errorContexts ...string) channeltypes.Acknowledgement { + EmitIBCErrorEvents(ctx, err, errorContexts) + + return channeltypes.NewResultAcknowledgement(errorContent) +} + +// EmitIBCErrorEvents Emit and Log errors +func EmitIBCErrorEvents(ctx sdk.Context, err error, errorContexts []string) { logger := ctx.Logger().With("module", IbcAcknowledgementErrorType) attributes := make([]sdk.Attribute, len(errorContexts)+1) @@ -29,8 +45,6 @@ func NewEmitErrorAcknowledgement(ctx sdk.Context, err error, errorContexts ...st attributes..., ), }) - - return channeltypes.NewErrorAcknowledgement(err) } // MustExtractDenomFromPacketOnRecv takes a packet with a valid ICS20 token data in the Data field and returns the diff --git a/proto/osmosis/ibc-hooks/genesis.proto b/proto/osmosis/ibc-hooks/genesis.proto new file mode 100644 index 00000000000..435af285cac --- /dev/null +++ b/proto/osmosis/ibc-hooks/genesis.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; +package osmosis.ibchooks; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "osmosis/ibc-hooks/params.proto"; + +option go_package = "github.com/osmosis-labs/osmosis/v17/x/ibc-hooks/types"; + +message GenesisState { Params params = 1 [ (gogoproto.nullable) = false ]; } diff --git a/proto/osmosis/ibc-hooks/params.proto b/proto/osmosis/ibc-hooks/params.proto new file mode 100644 index 00000000000..6ca3974c304 --- /dev/null +++ b/proto/osmosis/ibc-hooks/params.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; +package osmosis.ibchooks; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "google/protobuf/duration.proto"; + +option go_package = "github.com/osmosis-labs/osmosis/v17/x/ibc-hooks/types"; + +message Params { + repeated string allowed_async_ack_contracts = 1 + [ (gogoproto.moretags) = "yaml:\"allowed_async_ack_contracts\"" ]; +} diff --git a/proto/osmosis/ibc-hooks/tx.proto b/proto/osmosis/ibc-hooks/tx.proto new file mode 100644 index 00000000000..0ec7c398587 --- /dev/null +++ b/proto/osmosis/ibc-hooks/tx.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; +package osmosis.ibchooks; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/osmosis-labs/osmosis/v17/x/ibc-hooks/types"; + +// Msg defines the Msg service. +service Msg { + // EmitIBCAck checks the sender can emit the ack and writes the IBC + // acknowledgement + rpc EmitIBCAck(MsgEmitIBCAck) returns (MsgEmitIBCAckResponse); +} + +message MsgEmitIBCAck { + string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ]; + uint64 packet_sequence = 2 + [ (gogoproto.moretags) = "yaml:\"packet_sequence\"" ]; + string channel = 3 [ (gogoproto.moretags) = "yaml:\"channel\"" ]; +} +message MsgEmitIBCAckResponse { + string contract_result = 1 + [ (gogoproto.moretags) = "yaml:\"contract_result\"" ]; + string ibc_ack = 2 [ (gogoproto.moretags) = "yaml:\"ibc_ack\"" ]; +} diff --git a/proto/osmosis/incentives/gauge.proto b/proto/osmosis/incentives/gauge.proto index 7571090e1dc..4f18a88b90a 100644 --- a/proto/osmosis/incentives/gauge.proto +++ b/proto/osmosis/incentives/gauge.proto @@ -50,6 +50,23 @@ message Gauge { ]; } +// SplittingPolicy determines the way we want to split incentives in groupGauges +enum SplittingPolicy { + option (gogoproto.goproto_enum_prefix) = false; + + Volume = 0; + Evenly = 1; +} + +// Gauge is an object that stores GroupGaugeId as well as internalGaugeIds. We +// linked these two together so that we can distribute tokens from groupGauge to +// internalGauges. +message GroupGauge { + uint64 group_gauge_id = 1; + repeated uint64 internal_ids = 2; + SplittingPolicy splitting_policy = 3; +} + message LockableDurationsInfo { // List of incentivised durations that gauges will pay out to repeated google.protobuf.Duration lockable_durations = 1 [ diff --git a/proto/osmosis/lockup/lock.proto b/proto/osmosis/lockup/lock.proto index 5d39efb93c3..9c4624ab9a1 100644 --- a/proto/osmosis/lockup/lock.proto +++ b/proto/osmosis/lockup/lock.proto @@ -57,6 +57,7 @@ enum LockQueryType { ByDuration = 0; ByTime = 1; NoLock = 2; + ByGroup = 3; } // QueryCondition is a struct used for querying locks upon different conditions. diff --git a/proto/osmosis/poolmanager/v1beta1/tracked_volume.proto b/proto/osmosis/poolmanager/v1beta1/tracked_volume.proto new file mode 100644 index 00000000000..3ba5597c387 --- /dev/null +++ b/proto/osmosis/poolmanager/v1beta1/tracked_volume.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; +package osmosis.poolmanager.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/osmosis-labs/osmosis/v17/x/poolmanager/types"; + +message TrackedVolume { + repeated cosmos.base.v1beta1.Coin amount = 1 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} diff --git a/proto/osmosis/protorev/v1beta1/genesis.proto b/proto/osmosis/protorev/v1beta1/genesis.proto index 38dfc6442b1..689f984526c 100644 --- a/proto/osmosis/protorev/v1beta1/genesis.proto +++ b/proto/osmosis/protorev/v1beta1/genesis.proto @@ -27,6 +27,9 @@ message GenesisState { ]; // The pool weights that are being used to calculate the weight (compute cost) // of each route. + // + // DEPRECATED: This field is deprecated and will be removed in the next + // release. It is replaced by the `info_by_pool_type` field. PoolWeights pool_weights = 4 [ (gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"pool_weights\"" @@ -63,4 +66,10 @@ message GenesisState { (gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"profits\"" ]; + // Information that is used to estimate execution time / gas + // consumption of a swap on a given pool type. + InfoByPoolType info_by_pool_type = 13 [ + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"info_by_pool_type\"" + ]; } \ No newline at end of file diff --git a/proto/osmosis/protorev/v1beta1/protorev.proto b/proto/osmosis/protorev/v1beta1/protorev.proto index c3dbda4db31..34e624e34df 100644 --- a/proto/osmosis/protorev/v1beta1/protorev.proto +++ b/proto/osmosis/protorev/v1beta1/protorev.proto @@ -78,6 +78,9 @@ message RouteStatistics { // significantly between the different pool types. Each weight roughly // corresponds to the amount of time (in ms) it takes to execute a swap on that // pool type. +// +// DEPRECATED: This field is deprecated and will be removed in the next +// release. It is replaced by the `info_by_pool_type` field. message PoolWeights { // The weight of a stableswap pool uint64 stable_weight = 1 [ (gogoproto.moretags) = "yaml:\"stable_weight\"" ]; @@ -92,6 +95,74 @@ message PoolWeights { [ (gogoproto.moretags) = "yaml:\"cosmwasm_weight\"" ]; } +// InfoByPoolType contains information pertaining to how expensive (in terms of +// gas and time) it is to execute a swap on a given pool type. This distinction +// is made and necessary because the execution time ranges significantly between +// the different pool types. +message InfoByPoolType { + // The stable pool info + StablePoolInfo stable = 1 [ + (gogoproto.moretags) = "yaml:\"stable\"", + (gogoproto.nullable) = false + ]; + // The balancer pool info + BalancerPoolInfo balancer = 2 [ + (gogoproto.moretags) = "yaml:\"balancer\"", + (gogoproto.nullable) = false + ]; + // The concentrated pool info + ConcentratedPoolInfo concentrated = 3 [ + (gogoproto.moretags) = "yaml:\"concentrated\"", + (gogoproto.nullable) = false + ]; + // The cosmwasm pool info + CosmwasmPoolInfo cosmwasm = 4 [ + (gogoproto.moretags) = "yaml:\"cosmwasm\"", + (gogoproto.nullable) = false + ]; +} + +// StablePoolInfo contains meta data pertaining to a stableswap pool type. +message StablePoolInfo { + // The weight of a stableswap pool + uint64 weight = 1 [ (gogoproto.moretags) = "yaml:\"weight\"" ]; +} + +// BalancerPoolInfo contains meta data pertaining to a balancer pool type. +message BalancerPoolInfo { + // The weight of a balancer pool + uint64 weight = 1 [ (gogoproto.moretags) = "yaml:\"weight\"" ]; +} + +// ConcentratedPoolInfo contains meta data pertaining to a concentrated pool +// type. +message ConcentratedPoolInfo { + // The weight of a concentrated pool + uint64 weight = 1 [ (gogoproto.moretags) = "yaml:\"weight\"" ]; + // The maximum number of ticks we can move when rebalancing + uint64 max_ticks_crossed = 2 + [ (gogoproto.moretags) = "yaml:\"max_ticks_crossed\"" ]; +} + +// CosmwasmPoolInfo contains meta data pertaining to a cosmwasm pool type. +message CosmwasmPoolInfo { + // The weight of a cosmwasm pool (by contract address) + repeated WeightMap weight_maps = 1 [ + (gogoproto.moretags) = "yaml:\"weight_maps\"", + (gogoproto.nullable) = false + ]; +} + +// WeightMap maps a contract address to a weight. The weight of an address +// corresponds to the amount of ms required to execute a swap on that contract. +message WeightMap { + // The weight of a cosmwasm pool (by contract address) + uint64 weight = 1 [ (gogoproto.moretags) = "yaml:\"weight\"" ]; + // The contract address + string contract_address = 2 + [ (gogoproto.moretags) = "yaml:\"contract_address\"" ]; +} + // BaseDenom represents a single base denom that the module uses for its // arbitrage trades. It contains the denom name alongside the step size of the // binary search that is used to find the optimal swap amount diff --git a/proto/osmosis/protorev/v1beta1/query.proto b/proto/osmosis/protorev/v1beta1/query.proto index 0c0b3f13bcc..a57757a9aff 100644 --- a/proto/osmosis/protorev/v1beta1/query.proto +++ b/proto/osmosis/protorev/v1beta1/query.proto @@ -71,11 +71,11 @@ service Query { option (google.api.http).get = "/osmosis/protorev/developer_account"; } - // GetProtoRevPoolWeights queries the weights of each pool type currently - // being used by the module - rpc GetProtoRevPoolWeights(QueryGetProtoRevPoolWeightsRequest) - returns (QueryGetProtoRevPoolWeightsResponse) { - option (google.api.http).get = "/osmosis/protorev/pool_weights"; + // GetProtoRevInfoByPoolType queries pool type information that is currently + // being utilized by the module + rpc GetProtoRevInfoByPoolType(QueryGetProtoRevInfoByPoolTypeRequest) + returns (QueryGetProtoRevInfoByPoolTypeResponse) { + option (google.api.http).get = "/osmosis/protorev/info_by_pool_type"; } // GetProtoRevMaxPoolPointsPerTx queries the maximum number of pool points @@ -242,16 +242,17 @@ message QueryGetProtoRevDeveloperAccountResponse { [ (gogoproto.moretags) = "yaml:\"developer_account\"" ]; } -// QueryGetProtoRevPoolWeightsRequest is request type for the -// Query/GetProtoRevPoolWeights RPC method. -message QueryGetProtoRevPoolWeightsRequest {} - -// QueryGetProtoRevPoolWeightsResponse is response type for the -// Query/GetProtoRevPoolWeights RPC method. -message QueryGetProtoRevPoolWeightsResponse { - // pool_weights is a list of all of the pool weights - PoolWeights pool_weights = 1 [ - (gogoproto.moretags) = "yaml:\"pool_weights\"", +// QueryGetProtoRevInfoByPoolTypeRequest is request type for the +// Query/GetProtoRevInfoByPoolType RPC method. +message QueryGetProtoRevInfoByPoolTypeRequest {} + +// QueryGetProtoRevInfoByPoolTypeResponse is response type for the +// Query/GetProtoRevInfoByPoolType RPC method. +message QueryGetProtoRevInfoByPoolTypeResponse { + // InfoByPoolType contains all information pertaining to how different + // pool types are handled by the module. + InfoByPoolType info_by_pool_type = 1 [ + (gogoproto.moretags) = "yaml:\"info_by_pool_type\"", (gogoproto.nullable) = false ]; } diff --git a/proto/osmosis/protorev/v1beta1/tx.proto b/proto/osmosis/protorev/v1beta1/tx.proto index 4fb23be05b3..9c59aaa3d38 100644 --- a/proto/osmosis/protorev/v1beta1/tx.proto +++ b/proto/osmosis/protorev/v1beta1/tx.proto @@ -39,10 +39,11 @@ service Msg { "/osmosis/protorev/set_max_pool_points_per_block"; }; - // SetPoolWeights sets the weights of each pool type in the store. Can only be - // called by the admin account. - rpc SetPoolWeights(MsgSetPoolWeights) returns (MsgSetPoolWeightsResponse) { - option (google.api.http).post = "/osmosis/protorev/set_pool_weights"; + // SetInfoByPoolType sets the pool type information needed to make smart + // assumptions about swapping on different pool types + rpc SetInfoByPoolType(MsgSetInfoByPoolType) + returns (MsgSetInfoByPoolTypeResponse) { + option (google.api.http).post = "/osmosis/protorev/set_info_by_pool_type"; }; // SetBaseDenoms sets the base denoms that will be used to create cyclic @@ -90,24 +91,24 @@ message MsgSetDeveloperAccount { // type. message MsgSetDeveloperAccountResponse {} -// MsgSetPoolWeights defines the Msg/SetPoolWeights request type. -message MsgSetPoolWeights { - option (amino.name) = "osmosis/MsgSetPoolWeights"; +// MsgSetInfoByPoolType defines the Msg/SetInfoByPoolType request type. +message MsgSetInfoByPoolType { + option (amino.name) = "osmosis/MsgSetInfoByPoolType"; // admin is the account that is authorized to set the pool weights. string admin = 1 [ (gogoproto.moretags) = "yaml:\"admin\"", (cosmos_proto.scalar) = "cosmos.AddressString" ]; - // pool_weights is the list of pool weights to set. - PoolWeights pool_weights = 2 [ - (gogoproto.moretags) = "yaml:\"pool_weights\"", + // info_by_pool_type contains information about the pool types. + InfoByPoolType info_by_pool_type = 2 [ + (gogoproto.moretags) = "yaml:\"info_by_pool_type\"", (gogoproto.nullable) = false ]; } -// MsgSetPoolWeightsResponse defines the Msg/SetPoolWeights response type. -message MsgSetPoolWeightsResponse {} +// MsgSetInfoByPoolTypeResponse defines the Msg/SetInfoByPoolType response type. +message MsgSetInfoByPoolTypeResponse {} // MsgSetMaxPoolPointsPerTx defines the Msg/SetMaxPoolPointsPerTx request type. message MsgSetMaxPoolPointsPerTx { diff --git a/proto/osmosis/superfluid/query.proto b/proto/osmosis/superfluid/query.proto index 7a00a20a45c..cc66250a73a 100644 --- a/proto/osmosis/superfluid/query.proto +++ b/proto/osmosis/superfluid/query.proto @@ -125,6 +125,7 @@ service Query { "unpool_whitelist"; } + // Returns all of a user's full range CL positions that are superfluid staked. rpc UserConcentratedSuperfluidPositionsDelegated( UserConcentratedSuperfluidPositionsDelegatedRequest) returns (UserConcentratedSuperfluidPositionsDelegatedResponse) { diff --git a/proto/osmosis/superfluid/tx.proto b/proto/osmosis/superfluid/tx.proto index 4c514aa0646..9ca6df217f5 100644 --- a/proto/osmosis/superfluid/tx.proto +++ b/proto/osmosis/superfluid/tx.proto @@ -52,6 +52,11 @@ service Msg { rpc AddToConcentratedLiquiditySuperfluidPosition( MsgAddToConcentratedLiquiditySuperfluidPosition) returns (MsgAddToConcentratedLiquiditySuperfluidPositionResponse); + + // UnbondConvertAndStake breaks all locks / superfluid staked assets, + // converts them to osmo then stakes the osmo to the designated validator. + rpc UnbondConvertAndStake(MsgUnbondConvertAndStake) + returns (MsgUnbondConvertAndStakeResponse); } message MsgSuperfluidDelegate { @@ -231,4 +236,40 @@ message MsgAddToConcentratedLiquiditySuperfluidPositionResponse { (gogoproto.nullable) = false ]; uint64 lock_id = 4 [ (gogoproto.moretags) = "yaml:\"lock_id\"" ]; +} + +// ===================== MsgUnbondConvertAndStake +message MsgUnbondConvertAndStake { + option (amino.name) = "osmosis/unbond-convert-and-stake"; + + // lock ID to convert and stake. + // lock id with 0 should be provided if converting liquid gamm shares to stake + uint64 lock_id = 1 [ (gogoproto.moretags) = "yaml:\"lock_id\"" ]; + string sender = 2 [ (gogoproto.moretags) = "yaml:\"sender\"" ]; + // validator address to delegate to. + // If provided empty string, we use the validators returned from + // valset-preference module. + string val_addr = 3; + // min_amt_to_stake indicates the minimum amount to stake after conversion + string min_amt_to_stake = 4 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.moretags) = "yaml:\"min_amt_to_stake\"", + (gogoproto.nullable) = false + ]; + // shares_to_convert indicates shares wanted to stake. + // Note that this field is only used for liquid(unlocked) gamm shares. + // For all other cases, this field would be disregarded. + cosmos.base.v1beta1.Coin shares_to_convert = 5 [ + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"shares_to_convert\"", + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coin" + ]; +} + +message MsgUnbondConvertAndStakeResponse { + string total_amt_staked = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.moretags) = "yaml:\"total_amt_staked\"", + (gogoproto.nullable) = false + ]; } \ No newline at end of file diff --git a/scripts/release/create_all_binaries_json.sh b/scripts/release/create_all_binaries_json.sh index 84cadc40bb0..a42d4ae800c 100755 --- a/scripts/release/create_all_binaries_json.sh +++ b/scripts/release/create_all_binaries_json.sh @@ -12,6 +12,7 @@ tags=( "v14.0.1" "v15.2.0" "v16.1.1" + "v17.0.0" ) echo "# Cosmovisor binaries" diff --git a/simulation/executor/legacyconfig.go b/simulation/executor/legacyconfig.go index 72534f9ff84..c13e1d68310 100644 --- a/simulation/executor/legacyconfig.go +++ b/simulation/executor/legacyconfig.go @@ -115,6 +115,7 @@ func NewExecutionDbConfigFromFlags() ExecutionDbConfig { // SetupSimulation creates the config, db (levelDB), temporary directory and logger for // the simulation tests. If `FlagEnabledValue` is false it skips the current test. // Returns error on an invalid db intantiation or temp dir creation. +// nolint: revive func SetupSimulation(dirPrefix, dbName string) (cfg Config, db dbm.DB, logger log.Logger, cleanup func(), err error) { if !FlagEnabledValue { return Config{}, nil, nil, func() {}, nil diff --git a/simulation/executor/mock_tendermint.go b/simulation/executor/mock_tendermint.go index 811859e8147..e324fb15885 100644 --- a/simulation/executor/mock_tendermint.go +++ b/simulation/executor/mock_tendermint.go @@ -181,7 +181,7 @@ func RandomRequestBeginBlock(r *rand.Rand, params Params, } voteInfos := randomVoteInfos(r, params, validators) - evidence := randomDoubleSignEvidence(r, params, validators, pastTimes, pastVoteInfos, event, header, voteInfos) + evidence := randomDoubleSignEvidence(r, params, pastTimes, pastVoteInfos, event, header, voteInfos) return abci.RequestBeginBlock{ Header: header, @@ -230,8 +230,7 @@ func randomVoteInfos(r *rand.Rand, simParams Params, validators mockValidators, return voteInfos } -func randomDoubleSignEvidence(r *rand.Rand, params Params, - validators mockValidators, pastTimes []time.Time, +func randomDoubleSignEvidence(r *rand.Rand, params Params, pastTimes []time.Time, pastVoteInfos [][]abci.VoteInfo, event func(route, op, evResult string), header tmproto.Header, voteInfos []abci.VoteInfo, ) []abci.Evidence { diff --git a/simulation/executor/util.go b/simulation/executor/util.go index 849461124cb..e77e055b948 100644 --- a/simulation/executor/util.go +++ b/simulation/executor/util.go @@ -8,6 +8,7 @@ import ( "github.com/osmosis-labs/osmosis/v17/simulation/simtypes" ) +//nolint:unparam func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *testing.B) { tb.Helper() testingMode = false diff --git a/simulation/simtypes/txbuilder.go b/simulation/simtypes/txbuilder.go index df0a4390801..95c9ae47bce 100644 --- a/simulation/simtypes/txbuilder.go +++ b/simulation/simtypes/txbuilder.go @@ -62,14 +62,14 @@ func (sim *SimCtx) deliverTx(tx sdk.Tx, msg sdk.Msg, msgName string) (simulation txConfig := params.MakeEncodingConfig().TxConfig // TODO: unhardcode gasInfo, results, err := sim.BaseApp().Deliver(txConfig.TxEncoder(), tx) if err != nil { - return simulation.NoOpMsg(msgName, msgName, fmt.Sprintf("unable to deliver tx. \nreason: %v\n results: %v\n msg: %s\n tx: %s", err, results, msg, tx)), nil, nil, err + return simulation.NoOpMsg(msgName, msgName, fmt.Sprintf("unable to deliver tx. \nreason: %v\n results: %v\n msg: %s\n tx: %s", err, results, msg, tx)), []simulation.FutureOperation{}, nil, err } opMsg := simulation.NewOperationMsg(msg, true, "", gasInfo.GasWanted, gasInfo.GasUsed, nil) opMsg.Route = msgName opMsg.Name = msgName - return opMsg, nil, results.Data, nil + return opMsg, []simulation.FutureOperation{}, results.Data, nil } // GenTx generates a signed mock transaction. diff --git a/tests/cl-genesis-positions/convert.go b/tests/cl-genesis-positions/convert.go index ecf0b8311e2..911b45430d6 100644 --- a/tests/cl-genesis-positions/convert.go +++ b/tests/cl-genesis-positions/convert.go @@ -111,7 +111,7 @@ func ConvertSubgraphToOsmosisGenesis(positionCreatorAddresses []sdk.AccAddress, // Initialize first position to be 1:1 price // this is because the first position must have non-zero token0 and token1 to initialize the price // however, our data has first position with non-zero amount. - _, _, _, _, err = osmosis.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(osmosis.Ctx, pool.GetId(), osmosis.TestAccs[0], sdk.NewCoins(sdk.NewCoin(msgCreatePool.Denom0, sdk.NewInt(100)), sdk.NewCoin(msgCreatePool.Denom1, sdk.NewInt(100)))) + _, err = osmosis.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(osmosis.Ctx, pool.GetId(), osmosis.TestAccs[0], sdk.NewCoins(sdk.NewCoin(msgCreatePool.Denom0, sdk.NewInt(100)), sdk.NewCoin(msgCreatePool.Denom1, sdk.NewInt(100)))) if err != nil { panic(err) } @@ -138,7 +138,7 @@ func ConvertSubgraphToOsmosisGenesis(positionCreatorAddresses []sdk.AccAddress, if err != nil { panic(err) } - lowerTickOsmosis, err := math.SqrtPriceToTickRoundDownSpacing(sqrtPriceLower, pool.GetTickSpacing()) + lowerTickOsmosis, err := math.SqrtPriceToTickRoundDownSpacing(osmomath.BigDecFromSDKDec(sqrtPriceLower), pool.GetTickSpacing()) if err != nil { panic(err) } @@ -147,7 +147,7 @@ func ConvertSubgraphToOsmosisGenesis(positionCreatorAddresses []sdk.AccAddress, if err != nil { panic(err) } - upperTickOsmosis, err := math.SqrtPriceToTickRoundDownSpacing(sqrtPriceUpper, pool.GetTickSpacing()) + upperTickOsmosis, err := math.SqrtPriceToTickRoundDownSpacing(osmomath.BigDecFromSDKDec(sqrtPriceUpper), pool.GetTickSpacing()) if err != nil { panic(err) } diff --git a/tests/e2e/configurer/chain/chain.go b/tests/e2e/configurer/chain/chain.go index 268f6674b8e..7f35df9dcca 100644 --- a/tests/e2e/configurer/chain/chain.go +++ b/tests/e2e/configurer/chain/chain.go @@ -26,11 +26,10 @@ type Config struct { VotingPeriod float32 ExpeditedVotingPeriod float32 // upgrade proposal height for chain. - UpgradePropHeight int64 - LatestProposalNumber int - LatestLockNumber int - NodeConfigs []*NodeConfig - NodeTempConfigs []*NodeConfig + UpgradePropHeight int64 + + NodeConfigs []*NodeConfig + NodeTempConfigs []*NodeConfig LatestCodeId int @@ -227,6 +226,12 @@ func (c *Config) SendIBC(dstChain *Config, recipient string, token sdk.Coin) { c.t.Log("successfully sent IBC tokens") } +func (c *Config) GetAllChainNodes() []*NodeConfig { + nodeConfigs := make([]*NodeConfig, len(c.NodeConfigs)) + copy(nodeConfigs, c.NodeConfigs) + return nodeConfigs +} + // GetDefaultNode returns the default node of the chain. // The default node is the first one created. Returns error if no // ndoes created. @@ -251,21 +256,15 @@ func (c *Config) GetNodeAtIndex(nodeIndex int) (*NodeConfig, error) { func (c *Config) getNodeAtIndex(nodeIndex int) (*NodeConfig, error) { if nodeIndex > len(c.NodeConfigs) { - return nil, fmt.Errorf("node index (%d) is greter than the number of nodes available (%d)", nodeIndex, len(c.NodeConfigs)) + return nil, fmt.Errorf("node index (%d) is greater than the number of nodes available (%d)", nodeIndex, len(c.NodeConfigs)) } return c.NodeConfigs[nodeIndex], nil } -func (c *Config) SubmitCreateConcentratedPoolProposal() (uint64, error) { - node, err := c.GetDefaultNode() - if err != nil { - return 0, err - } - - propNumber := node.SubmitCreateConcentratedPoolProposal(sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(config.InitialMinDeposit))) - c.LatestProposalNumber += 1 +func (c *Config) SubmitCreateConcentratedPoolProposal(chainANode *NodeConfig) (uint64, error) { + propNumber := chainANode.SubmitCreateConcentratedPoolProposal(sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(config.InitialMinDeposit))) - node.DepositProposal(propNumber, false) + chainANode.DepositProposal(propNumber, false) var wg sync.WaitGroup @@ -280,12 +279,12 @@ func (c *Config) SubmitCreateConcentratedPoolProposal() (uint64, error) { wg.Wait() require.Eventually(c.t, func() bool { - status, err := node.QueryPropStatus(propNumber) + status, err := chainANode.QueryPropStatus(propNumber) if err != nil { return false } return status == proposalStatusPassed - }, time.Second*30, time.Millisecond*500) - poolId := node.QueryNumPools() + }, time.Second*30, 10*time.Millisecond) + poolId := chainANode.QueryNumPools() return poolId, nil } diff --git a/tests/e2e/configurer/chain/commands.go b/tests/e2e/configurer/chain/commands.go index 419cde40ada..a9c3c3e9358 100644 --- a/tests/e2e/configurer/chain/commands.go +++ b/tests/e2e/configurer/chain/commands.go @@ -2,6 +2,7 @@ package chain import ( "encoding/json" + "errors" "fmt" "os" "path/filepath" @@ -11,6 +12,7 @@ import ( "sync" "time" + transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" "github.com/tendermint/tendermint/libs/bytes" appparams "github.com/osmosis-labs/osmosis/v17/app/params" @@ -40,21 +42,21 @@ type params struct { Value string `json:"value"` } +func (n *NodeConfig) SetMaxPoolPointsPerTx(maxPoolPoints int, from string) { + n.LogActionF("setting max pool points per tx for protorev at %d points", maxPoolPoints) + cmd := []string{"osmosisd", "tx", "protorev", "set-max-pool-points-per-tx", fmt.Sprintf("%d", maxPoolPoints), fmt.Sprintf("--from=%s", from), "--gas=700000", "--fees=5000uosmo"} + _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + require.NoError(n.t, err) +} + func (n *NodeConfig) CreateBalancerPool(poolFile, from string) uint64 { n.LogActionF("creating balancer pool from file %s", poolFile) cmd := []string{"osmosisd", "tx", "gamm", "create-pool", fmt.Sprintf("--pool-file=/osmosis/%s", poolFile), fmt.Sprintf("--from=%s", from), "--gas=700000", "--fees=5000uosmo"} resp, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) require.NoError(n.t, err) - // TODO: create a helper function for parsing pool ID and prop ID from the response - startIndex := strings.Index(resp.String(), `{"key":"pool_id","value":"`) + len(`{"key":"pool_id","value":"`) - endIndex := strings.Index(resp.String()[startIndex:], `"`) - - // Extract the proposal ID substring - codeIdStr := resp.String()[startIndex : startIndex+endIndex] - - // Convert the proposal ID from string to int - poolID, _ := strconv.ParseUint(codeIdStr, 10, 64) + poolID, err := extractPoolIdFromResponse(resp.String()) + require.NoError(n.t, err) n.LogActionF("successfully created balancer pool %d", poolID) return poolID @@ -66,14 +68,8 @@ func (n *NodeConfig) CreateStableswapPool(poolFile, from string) uint64 { resp, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) require.NoError(n.t, err) - startIndex := strings.Index(resp.String(), `{"key":"pool_id","value":"`) + len(`{"key":"pool_id","value":"`) - endIndex := strings.Index(resp.String()[startIndex:], `"`) - - // Extract the proposal ID substring - codeIdStr := resp.String()[startIndex : startIndex+endIndex] - - // Convert the proposal ID from string to int - poolID, _ := strconv.ParseUint(codeIdStr, 10, 64) + poolID, err := extractPoolIdFromResponse(resp.String()) + require.NoError(n.t, err) n.LogActionF("successfully created stableswap pool with ID %d", poolID) return poolID @@ -91,26 +87,18 @@ func (n *NodeConfig) CollectSpreadRewards(from, positionIds string) { // CreateConcentratedPool creates a concentrated pool. // Returns pool id of newly created pool on success -func (n *NodeConfig) CreateConcentratedPool(from, denom1, denom2 string, tickSpacing uint64, spreadFactor string) (uint64, error) { +func (n *NodeConfig) CreateConcentratedPool(from, denom1, denom2 string, tickSpacing uint64, spreadFactor string) uint64 { n.LogActionF("creating concentrated pool") cmd := []string{"osmosisd", "tx", "concentratedliquidity", "create-pool", denom1, denom2, fmt.Sprintf("%d", tickSpacing), spreadFactor, fmt.Sprintf("--from=%s", from)} resp, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) - if err != nil { - return 0, err - } - - startIndex := strings.Index(resp.String(), `{"key":"pool_id","value":"`) + len(`{"key":"pool_id","value":"`) - endIndex := strings.Index(resp.String()[startIndex:], `"`) - - // Extract the proposal ID substring - codeIdStr := resp.String()[startIndex : startIndex+endIndex] + require.NoError(n.t, err) - // Convert the proposal ID from string to int - poolID, _ := strconv.ParseUint(codeIdStr, 10, 64) + poolID, err := extractPoolIdFromResponse(resp.String()) + require.NoError(n.t, err) n.LogActionF("successfully created concentrated pool with ID %d", poolID) - return poolID, nil + return poolID } // CreateConcentratedPosition creates a concentrated position from [lowerTick; upperTick] in pool with id of poolId @@ -120,17 +108,10 @@ func (n *NodeConfig) CreateConcentratedPosition(from, lowerTick, upperTick strin // gas = 50,000 because e2e default to 40,000, we hardcoded extra 10k gas to initialize tick // fees = 1250 (because 50,000 * 0.0025 = 1250) cmd := []string{"osmosisd", "tx", "concentratedliquidity", "create-position", fmt.Sprint(poolId), lowerTick, upperTick, tokens, fmt.Sprintf("%d", token0MinAmt), fmt.Sprintf("%d", token1MinAmt), fmt.Sprintf("--from=%s", from), "--gas=500000", "--fees=1250uosmo", "-o json"} - outJson, _, err := n.containerManager.ExecTxCmdWithSuccessString(n.t, n.chainId, n.Name, cmd, "code\":0") + resp, _, err := n.containerManager.ExecTxCmdWithSuccessString(n.t, n.chainId, n.Name, cmd, "code\":0") require.NoError(n.t, err) - var txResponse map[string]interface{} - err = json.Unmarshal(outJson.Bytes(), &txResponse) - require.NoError(n.t, err) - - positionIDString, err := GetPositionID(txResponse) - require.NoError(n.t, err) - - positionID, err := strconv.ParseUint(positionIDString, 10, 64) + positionID, err := extractPositionIdFromResponse(resp.Bytes()) require.NoError(n.t, err) n.LogActionF("successfully created concentrated position from %s to %s", lowerTick, upperTick) @@ -143,14 +124,10 @@ func (n *NodeConfig) StoreWasmCode(wasmFile, from string) int { cmd := []string{"osmosisd", "tx", "wasm", "store", wasmFile, fmt.Sprintf("--from=%s", from), "--gas=auto", "--gas-prices=0.1uosmo", "--gas-adjustment=1.3"} resp, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) require.NoError(n.t, err) - startIndex := strings.Index(resp.String(), `{"key":"code_id","value":"`) + len(`{"key":"code_id","value":"`) - endIndex := strings.Index(resp.String()[startIndex:], `"`) - // Extract the proposal ID substring - codeIdStr := resp.String()[startIndex : startIndex+endIndex] + codeId, err := extractCodeIdFromResponse(resp.String()) + require.NoError(n.t, err) - // Convert the proposal ID from string to int - codeId, _ := strconv.Atoi(codeIdStr) n.LogActionF("successfully stored") return codeId } @@ -235,17 +212,10 @@ func (n *NodeConfig) SubmitParamChangeProposal(proposalJson, from string) int { resp, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) require.NoError(n.t, err) - err = os.Remove(localProposalFile) - require.NoError(n.t, err) - - startIndex := strings.Index(resp.String(), `[{"key":"proposal_id","value":"`) + len(`[{"key":"proposal_id","value":"`) - endIndex := strings.Index(resp.String()[startIndex:], `"`) - - // Extract the proposal ID substring - proposalIDStr := resp.String()[startIndex : startIndex+endIndex] + os.Remove(localProposalFile) - // Convert the proposal ID from string to int - proposalID, _ := strconv.Atoi(proposalIDStr) + proposalID, err := extractProposalIdFromResponse(resp.String()) + require.NoError(n.t, err) n.LogActionF("successfully submitted param change proposal") @@ -255,8 +225,8 @@ func (n *NodeConfig) SubmitParamChangeProposal(proposalJson, from string) int { func (n *NodeConfig) SendIBCTransfer(dstChain *Config, from, recipient, memo string, token sdk.Coin) { n.LogActionF("IBC sending %s from %s to %s. memo: %s", token.Amount.String(), from, recipient, memo) - cmd := []string{"hermes", "tx", "ft-transfer", "--dst-chain", dstChain.Id, "--src-chain", n.chainId, "--src-port", "transfer", "--src-channel", "channel-0", "--amount", token.Amount.String(), fmt.Sprintf("--denom=%s", token.Denom), fmt.Sprintf("--receiver=%s", recipient), "--timeout-height-offset=1000", "--memo", memo} - _, _, err := n.containerManager.ExecHermesCmd(n.t, cmd, "SUCCESS") + cmd := []string{"osmosisd", "tx", "ibc-transfer", "transfer", "transfer", "channel-0", recipient, token.String(), fmt.Sprintf("--from=%s", from), "--memo", memo} + _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) require.NoError(n.t, err) cmd = []string{"hermes", "clear", "packets", "--chain", dstChain.Id, "--port", "transfer", "--channel", "channel-0"} @@ -274,7 +244,6 @@ func (n *NodeConfig) FailIBCTransfer(from, recipient, amount string) { n.LogActionF("IBC sending %s from %s to %s", amount, from, recipient) cmd := []string{"osmosisd", "tx", "ibc-transfer", "transfer", "transfer", "channel-0", recipient, amount, fmt.Sprintf("--from=%s", from)} - _, _, err := n.containerManager.ExecTxCmdWithSuccessString(n.t, n.chainId, n.Name, cmd, "rate limit exceeded") require.NoError(n.t, err) @@ -310,12 +279,19 @@ func (n *NodeConfig) ExitPool(from, minAmountsOut string, poolId uint64, shareAm n.LogActionF("successfully exited pool %d, minAmountsOut %s, shareAmountIn %s", poolId, minAmountsOut, shareAmountIn) } -func (n *NodeConfig) SubmitUpgradeProposal(upgradeVersion string, upgradeHeight int64, initialDeposit sdk.Coin) { +func (n *NodeConfig) SubmitUpgradeProposal(upgradeVersion string, upgradeHeight int64, initialDeposit sdk.Coin) int { n.LogActionF("submitting upgrade proposal %s for height %d", upgradeVersion, upgradeHeight) cmd := []string{"osmosisd", "tx", "gov", "submit-proposal", "software-upgrade", upgradeVersion, fmt.Sprintf("--title=\"%s upgrade\"", upgradeVersion), "--description=\"upgrade proposal submission\"", fmt.Sprintf("--upgrade-height=%d", upgradeHeight), "--upgrade-info=\"\"", "--from=val", fmt.Sprintf("--deposit=%s", initialDeposit)} - _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + resp, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + require.NoError(n.t, err) + + proposalID, err := extractProposalIdFromResponse(resp.String()) + require.NoError(n.t, err) + require.NoError(n.t, err) n.LogActionF("successfully submitted upgrade proposal") + + return proposalID } func (n *NodeConfig) SubmitSuperfluidProposal(asset string, initialDeposit sdk.Coin) int { @@ -324,15 +300,8 @@ func (n *NodeConfig) SubmitSuperfluidProposal(asset string, initialDeposit sdk.C resp, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) require.NoError(n.t, err) - // Extract the proposal ID from the response - startIndex := strings.Index(resp.String(), `[{"key":"proposal_id","value":"`) + len(`[{"key":"proposal_id","value":"`) - endIndex := strings.Index(resp.String()[startIndex:], `"`) - - // Extract the proposal ID substring - proposalIDStr := resp.String()[startIndex : startIndex+endIndex] - - // Convert the proposal ID from string to int - proposalID, _ := strconv.Atoi(proposalIDStr) + proposalID, err := extractProposalIdFromResponse(resp.String()) + require.NoError(n.t, err) n.LogActionF("successfully submitted superfluid proposal for asset %s", asset) @@ -344,15 +313,9 @@ func (n *NodeConfig) SubmitCreateConcentratedPoolProposal(initialDeposit sdk.Coi cmd := []string{"osmosisd", "tx", "gov", "submit-proposal", "create-concentratedliquidity-pool-proposal", "--pool-records=stake,uosmo,100,0.001", "--title=\"create concentrated pool\"", "--description=\"create concentrated pool", "--from=val", fmt.Sprintf("--deposit=%s", initialDeposit)} resp, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) require.NoError(n.t, err) - // Extract the proposal ID from the response - startIndex := strings.Index(resp.String(), `[{"key":"proposal_id","value":"`) + len(`[{"key":"proposal_id","value":"`) - endIndex := strings.Index(resp.String()[startIndex:], `"`) - - // Extract the proposal ID substring - proposalIDStr := resp.String()[startIndex : startIndex+endIndex] - // Convert the proposal ID from string to int - proposalID, _ := strconv.Atoi(proposalIDStr) + proposalID, err := extractProposalIdFromResponse(resp.String()) + require.NoError(n.t, err) n.LogActionF("successfully created a create concentrated liquidity pool proposal") @@ -368,30 +331,29 @@ func (n *NodeConfig) SubmitTextProposal(text string, initialDeposit sdk.Coin, is resp, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) require.NoError(n.t, err) - // Extract the proposal ID from the response - startIndex := strings.Index(resp.String(), `[{"key":"proposal_id","value":"`) + len(`[{"key":"proposal_id","value":"`) - endIndex := strings.Index(resp.String()[startIndex:], `"`) - - // Extract the proposal ID substring - proposalIDStr := resp.String()[startIndex : startIndex+endIndex] - - // Convert the proposal ID from string to int - proposalID, _ := strconv.Atoi(proposalIDStr) + proposalID, err := extractProposalIdFromResponse(resp.String()) + require.NoError(n.t, err) n.LogActionF("successfully submitted text gov proposal") return proposalID } -func (n *NodeConfig) SubmitTickSpacingReductionProposal(poolTickSpacingRecords string, initialDeposit sdk.Coin, isExpedited bool) { +func (n *NodeConfig) SubmitTickSpacingReductionProposal(poolTickSpacingRecords string, initialDeposit sdk.Coin, isExpedited bool) int { n.LogActionF("submitting tick spacing reduction gov proposal") cmd := []string{"osmosisd", "tx", "gov", "submit-proposal", "tick-spacing-decrease-proposal", "--title=\"test tick spacing reduction proposal title\"", "--description=\"test tick spacing reduction proposal\"", "--from=val", fmt.Sprintf("--deposit=%s", initialDeposit), fmt.Sprintf("--pool-tick-spacing-records=%s", poolTickSpacingRecords)} if isExpedited { cmd = append(cmd, "--is-expedited=true") } - _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + resp, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + require.NoError(n.t, err) + + proposalID, err := extractProposalIdFromResponse(resp.String()) require.NoError(n.t, err) + n.LogActionF("successfully submitted tick spacing reduction gov proposal") + + return proposalID } func (n *NodeConfig) DepositProposal(proposalNumber int, isExpedited bool) { @@ -498,28 +460,49 @@ func (n *NodeConfig) FundCommunityPool(sendAddress string, funds string) { // This method also funds fee tokens from the `initialization.ValidatorWalletName` account. // TODO: Abstract this to be a fee token provider account. -func (n *NodeConfig) CreateWallet(walletName string) string { +func (n *NodeConfig) CreateWallet(walletName string, chain *Config) string { n.LogActionF("creating wallet %s", walletName) cmd := []string{"osmosisd", "keys", "add", walletName, "--keyring-backend=test"} - outBuf, _, err := n.containerManager.ExecCmd(n.t, n.Name, cmd, "") + outBuf, errBuf, err := n.containerManager.ExecCmd(n.t, n.Name, cmd, "") require.NoError(n.t, err) re := regexp.MustCompile("osmo1(.{38})") walletAddr := fmt.Sprintf("%s\n", re.FindString(outBuf.String())) walletAddr = strings.TrimSuffix(walletAddr, "\n") + + mnemonic, err := pullMnemonicFromResponse(errBuf.String()) + require.NoError(n.t, err) + + chainNodes := chain.GetAllChainNodes() + for _, node := range chainNodes { + if node.Name == n.Name { + continue + } + node.AddExistingWallet(walletName, mnemonic) + } + n.LogActionF("created wallet %s, wallet address - %s", walletName, walletAddr) n.BankSend(initialization.WalletFeeTokens.String(), initialization.ValidatorWalletName, walletAddr) n.LogActionF("Sent fee tokens from %s", initialization.ValidatorWalletName) return walletAddr } -func (n *NodeConfig) CreateWalletAndFund(walletName string, tokensToFund []string) string { - return n.CreateWalletAndFundFrom(walletName, initialization.ValidatorWalletName, tokensToFund) +func (n *NodeConfig) AddExistingWallet(walletName, mnemonic string) { + n.LogActionF("adding existing wallet %s", walletName) + cmd := []string{"sh", "-c", fmt.Sprintf("echo '%s' | osmosisd keys add %s --keyring-backend=test --recover", mnemonic, walletName)} + _, _, err := n.containerManager.ExecCmd(n.t, n.Name, cmd, "") + require.NoError(n.t, err) + + n.LogActionF("added existing wallet %s", walletName) } -func (n *NodeConfig) CreateWalletAndFundFrom(newWalletName string, fundingWalletName string, tokensToFund []string) string { +func (n *NodeConfig) CreateWalletAndFund(walletName string, tokensToFund []string, chain *Config) string { + return n.CreateWalletAndFundFrom(walletName, initialization.ValidatorWalletName, tokensToFund, chain) +} + +func (n *NodeConfig) CreateWalletAndFundFrom(newWalletName string, fundingWalletName string, tokensToFund []string, chain *Config) string { n.LogActionF("Sending tokens to %s", newWalletName) - walletAddr := n.CreateWallet(newWalletName) + walletAddr := n.CreateWallet(newWalletName, chain) for _, tokenToFund := range tokensToFund { n.BankSend(tokenToFund, fundingWalletName, walletAddr) } @@ -642,54 +625,51 @@ func ParseEvent(responseJson map[string]interface{}, field string) (string, erro return "", fmt.Errorf("%s field not found in response", field) } -func (n *NodeConfig) SendIBC(dstChain *Config, recipient string, token sdk.Coin) { +var addrMutexMap = make(map[string]*sync.Mutex) + +func (n *NodeConfig) SendIBC(srcChain, dstChain *Config, recipient string, token sdk.Coin) { n.t.Logf("IBC sending %s from %s to %s (%s)", token, n.chainId, dstChain.Id, recipient) + // We add a mutex here since we don't want multiple IBC transfers to happen at the same time + // Otherwise, when we check if the receiving end has the correct balance, we might get the balance + // of a previous transfer. + sender := n.GetWallet(initialization.ValidatorWalletName) + + // Create or get the mutex for the specific sender and recipient + func() { + if _, exists := addrMutexMap[recipient]; !exists { + addrMutexMap[recipient] = &sync.Mutex{} + } + if _, exists := addrMutexMap[sender]; !exists { + addrMutexMap[sender] = &sync.Mutex{} + } + }() + + // Lock the mutex for the specific sender and recipient + addrMutexMap[recipient].Lock() + defer addrMutexMap[recipient].Unlock() + addrMutexMap[sender].Lock() + defer addrMutexMap[sender].Unlock() dstNode, err := dstChain.GetDefaultNode() require.NoError(n.t, err) - // removes the fee token from balances for calculating the difference in other tokens - // before and after the IBC send. Since we run tests in parallel now, some tests may - // send uosmo between accounts while this test is running. Since we don't care about - // non ibc denoms, its safe to filter uosmo out. - removeFeeTokenFromBalance := func(balance sdk.Coins) sdk.Coins { - filteredCoinDenoms := []string{} - for _, coin := range balance { - if !strings.HasPrefix(coin.Denom, "ibc/") { - filteredCoinDenoms = append(filteredCoinDenoms, coin.Denom) - } - } - feeRewardTokenBalance := balance.FilterDenoms(filteredCoinDenoms) - return balance.Sub(feeRewardTokenBalance) - } + denomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", "channel-0", token.Denom)) + ibcDenom := denomTrace.IBCDenom() - balancesDstPreWithTxFeeBalance, err := dstNode.QueryBalances(recipient) - require.NoError(n.t, err) - fmt.Println("balancesDstPre with no fee removed: ", balancesDstPreWithTxFeeBalance) - balancesDstPre := removeFeeTokenFromBalance(balancesDstPreWithTxFeeBalance) - cmd := []string{"hermes", "tx", "ft-transfer", "--dst-chain", dstChain.Id, "--src-chain", n.chainId, "--src-port", "transfer", "--src-channel", "channel-0", "--amount", token.Amount.String(), fmt.Sprintf("--denom=%s", token.Denom), fmt.Sprintf("--receiver=%s", recipient), "--timeout-height-offset=1000"} - _, _, err = n.containerManager.ExecHermesCmd(n.t, cmd, "SUCCESS") + balancePre, err := dstNode.QueryBalance(recipient, ibcDenom) require.NoError(n.t, err) + n.SendIBCTransfer(dstChain, sender, recipient, "", token) + require.Eventually( n.t, func() bool { - balancesDstPostWithTxFeeBalance, err := dstNode.QueryBalances(recipient) + balancePost, err := dstNode.QueryBalance(recipient, ibcDenom) require.NoError(n.t, err) - balancesDstPost := removeFeeTokenFromBalance(balancesDstPostWithTxFeeBalance) - - ibcCoin := balancesDstPost.Sub(balancesDstPre) - if ibcCoin.Len() == 1 { - tokenPre := balancesDstPre.AmountOfNoDenomValidation(ibcCoin[0].Denom) - tokenPost := balancesDstPost.AmountOfNoDenomValidation(ibcCoin[0].Denom) - resPre := token.Amount - resPost := tokenPost.Sub(tokenPre) - return resPost.Uint64() == resPre.Uint64() - } else { - return false - } + + return balancePost.Amount.Equal(balancePre.Amount.Add(token.Amount)) }, - time.Minute, + 3*time.Minute, 10*time.Millisecond, "tx not received on destination chain", ) @@ -699,7 +679,6 @@ func (n *NodeConfig) SendIBC(dstChain *Config, recipient string, token sdk.Coin) func (n *NodeConfig) EnableSuperfluidAsset(srcChain *Config, denom string) { propNumber := n.SubmitSuperfluidProposal(denom, sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(config.InitialMinDeposit))) - srcChain.LatestProposalNumber += 1 n.DepositProposal(propNumber, false) var wg sync.WaitGroup @@ -718,15 +697,13 @@ func (n *NodeConfig) EnableSuperfluidAsset(srcChain *Config, denom string) { func (n *NodeConfig) LockAndAddToExistingLock(srcChain *Config, amount sdk.Int, denom, lockupWalletAddr, lockupWalletSuperfluidAddr string) { // lock tokens lockID := n.LockTokens(fmt.Sprintf("%v%s", amount, denom), "240s", lockupWalletAddr) - srcChain.LatestLockNumber += 1 - fmt.Println("lock number: ", lockID) + // add to existing lock n.AddToExistingLock(amount, denom, "240s", lockupWalletAddr, lockID) // superfluid lock tokens lockID = n.LockTokens(fmt.Sprintf("%v%s", amount, denom), "240s", lockupWalletSuperfluidAddr) - srcChain.LatestLockNumber += 1 - fmt.Println("lock number: ", lockID) + n.SuperfluidDelegate(lockID, srcChain.NodeConfigs[1].OperatorAddress, lockupWalletSuperfluidAddr) // add to existing lock n.AddToExistingLock(amount, denom, "240s", lockupWalletSuperfluidAddr, lockID) @@ -804,12 +781,7 @@ func (n *NodeConfig) ParamChangeProposal(subspace, key string, value []byte, cha return err } - node, err := chain.GetDefaultNode() - if err != nil { - return err - } - propNumber := node.SubmitParamChangeProposal(string(proposalJson), initialization.ValidatorWalletName) - chain.LatestProposalNumber += 1 + propNumber := n.SubmitParamChangeProposal(string(proposalJson), initialization.ValidatorWalletName) var wg sync.WaitGroup @@ -824,7 +796,7 @@ func (n *NodeConfig) ParamChangeProposal(subspace, key string, value []byte, cha wg.Wait() require.Eventually(n.t, func() bool { - status, err := node.QueryPropStatus(propNumber) + status, err := n.QueryPropStatus(propNumber) if err != nil { return false } @@ -832,3 +804,94 @@ func (n *NodeConfig) ParamChangeProposal(subspace, key string, value []byte, cha }, time.Minute, 10*time.Millisecond) return nil } + +func extractProposalIdFromResponse(response string) (int, error) { + // Extract the proposal ID from the response + startIndex := strings.Index(response, `[{"key":"proposal_id","value":"`) + len(`[{"key":"proposal_id","value":"`) + endIndex := strings.Index(response[startIndex:], `"`) + + // Extract the proposal ID substring + proposalIDStr := response[startIndex : startIndex+endIndex] + + // Convert the proposal ID from string to int + proposalID, err := strconv.Atoi(proposalIDStr) + if err != nil { + return 0, err + } + + return proposalID, nil +} + +func extractPoolIdFromResponse(response string) (uint64, error) { + // Extract the pool ID from the response + startIndex := strings.Index(response, `{"key":"pool_id","value":"`) + len(`{"key":"pool_id","value":"`) + endIndex := strings.Index(response[startIndex:], `"`) + + // Extract the pool ID substring + codeIdStr := response[startIndex : startIndex+endIndex] + + // Convert the pool ID from string to int + poolID, err := strconv.ParseUint(codeIdStr, 10, 64) + if err != nil { + return 0, err + } + + return poolID, nil +} + +func extractPositionIdFromResponse(responseBytes []byte) (uint64, error) { + var txResponse map[string]interface{} + err := json.Unmarshal(responseBytes, &txResponse) + if err != nil { + return 0, err + } + + positionIDString, err := GetPositionID(txResponse) + if err != nil { + return 0, err + } + + positionID, err := strconv.ParseUint(positionIDString, 10, 64) + if err != nil { + return 0, err + } + + return positionID, nil +} + +func extractCodeIdFromResponse(response string) (int, error) { + startIndex := strings.Index(response, `{"key":"code_id","value":"`) + len(`{"key":"code_id","value":"`) + endIndex := strings.Index(response[startIndex:], `"`) + + // Extract the proposal ID substring + codeIdStr := response[startIndex : startIndex+endIndex] + + // Convert the proposal ID from string to int + codeId, err := strconv.Atoi(codeIdStr) + if err != nil { + return 0, err + } + + return codeId, nil +} + +func pullMnemonicFromResponse(response string) (string, error) { + // Using regex to get mnemonic + r := regexp.MustCompile(`(?m)(?i)^(\w+\s){23}\w+$`) + mnemonicMatch := r.FindString(response) + + // Check if we found the mnemonic + if mnemonicMatch == "" { + return "", errors.New("mnemonic not found") + } + + // Split the mnemonicMatch on spaces to get individual words + mnemonicWords := strings.Split(mnemonicMatch, " ") + + // Check if we got 24 words + if len(mnemonicWords) != 24 { + return "", errors.New("mnemonic does not contain 24 words") + } + + return mnemonicMatch, nil +} diff --git a/tests/e2e/configurer/chain/queries.go b/tests/e2e/configurer/chain/queries.go index 759dc7a12e1..436db9a9075 100644 --- a/tests/e2e/configurer/chain/queries.go +++ b/tests/e2e/configurer/chain/queries.go @@ -118,20 +118,20 @@ func (n *NodeConfig) QueryProtoRevDeveloperAccount() (sdk.AccAddress, error) { return account, nil } -// QueryProtoRevPoolWeights gets the pool point weights of the module. -func (n *NodeConfig) QueryProtoRevPoolWeights() (protorevtypes.PoolWeights, error) { - path := "/osmosis/protorev/pool_weights" +// QueryProtoRevInfoByPoolType gets information on how the module handles different pool types. +func (n *NodeConfig) QueryProtoRevInfoByPoolType() (*protorevtypes.InfoByPoolType, error) { + path := "/osmosis/protorev/info_by_pool_type" bz, err := n.QueryGRPCGateway(path) if err != nil { - return protorevtypes.PoolWeights{}, err + return nil, err } // nolint: staticcheck - var response protorevtypes.QueryGetProtoRevPoolWeightsResponse + var response protorevtypes.QueryGetProtoRevInfoByPoolTypeResponse err = util.Cdc.UnmarshalJSON(bz, &response) require.NoError(n.t, err) // this error should not happen - return response.PoolWeights, nil + return &response.InfoByPoolType, nil } // QueryProtoRevMaxPoolPointsPerTx gets the max pool points per tx of the module. @@ -339,6 +339,18 @@ func (n *NodeConfig) QueryBalances(address string) (sdk.Coins, error) { return balancesResp.GetBalances(), nil } +func (n *NodeConfig) QueryBalance(address, denom string) (sdk.Coin, error) { + path := fmt.Sprintf("cosmos/bank/v1beta1/balances/%s/by_denom?denom=%s", address, denom) + bz, err := n.QueryGRPCGateway(path) + require.NoError(n.t, err) + + var balancesResp banktypes.QueryBalanceResponse + if err := util.Cdc.UnmarshalJSON(bz, &balancesResp); err != nil { + return sdk.Coin{}, err + } + return *balancesResp.GetBalance(), nil +} + func (n *NodeConfig) QuerySupplyOf(denom string) (sdk.Int, error) { path := fmt.Sprintf("cosmos/bank/v1beta1/supply/%s", denom) bz, err := n.QueryGRPCGateway(path) diff --git a/tests/e2e/configurer/config/constants.go b/tests/e2e/configurer/config/constants.go index de1c38af4ff..28713db9edd 100644 --- a/tests/e2e/configurer/config/constants.go +++ b/tests/e2e/configurer/config/constants.go @@ -33,15 +33,15 @@ var ( DaiOsmoPoolIdv16 uint64 // A pool created via CLI before starting an // upgrade. - PreUpgradePoolId uint64 + PreUpgradePoolId = []uint64{} - PreUpgradeStableSwapPoolId uint64 + PreUpgradeStableSwapPoolId = []uint64{} - StrideMigrateWallet = "stride-migration" + StrideMigrateWallet = []string{"stride-migration", "stride-migration"} - LockupWallet = "lockup-wallet" + LockupWallet = []string{"lockup-wallet", "lockup-wallet"} - LockupWalletSuperfluid = "lockup-wallet-superfluid" + LockupWalletSuperfluid = []string{"lockup-wallet-superfluid", "lockup-wallet-superfluid"} - StableswapWallet = "stableswap-wallet" + StableswapWallet = []string{"stableswap-wallet", "stableswap-wallet"} ) diff --git a/tests/e2e/configurer/upgrade.go b/tests/e2e/configurer/upgrade.go index bdddce8015d..b71559fff5d 100644 --- a/tests/e2e/configurer/upgrade.go +++ b/tests/e2e/configurer/upgrade.go @@ -11,7 +11,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" appparams "github.com/osmosis-labs/osmosis/v17/app/params" - v17 "github.com/osmosis-labs/osmosis/v17/app/upgrades/v17" "github.com/osmosis-labs/osmosis/v17/tests/e2e/configurer/chain" "github.com/osmosis-labs/osmosis/v17/tests/e2e/configurer/config" "github.com/osmosis-labs/osmosis/v17/tests/e2e/containers" @@ -126,134 +125,134 @@ func (uc *UpgradeConfigurer) CreatePreUpgradeState() error { go func() { defer wg.Done() - chainA.SendIBC(chainB, chainB.NodeConfigs[0].PublicAddress, initialization.OsmoToken) - chainA.SendIBC(chainB, chainB.NodeConfigs[0].PublicAddress, initialization.StakeToken) + chainA.SendIBC(chainB, chainBNode.PublicAddress, initialization.OsmoToken) + chainA.SendIBC(chainB, chainBNode.PublicAddress, initialization.StakeToken) }() go func() { defer wg.Done() - chainB.SendIBC(chainA, chainA.NodeConfigs[0].PublicAddress, initialization.OsmoToken) - chainB.SendIBC(chainA, chainA.NodeConfigs[0].PublicAddress, initialization.StakeToken) + chainB.SendIBC(chainA, chainANode.PublicAddress, initialization.OsmoToken) + chainB.SendIBC(chainA, chainANode.PublicAddress, initialization.StakeToken) }() // Wait for all goroutines to complete wg.Wait() - // START: CAN REMOVE POST v17 UPGRADE - - v17SuperfluidAssets := v17GetSuperfluidAssets() + var ( + poolShareDenom = make([]string, 2) + preUpgradePoolId = make([]uint64, 2) + preUpgradeStableSwapPoolId = make([]uint64, 2) + ) + // Increment the WaitGroup counter for each goroutine wg.Add(4) + // Chain A + go func() { defer wg.Done() - chainANode.FundCommunityPool(initialization.ValidatorWalletName, strAllUpgradeBaseDenoms()) + preUpgradePoolId[0] = chainANode.CreateBalancerPool("pool1A.json", initialization.ValidatorWalletName) + poolShareDenom[0] = fmt.Sprintf("gamm/pool/%d", preUpgradePoolId[0]) + chainANode.EnableSuperfluidAsset(chainA, poolShareDenom[0]) }() go func() { defer wg.Done() - chainBNode.FundCommunityPool(initialization.ValidatorWalletName, strAllUpgradeBaseDenoms()) + preUpgradeStableSwapPoolId[0] = chainANode.CreateStableswapPool("stablePool.json", initialization.ValidatorWalletName) }() + // Chain B + go func() { defer wg.Done() - chainANode.EnableSuperfluidAsset(chainA, v17SuperfluidAssets) + preUpgradePoolId[1] = chainBNode.CreateBalancerPool("pool1B.json", initialization.ValidatorWalletName) + poolShareDenom[1] = fmt.Sprintf("gamm/pool/%d", preUpgradePoolId[1]) + chainBNode.EnableSuperfluidAsset(chainB, poolShareDenom[1]) }() go func() { defer wg.Done() - chainBNode.EnableSuperfluidAsset(chainB, v17SuperfluidAssets) + preUpgradeStableSwapPoolId[1] = chainBNode.CreateStableswapPool("stablePool.json", initialization.ValidatorWalletName) }() + // Wait for all goroutines to complete wg.Wait() - // END: CAN REMOVE POST v17 UPGRADE + config.PreUpgradePoolId = preUpgradePoolId + config.PreUpgradeStableSwapPoolId = preUpgradeStableSwapPoolId var ( - poolShareDenom string - preUpgradePoolId uint64 - preUpgradeStableSwapPoolId uint64 + lockupWallet = make([]string, 2) + lockupWalletSuperfluid = make([]string, 2) + stableswapWallet = make([]string, 2) ) - // Increment the WaitGroup counter for each goroutine - wg.Add(4) + wg.Add(6) + // Chain A go func() { defer wg.Done() - preUpgradePoolId = chainANode.CreateBalancerPool("pool1A.json", initialization.ValidatorWalletName) - poolShareDenom = fmt.Sprintf("gamm/pool/%d", preUpgradePoolId) - chainANode.EnableSuperfluidAsset(chainA, poolShareDenom) - }() - - go func() { - defer wg.Done() - chainBNode.CreateBalancerPool("pool1B.json", initialization.ValidatorWalletName) + // Setup wallets and send tokens to wallets (only chainA) + lockupWallet[0] = chainANode.CreateWalletAndFund(config.LockupWallet[0], []string{ + "10000000000000000000" + poolShareDenom[0], + }, chainA) }() go func() { defer wg.Done() - preUpgradeStableSwapPoolId = chainANode.CreateStableswapPool("stablePool.json", initialization.ValidatorWalletName) + lockupWalletSuperfluid[0] = chainANode.CreateWalletAndFund(config.LockupWalletSuperfluid[0], []string{ + "10000000000000000000" + poolShareDenom[0], + }, chainA) }() go func() { defer wg.Done() - chainBNode.CreateStableswapPool("stablePool.json", initialization.ValidatorWalletName) + stableswapWallet[0] = chainANode.CreateWalletAndFund(config.StableswapWallet[0], []string{ + "100000stake", + }, chainA) }() - // Wait for all goroutines to complete - wg.Wait() - - config.PreUpgradePoolId = preUpgradePoolId - config.PreUpgradeStableSwapPoolId = preUpgradeStableSwapPoolId - - var ( - lockupWallet string - lockupWalletSuperfluid string - stableswapWallet string - ) - - wg.Add(3) - + // Chain B go func() { defer wg.Done() // Setup wallets and send tokens to wallets (only chainA) - lockupWallet = chainANode.CreateWalletAndFund(config.LockupWallet, []string{ - "10000000000000000000" + poolShareDenom, - }) + lockupWallet[1] = chainBNode.CreateWalletAndFund(config.LockupWallet[1], []string{ + "10000000000000000000" + poolShareDenom[1], + }, chainB) }() go func() { defer wg.Done() - lockupWalletSuperfluid = chainANode.CreateWalletAndFund(config.LockupWalletSuperfluid, []string{ - "10000000000000000000" + poolShareDenom, - }) + lockupWalletSuperfluid[1] = chainBNode.CreateWalletAndFund(config.LockupWalletSuperfluid[1], []string{ + "10000000000000000000" + poolShareDenom[1], + }, chainB) }() go func() { defer wg.Done() - stableswapWallet = chainANode.CreateWalletAndFund(config.StableswapWallet, []string{ + stableswapWallet[1] = chainBNode.CreateWalletAndFund(config.StableswapWallet[1], []string{ "100000stake", - }) + }, chainB) }() - // Wait for all goroutines to complete wg.Wait() config.LockupWallet = lockupWallet config.LockupWalletSuperfluid = lockupWalletSuperfluid config.StableswapWallet = stableswapWallet - wg.Add(4) + wg.Add(6) var errCh = make(chan error, 2) + // Chain A + go func() { defer wg.Done() - // test swap exact amount in for stable swap pool (only chainA)A - chainANode.SwapExactAmountIn("2000stake", "1", fmt.Sprintf("%d", config.PreUpgradeStableSwapPoolId), "uosmo", config.StableswapWallet) + // test swap exact amount in for stable swap pool + chainANode.SwapExactAmountIn("2000stake", "1", fmt.Sprintf("%d", config.PreUpgradeStableSwapPoolId[0]), "uosmo", config.StableswapWallet[0]) }() - // Upload the rate limiting contract to both chains (as they both will be updated) go func() { defer wg.Done() uc.t.Logf("Uploading rate limiting contract to chainA") @@ -261,6 +260,20 @@ func (uc *UpgradeConfigurer) CreatePreUpgradeState() error { errCh <- err }() + go func() { + defer wg.Done() + uc.t.Logf("Lock and add to existing lock for both regular and superfluid lockups on chainA") + chainANode.LockAndAddToExistingLock(chainA, sdk.NewInt(1000000000000000000), poolShareDenom[0], config.LockupWallet[0], config.LockupWalletSuperfluid[0]) + }() + + // Chain B + + go func() { + defer wg.Done() + // test swap exact amount in for stable swap pool + chainBNode.SwapExactAmountIn("2000stake", "1", fmt.Sprintf("%d", config.PreUpgradeStableSwapPoolId[1]), "uosmo", config.StableswapWallet[1]) + }() + go func() { defer wg.Done() uc.t.Logf("Uploading rate limiting contract to chainB") @@ -270,8 +283,8 @@ func (uc *UpgradeConfigurer) CreatePreUpgradeState() error { go func() { defer wg.Done() - uc.t.Logf("Lock and add to existing lock for both regular and superfluid lockups on chainA") - chainANode.LockAndAddToExistingLock(chainA, sdk.NewInt(1000000000000000000), poolShareDenom, config.LockupWallet, config.LockupWalletSuperfluid) + uc.t.Logf("Lock and add to existing lock for both regular and superfluid lockups on chainB") + chainBNode.LockAndAddToExistingLock(chainB, sdk.NewInt(1000000000000000000), poolShareDenom[1], config.LockupWallet[1], config.LockupWalletSuperfluid[1]) }() wg.Wait() @@ -294,7 +307,7 @@ func (uc *UpgradeConfigurer) RunSetup() error { func (uc *UpgradeConfigurer) RunUpgrade() error { var err error if uc.forkHeight > 0 { - err = uc.runForkUpgrade() + uc.runForkUpgrade() } else { err = uc.runProposalUpgrade() } @@ -332,10 +345,9 @@ func (uc *UpgradeConfigurer) runProposalUpgrade() error { return err } chainConfig.UpgradePropHeight = currentHeight + int64(chainConfig.VotingPeriod) + int64(config.PropSubmitBlocks) + int64(config.PropBufferBlocks) - node.SubmitUpgradeProposal(uc.upgradeVersion, chainConfig.UpgradePropHeight, sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(config.InitialMinDeposit))) - chainConfig.LatestProposalNumber += 1 - node.DepositProposal(chainConfig.LatestProposalNumber, false) - propNumber := chainConfig.LatestProposalNumber + propNumber := node.SubmitUpgradeProposal(uc.upgradeVersion, chainConfig.UpgradePropHeight, sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(config.InitialMinDeposit))) + + node.DepositProposal(propNumber, false) var wg sync.WaitGroup @@ -376,13 +388,12 @@ func (uc *UpgradeConfigurer) runProposalUpgrade() error { return nil } -func (uc *UpgradeConfigurer) runForkUpgrade() error { +func (uc *UpgradeConfigurer) runForkUpgrade() { for _, chainConfig := range uc.chainConfigs { uc.t.Logf("waiting to reach fork height on chain %s", chainConfig.Id) chainConfig.WaitUntilHeight(uc.forkHeight) uc.t.Logf("fork height reached on chain %s", chainConfig.Id) } - return nil } func (uc *UpgradeConfigurer) upgradeContainers(chainConfig *chain.Config, propHeight int64) error { @@ -402,32 +413,3 @@ func (uc *UpgradeConfigurer) upgradeContainers(chainConfig *chain.Config, propHe uc.t.Logf("upgrade successful on chain %s", chainConfig.Id) return nil } - -// START: CAN REMOVE POST v17 UPGRADE - -func strAllUpgradeBaseDenoms() string { - upgradeBaseDenoms := "" - n := len(v17.AssetPairsForTestsOnly) - for i, assetPair := range v17.AssetPairsForTestsOnly { - upgradeBaseDenoms += "2000000" + assetPair.BaseAsset - if i < n-1 { // Check if it's not the last iteration - upgradeBaseDenoms += "," - } - } - return upgradeBaseDenoms -} - -func v17GetSuperfluidAssets() string { - assets := "" - for _, assetPair := range v17.AssetPairsForTestsOnly { - if assetPair.Superfluid { - assets += fmt.Sprintf("gamm/pool/%d,", assetPair.LinkedClassicPool) - } - } - if len(assets) > 0 { - assets = assets[:len(assets)-1] - } - return assets -} - -// END: CAN REMOVE POST v17 UPGRADE diff --git a/tests/e2e/containers/config.go b/tests/e2e/containers/config.go index 8746ddd5946..95b3f8fd0b5 100644 --- a/tests/e2e/containers/config.go +++ b/tests/e2e/containers/config.go @@ -24,10 +24,10 @@ const ( // It should be uploaded to Docker Hub. OSMOSIS_E2E_SKIP_UPGRADE should be unset // for this functionality to be used. previousVersionOsmoRepository = "osmolabs/osmosis-dev" - previousVersionOsmoTag = "16.0" + previousVersionOsmoTag = "17.0-alpine" // Pre-upgrade repo/tag for osmosis initialization (this should be one version below upgradeVersion) previousVersionInitRepository = "osmolabs/osmosis-e2e-init-chain" - previousVersionInitTag = "v16.x-f34d07f8-1690568248" + previousVersionInitTag = "17.0.0-rc0" // Hermes repo/version for relayer relayerRepository = "informalsystems/hermes" relayerTag = "1.5.1" diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index e8e57aee6aa..7a3f1b015a2 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -11,10 +11,12 @@ import ( "testing" "time" + "github.com/cosmos/cosmos-sdk/types/address" + transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" "github.com/iancoleman/orderedmap" - packetforwardingtypes "github.com/strangelove-ventures/packet-forward-middleware/v4/router/types" + packetforwardingtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v4/router/types" "github.com/osmosis-labs/osmosis/osmomath" ibchookskeeper "github.com/osmosis-labs/osmosis/x/ibc-hooks/keeper" @@ -27,21 +29,16 @@ import ( "github.com/osmosis-labs/osmosis/osmoutils/osmoassert" appparams "github.com/osmosis-labs/osmosis/v17/app/params" - v17 "github.com/osmosis-labs/osmosis/v17/app/upgrades/v17" "github.com/osmosis-labs/osmosis/v17/tests/e2e/configurer/chain" "github.com/osmosis-labs/osmosis/v17/tests/e2e/configurer/config" "github.com/osmosis-labs/osmosis/v17/tests/e2e/initialization" clmath "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/math" cltypes "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" + protorevtypes "github.com/osmosis-labs/osmosis/v17/x/protorev/types" ) // TODO: Find more scalable way to do this -// note: I don't think that these run in parallel. When linting, it complains about the t.Parallel() call -// being missing from the function TestAllE2E. So, I think that the tests don't actually run in parallel, and when they do, they fail. -func (s *IntegrationTestSuite) TestAllE2E() { //nolint:tparallel - // There appears to be an E2E quirk that requires a sleep here - time.Sleep(3 * time.Second) - +func (s *IntegrationTestSuite) TestAllE2E() { // Zero Dependent Tests s.T().Run("CreateConcentratedLiquidityPoolVoting_And_TWAP", func(t *testing.T) { t.Parallel() @@ -129,15 +126,6 @@ func (s *IntegrationTestSuite) TestAllE2E() { //nolint:tparallel }) } - if s.skipUpgrade { - s.T().Skip("Skipping ConcentratedLiquidity_CanonicalPools test") - } else { - s.T().Run("ConcentratedLiquidity_CanonicalPools", func(t *testing.T) { - t.Parallel() - s.ConcentratedLiquidity_CanonicalPools() - }) - } - // IBC Dependent Tests if s.skipIBC { @@ -197,7 +185,11 @@ func (s *IntegrationTestSuite) ProtoRev() { epochIdentifier = "day" ) - chainA, chainANode := s.getChainACfgs() + // NOTE: Uses chainA since IBC denoms are hard coded. + chainA, chainANode, err := s.getChainACfgs() + s.Require().NoError(err) + + sender := chainANode.GetWallet(initialization.ValidatorWalletName) // --------------- Module init checks ---------------- // // The module should be enabled by default. @@ -218,10 +210,10 @@ func (s *IntegrationTestSuite) ProtoRev() { s.Require().Error(err) // The module should have pool weights by default. - poolWeights, err := chainANode.QueryProtoRevPoolWeights() - s.T().Logf("checking that the protorev module has pool weights on init: %v", poolWeights) + info, err := chainANode.QueryProtoRevInfoByPoolType() + s.T().Logf("checking that the protorev module has pool info on init: %v", info) s.Require().NoError(err) - s.Require().NotNil(poolWeights) + s.Require().NotNil(info) // The module should have max pool points per tx by default. maxPoolPointsPerTx, err := chainANode.QueryProtoRevMaxPoolPointsPerTx() @@ -252,9 +244,9 @@ func (s *IntegrationTestSuite) ProtoRev() { chainA.WaitForNumEpochs(1, epochIdentifier) // Create a wallet to use for the swap txs. - swapWalletAddr := chainANode.CreateWallet(walletName) + swapWalletAddr := chainANode.CreateWallet(walletName, chainA) coinIn := fmt.Sprintf("%s%s", amount, denomIn) - chainANode.BankSend(coinIn, chainA.NodeConfigs[0].PublicAddress, swapWalletAddr) + chainANode.BankSend(coinIn, sender, swapWalletAddr) // Check supplies before swap. supplyBefore, err := chainANode.QuerySupply() @@ -299,7 +291,6 @@ func (s *IntegrationTestSuite) ProtoRev() { } func (s *IntegrationTestSuite) ConcentratedLiquidity() { - chainA, chainANode := s.getChainACfgs() var ( denom0 = "uion" denom1 = "uosmo" @@ -308,27 +299,38 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { spreadFactorDec = sdk.MustNewDecFromStr("0.001") ) + chainAB, chainABNode, err := s.getChainCfgs() + s.Require().NoError(err) + // Get the permisionless pool creation parameter. - isPermisionlessCreationEnabledStr := chainANode.QueryParams(cltypes.ModuleName, string(cltypes.KeyIsPermisionlessPoolCreationEnabled)) + isPermisionlessCreationEnabledStr := chainABNode.QueryParams(cltypes.ModuleName, string(cltypes.KeyIsPermisionlessPoolCreationEnabled)) if !strings.EqualFold(isPermisionlessCreationEnabledStr, "false") { s.T().Fatal("concentrated liquidity pool creation is enabled when should not have been") } // Change the parameter to enable permisionless pool creation. - err := chainANode.ParamChangeProposal("concentratedliquidity", string(cltypes.KeyIsPermisionlessPoolCreationEnabled), []byte("true"), chainA) + err = chainABNode.ParamChangeProposal("concentratedliquidity", string(cltypes.KeyIsPermisionlessPoolCreationEnabled), []byte("true"), chainAB) s.Require().NoError(err) + // Update the protorev admin address to a known wallet we can control + + adminWalletAddr := chainABNode.CreateWalletAndFund("admin", []string{"4000000uosmo"}, chainAB) + err = chainABNode.ParamChangeProposal("protorev", string(protorevtypes.ParamStoreKeyAdminAccount), []byte(fmt.Sprintf(`"%s"`, adminWalletAddr)), chainAB) + s.Require().NoError(err) + + // Update the weight of CL pools so that this test case is not back run by protorev. + chainABNode.SetMaxPoolPointsPerTx(7, adminWalletAddr) + // Confirm that the parameter has been changed. - isPermisionlessCreationEnabledStr = chainANode.QueryParams(cltypes.ModuleName, string(cltypes.KeyIsPermisionlessPoolCreationEnabled)) + isPermisionlessCreationEnabledStr = chainABNode.QueryParams(cltypes.ModuleName, string(cltypes.KeyIsPermisionlessPoolCreationEnabled)) if !strings.EqualFold(isPermisionlessCreationEnabledStr, "true") { s.T().Fatal("concentrated liquidity pool creation is not enabled") } // Create concentrated liquidity pool when permisionless pool creation is enabled. - poolID, err := chainANode.CreateConcentratedPool(initialization.ValidatorWalletName, denom0, denom1, tickSpacing, spreadFactor) - s.Require().NoError(err) + poolID := chainABNode.CreateConcentratedPool(initialization.ValidatorWalletName, denom0, denom1, tickSpacing, spreadFactor) - concentratedPool := s.updatedConcentratedPool(chainANode, poolID) + concentratedPool := s.updatedConcentratedPool(chainABNode, poolID) // Sanity check that pool initialized with valid parameters (the ones that we haven't explicitly specified) s.Require().Equal(concentratedPool.GetCurrentTick(), int64(0)) @@ -346,27 +348,27 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { fundTokens := []string{"100000000uosmo", "100000000uion", "100000000stake"} // Get 3 addresses to create positions - address1 := chainANode.CreateWalletAndFund("addr1", fundTokens) - address2 := chainANode.CreateWalletAndFund("addr2", fundTokens) - address3 := chainANode.CreateWalletAndFund("addr3", fundTokens) + address1 := chainABNode.CreateWalletAndFund("addr1", fundTokens, chainAB) + address2 := chainABNode.CreateWalletAndFund("addr2", fundTokens, chainAB) + address3 := chainABNode.CreateWalletAndFund("addr3", fundTokens, chainAB) // Create 2 positions for address1: overlap together, overlap with 2 address3 positions - chainANode.CreateConcentratedPosition(address1, "[-120000]", "40000", fmt.Sprintf("10000000%s,10000000%s", denom0, denom1), 0, 0, poolID) - chainANode.CreateConcentratedPosition(address1, "[-40000]", "120000", fmt.Sprintf("10000000%s,10000000%s", denom0, denom1), 0, 0, poolID) + chainABNode.CreateConcentratedPosition(address1, "[-120000]", "40000", fmt.Sprintf("10000000%s,10000000%s", denom0, denom1), 0, 0, poolID) + chainABNode.CreateConcentratedPosition(address1, "[-40000]", "120000", fmt.Sprintf("10000000%s,10000000%s", denom0, denom1), 0, 0, poolID) // Create 1 position for address2: does not overlap with anything, ends at maximum - chainANode.CreateConcentratedPosition(address2, "220000", fmt.Sprintf("%d", cltypes.MaxTick), fmt.Sprintf("10000000%s,10000000%s", denom0, denom1), 0, 0, poolID) + chainABNode.CreateConcentratedPosition(address2, "220000", fmt.Sprintf("%d", cltypes.MaxTick), fmt.Sprintf("10000000%s,10000000%s", denom0, denom1), 0, 0, poolID) // Create 2 positions for address3: overlap together, overlap with 2 address1 positions, one position starts from minimum - chainANode.CreateConcentratedPosition(address3, "[-160000]", "[-20000]", fmt.Sprintf("10000000%s,10000000%s", denom0, denom1), 0, 0, poolID) - chainANode.CreateConcentratedPosition(address3, fmt.Sprintf("[%d]", cltypes.MinInitializedTick), "140000", fmt.Sprintf("10000000%s,10000000%s", denom0, denom1), 0, 0, poolID) + chainABNode.CreateConcentratedPosition(address3, "[-160000]", "[-20000]", fmt.Sprintf("10000000%s,10000000%s", denom0, denom1), 0, 0, poolID) + chainABNode.CreateConcentratedPosition(address3, fmt.Sprintf("[%d]", cltypes.MinInitializedTick), "140000", fmt.Sprintf("10000000%s,10000000%s", denom0, denom1), 0, 0, poolID) // Get newly created positions - positionsAddress1 := chainANode.QueryConcentratedPositions(address1) - positionsAddress2 := chainANode.QueryConcentratedPositions(address2) - positionsAddress3 := chainANode.QueryConcentratedPositions(address3) + positionsAddress1 := chainABNode.QueryConcentratedPositions(address1) + positionsAddress2 := chainABNode.QueryConcentratedPositions(address2) + positionsAddress3 := chainABNode.QueryConcentratedPositions(address3) - concentratedPool = s.updatedConcentratedPool(chainANode, poolID) + concentratedPool = s.updatedConcentratedPool(chainABNode, poolID) // Assert number of positions per address s.Require().Equal(len(positionsAddress1), 2) @@ -412,7 +414,7 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { uosmoIn_Swap1 = fmt.Sprintf("%suosmo", uosmoInDec_Swap1.SDKDec().String()) ) // Perform swap (not crossing initialized ticks) - chainANode.SwapExactAmountIn(uosmoIn_Swap1, outMinAmt, fmt.Sprintf("%d", poolID), denom0, initialization.ValidatorWalletName) + chainABNode.SwapExactAmountIn(uosmoIn_Swap1, outMinAmt, fmt.Sprintf("%d", poolID), denom0, initialization.ValidatorWalletName) // Calculate and track global spread reward growth for swap 1 spreadRewardGrowthGlobal.AddMut(calculateSpreadRewardGrowthGlobal(uosmoInDec_Swap1.SDKDec(), spreadFactorDec, concentratedPool.GetLiquidity())) @@ -420,7 +422,7 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { liquidityBeforeSwap := concentratedPool.GetLiquidity() sqrtPriceBeforeSwap := concentratedPool.GetCurrentSqrtPrice() - concentratedPool = s.updatedConcentratedPool(chainANode, poolID) + concentratedPool = s.updatedConcentratedPool(chainABNode, poolID) liquidityAfterSwap := concentratedPool.GetLiquidity() sqrtPriceAfterSwap := concentratedPool.GetCurrentSqrtPrice() @@ -438,9 +440,9 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { // Collect SpreadRewards: Swap 1 // Track balances for address1 position1 - addr1BalancesBefore := s.addrBalance(chainANode, address1) - chainANode.CollectSpreadRewards(address1, fmt.Sprint(positionsAddress1[0].Position.PositionId)) - addr1BalancesAfter := s.addrBalance(chainANode, address1) + addr1BalancesBefore := s.addrBalance(chainABNode, address1) + chainABNode.CollectSpreadRewards(address1, fmt.Sprint(positionsAddress1[0].Position.PositionId)) + addr1BalancesAfter := s.addrBalance(chainABNode, address1) // Assert that the balance changed only for tokenIn (uosmo) s.assertBalancesInvariants(addr1BalancesBefore, addr1BalancesAfter, false, true) @@ -484,8 +486,8 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { s.Require().NoError(err) _, sqrtPriceAtNextInitializedTick, err := clmath.TickToSqrtPrice(nextInitTick) s.Require().NoError(err) - sqrtPriceAfterNextInitializedTickBigDec := osmomath.BigDecFromSDKDec(sqrtPriceAfterNextInitializedTick) - sqrtPriceAtNextInitializedTickBigDec := osmomath.BigDecFromSDKDec(sqrtPriceAtNextInitializedTick) + sqrtPriceAfterNextInitializedTickBigDec := sqrtPriceAfterNextInitializedTick + sqrtPriceAtNextInitializedTickBigDec := sqrtPriceAtNextInitializedTick // Calculate Δ(sqrtPrice): // deltaSqrtPriceAfterNextInitializedTick = ΔsqrtP(40300) - ΔsqrtP(40000) @@ -512,13 +514,13 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { spreadRewardGrowthGlobal_Swap1 = spreadRewardGrowthGlobal.Clone() ) // Perform a swap - chainANode.SwapExactAmountIn(uosmoIn_Swap2, outMinAmt, fmt.Sprintf("%d", poolID), denom0, initialization.ValidatorWalletName) + chainABNode.SwapExactAmountIn(uosmoIn_Swap2, outMinAmt, fmt.Sprintf("%d", poolID), denom0, initialization.ValidatorWalletName) // Calculate the amount of liquidity of the position that was kicked out during swap (address1 position1) liquidityOfKickedOutPosition := positionsAddress1[0].Position.Liquidity // Update pool and track pool's liquidity - concentratedPool = s.updatedConcentratedPool(chainANode, poolID) + concentratedPool = s.updatedConcentratedPool(chainABNode, poolID) liquidityAfterSwap = concentratedPool.GetLiquidity() @@ -551,9 +553,9 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { // Assert that address1 position1 earned spread rewards only from first swap step // Track balances for address1 position1 - addr1BalancesBefore = s.addrBalance(chainANode, address1) - chainANode.CollectSpreadRewards(address1, fmt.Sprint(positionsAddress1[0].Position.PositionId)) - addr1BalancesAfter = s.addrBalance(chainANode, address1) + addr1BalancesBefore = s.addrBalance(chainABNode, address1) + chainABNode.CollectSpreadRewards(address1, fmt.Sprint(positionsAddress1[0].Position.PositionId)) + addr1BalancesAfter = s.addrBalance(chainABNode, address1) // Assert that the balance changed only for tokenIn (uosmo) s.assertBalancesInvariants(addr1BalancesBefore, addr1BalancesAfter, false, true) @@ -576,9 +578,9 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { // Assert that address3 position2 earned rewards from first and second swaps // Track balance off address3 position2: check that position that has not been kicked out earned full rewards - addr3BalancesBefore := s.addrBalance(chainANode, address3) - chainANode.CollectSpreadRewards(address3, fmt.Sprint(positionsAddress3[1].Position.PositionId)) - addr3BalancesAfter := s.addrBalance(chainANode, address3) + addr3BalancesBefore := s.addrBalance(chainABNode, address3) + chainABNode.CollectSpreadRewards(address3, fmt.Sprint(positionsAddress3[1].Position.PositionId)) + addr3BalancesAfter := s.addrBalance(chainABNode, address3) // Calculate uncollected spread rewards for address3 position2 earned from Swap 1 spreadRewardsUncollectedAddress3Position2_Swap1 := calculateUncollectedSpreadRewards( @@ -628,7 +630,7 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { s.Require().NoError(err) _, sqrtPriceAtNextInitializedTick, err = clmath.TickToSqrtPrice(nextInitTick) s.Require().NoError(err) - sqrtPriceAtNextInitializedTickBigDec = osmomath.BigDecFromSDKDec(sqrtPriceAtNextInitializedTick) + sqrtPriceAtNextInitializedTickBigDec = sqrtPriceAtNextInitializedTick // Calculate numerators numeratorBelowNextInitializedTick := sqrtPriceAtNextInitializedTick.Sub(sqrtPricebBelowNextInitializedTick) @@ -643,7 +645,7 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { fractionAtNextInitializedTick := numeratorNextInitializedTick.Quo(denominatorNextInitializedTick) // Calculate amounts of uionIn needed - amountInToGetToTickBelowInitialized := liquidityBeforeSwap.Add(positionsAddress1[0].Position.Liquidity).Mul(fractionBelowNextInitializedTick) + amountInToGetToTickBelowInitialized := liquidityBeforeSwap.Add(positionsAddress1[0].Position.Liquidity).Mul(fractionBelowNextInitializedTick.SDKDec()) amountInToGetToNextInitTick = liquidityBeforeSwap.Mul(fractionAtNextInitializedTick.SDKDec()) var ( @@ -657,21 +659,21 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { spreadRewardGrowthInsideAddress1Position1Last = spreadRewardGrowthGlobal_Swap1.Add(spreadRewardCharge_Swap2_Step1) ) // Collect spread rewards for address1 position1 to avoid overhead computations (swap2 already asserted spread rewards are aggregated correctly from multiple swaps) - chainANode.CollectSpreadRewards(address1, fmt.Sprint(positionsAddress1[0].Position.PositionId)) + chainABNode.CollectSpreadRewards(address1, fmt.Sprint(positionsAddress1[0].Position.PositionId)) // Perform a swap - chainANode.SwapExactAmountIn(uionIn_Swap3, outMinAmt, fmt.Sprintf("%d", poolID), denom1, initialization.ValidatorWalletName) + chainABNode.SwapExactAmountIn(uionIn_Swap3, outMinAmt, fmt.Sprintf("%d", poolID), denom1, initialization.ValidatorWalletName) // Assert liquidity of kicked in position was successfully added to the pool - concentratedPool = s.updatedConcentratedPool(chainANode, poolID) + concentratedPool = s.updatedConcentratedPool(chainABNode, poolID) liquidityAfterSwap = concentratedPool.GetLiquidity() s.Require().Equal(liquidityBeforeSwap.Add(positionsAddress1[0].Position.Liquidity), liquidityAfterSwap) // Track balance of address1 - addr1BalancesBefore = s.addrBalance(chainANode, address1) - chainANode.CollectSpreadRewards(address1, fmt.Sprint(positionsAddress1[0].Position.PositionId)) - addr1BalancesAfter = s.addrBalance(chainANode, address1) + addr1BalancesBefore = s.addrBalance(chainABNode, address1) + chainABNode.CollectSpreadRewards(address1, fmt.Sprint(positionsAddress1[0].Position.PositionId)) + addr1BalancesAfter = s.addrBalance(chainABNode, address1) // Assert that the balance changed only for tokenIn (uion) s.assertBalancesInvariants(addr1BalancesBefore, addr1BalancesAfter, true, false) @@ -715,9 +717,9 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { // Assert position that was active throughout the whole swap: // Track balance of address3 - addr3BalancesBefore = s.addrBalance(chainANode, address3) - chainANode.CollectSpreadRewards(address3, fmt.Sprint(positionsAddress3[1].Position.PositionId)) - addr3BalancesAfter = s.addrBalance(chainANode, address3) + addr3BalancesBefore = s.addrBalance(chainABNode, address3) + chainABNode.CollectSpreadRewards(address3, fmt.Sprint(positionsAddress3[1].Position.PositionId)) + addr3BalancesAfter = s.addrBalance(chainABNode, address3) // Assert that the balance changed only for tokenIn (uion) s.assertBalancesInvariants(addr3BalancesBefore, addr3BalancesAfter, true, false) @@ -756,17 +758,17 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { // Assert that positions, which were not included in swaps, were not affected // Address3 Position1: [-160000; -20000] - addr3BalancesBefore = s.addrBalance(chainANode, address3) - chainANode.CollectSpreadRewards(address3, fmt.Sprint(positionsAddress3[0].Position.PositionId)) - addr3BalancesAfter = s.addrBalance(chainANode, address3) + addr3BalancesBefore = s.addrBalance(chainABNode, address3) + chainABNode.CollectSpreadRewards(address3, fmt.Sprint(positionsAddress3[0].Position.PositionId)) + addr3BalancesAfter = s.addrBalance(chainABNode, address3) // Assert that balances did not change for any token s.assertBalancesInvariants(addr3BalancesBefore, addr3BalancesAfter, true, true) // Address2's only position: [220000; 342000] - addr2BalancesBefore := s.addrBalance(chainANode, address2) - chainANode.CollectSpreadRewards(address2, fmt.Sprint(positionsAddress2[0].Position.PositionId)) - addr2BalancesAfter := s.addrBalance(chainANode, address2) + addr2BalancesBefore := s.addrBalance(chainABNode, address2) + chainABNode.CollectSpreadRewards(address2, fmt.Sprint(positionsAddress2[0].Position.PositionId)) + addr2BalancesAfter := s.addrBalance(chainABNode, address2) // Assert the balances did not change for every token s.assertBalancesInvariants(addr2BalancesBefore, addr2BalancesAfter, true, true) @@ -776,41 +778,41 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { // Withdraw Position parameters defaultLiquidityRemoval := "1000" - chainA.WaitForNumHeights(2) + chainAB.WaitForNumHeights(2) // Assert removing some liquidity // address1: check removing some amount of liquidity address1position1liquidityBefore := positionsAddress1[0].Position.Liquidity - chainANode.WithdrawPosition(address1, defaultLiquidityRemoval, positionsAddress1[0].Position.PositionId) + chainABNode.WithdrawPosition(address1, defaultLiquidityRemoval, positionsAddress1[0].Position.PositionId) // assert - positionsAddress1 = chainANode.QueryConcentratedPositions(address1) + positionsAddress1 = chainABNode.QueryConcentratedPositions(address1) s.Require().Equal(address1position1liquidityBefore, positionsAddress1[0].Position.Liquidity.Add(sdk.MustNewDecFromStr(defaultLiquidityRemoval))) // address2: check removing some amount of liquidity address2position1liquidityBefore := positionsAddress2[0].Position.Liquidity - chainANode.WithdrawPosition(address2, defaultLiquidityRemoval, positionsAddress2[0].Position.PositionId) + chainABNode.WithdrawPosition(address2, defaultLiquidityRemoval, positionsAddress2[0].Position.PositionId) // assert - positionsAddress2 = chainANode.QueryConcentratedPositions(address2) + positionsAddress2 = chainABNode.QueryConcentratedPositions(address2) s.Require().Equal(address2position1liquidityBefore, positionsAddress2[0].Position.Liquidity.Add(sdk.MustNewDecFromStr(defaultLiquidityRemoval))) // address3: check removing some amount of liquidity address3position1liquidityBefore := positionsAddress3[0].Position.Liquidity - chainANode.WithdrawPosition(address3, defaultLiquidityRemoval, positionsAddress3[0].Position.PositionId) + chainABNode.WithdrawPosition(address3, defaultLiquidityRemoval, positionsAddress3[0].Position.PositionId) // assert - positionsAddress3 = chainANode.QueryConcentratedPositions(address3) + positionsAddress3 = chainABNode.QueryConcentratedPositions(address3) s.Require().Equal(address3position1liquidityBefore, positionsAddress3[0].Position.Liquidity.Add(sdk.MustNewDecFromStr(defaultLiquidityRemoval))) // Assert removing all liquidity // address2: no more positions left allLiquidityAddress2Position1 := positionsAddress2[0].Position.Liquidity - chainANode.WithdrawPosition(address2, allLiquidityAddress2Position1.String(), positionsAddress2[0].Position.PositionId) - positionsAddress2 = chainANode.QueryConcentratedPositions(address2) + chainABNode.WithdrawPosition(address2, allLiquidityAddress2Position1.String(), positionsAddress2[0].Position.PositionId) + positionsAddress2 = chainABNode.QueryConcentratedPositions(address2) s.Require().Empty(positionsAddress2) // address1: one position left allLiquidityAddress1Position1 := positionsAddress1[0].Position.Liquidity - chainANode.WithdrawPosition(address1, allLiquidityAddress1Position1.String(), positionsAddress1[0].Position.PositionId) - positionsAddress1 = chainANode.QueryConcentratedPositions(address1) + chainABNode.WithdrawPosition(address1, allLiquidityAddress1Position1.String(), positionsAddress1[0].Position.PositionId) + positionsAddress1 = chainABNode.QueryConcentratedPositions(address1) s.Require().Equal(len(positionsAddress1), 1) // Test tick spacing reduction proposal @@ -831,22 +833,19 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { newTickSpacing := cltypes.AuthorizedTickSpacing[indexOfCurrentTickSpacing-1] // Run the tick spacing reduction proposal - chainANode.SubmitTickSpacingReductionProposal(fmt.Sprintf("%d,%d", poolID, newTickSpacing), sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(config.InitialMinExpeditedDeposit)), true) - // TODO: We should remove every instance of prop number inc and just parse from tx response - chainA.LatestProposalNumber += 1 - latestPropNumber := chainA.LatestProposalNumber + propNumber := chainABNode.SubmitTickSpacingReductionProposal(fmt.Sprintf("%d,%d", poolID, newTickSpacing), sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(config.InitialMinExpeditedDeposit)), true) - chainANode.DepositProposal(latestPropNumber, true) + chainABNode.DepositProposal(propNumber, true) totalTimeChan := make(chan time.Duration, 1) - go chainANode.QueryPropStatusTimed(latestPropNumber, "PROPOSAL_STATUS_PASSED", totalTimeChan) + go chainABNode.QueryPropStatusTimed(propNumber, "PROPOSAL_STATUS_PASSED", totalTimeChan) var wg sync.WaitGroup // TODO: create a helper function for all these go routine yes vote calls. - for _, n := range chainA.NodeConfigs { + for _, n := range chainAB.NodeConfigs { wg.Add(1) go func(nodeConfig *chain.NodeConfig) { defer wg.Done() - nodeConfig.VoteYesProposal(initialization.ValidatorWalletName, latestPropNumber) + nodeConfig.VoteYesProposal(initialization.ValidatorWalletName, propNumber) }(n) } @@ -863,8 +862,11 @@ func (s *IntegrationTestSuite) ConcentratedLiquidity() { } // Check that the tick spacing was reduced to the expected new tick spacing - concentratedPool = s.updatedConcentratedPool(chainANode, poolID) + concentratedPool = s.updatedConcentratedPool(chainABNode, poolID) s.Require().Equal(newTickSpacing, concentratedPool.GetTickSpacing()) + + // Reset the maximum number of pool points + chainABNode.SetMaxPoolPointsPerTx(int(protorevtypes.DefaultMaxPoolPointsPerTx), adminWalletAddr) } func (s *IntegrationTestSuite) StableSwapPostUpgrade() { @@ -872,7 +874,12 @@ func (s *IntegrationTestSuite) StableSwapPostUpgrade() { s.T().Skip("Skipping StableSwapPostUpgrade test") } - chainA, chainANode := s.getChainACfgs() + chainAB, chainABNode, err := s.getChainCfgs() + s.Require().NoError(err) + + index := s.getChainIndex(chainAB) + + sender := chainABNode.GetWallet(initialization.ValidatorWalletName) const ( denomA = "stake" @@ -883,13 +890,13 @@ func (s *IntegrationTestSuite) StableSwapPostUpgrade() { coinAIn, coinBIn := fmt.Sprintf("20000%s", denomA), fmt.Sprintf("1%s", denomB) - chainANode.BankSend(initialization.WalletFeeTokens.String(), chainA.NodeConfigs[0].PublicAddress, config.StableswapWallet) - chainANode.BankSend(coinAIn, chainA.NodeConfigs[0].PublicAddress, config.StableswapWallet) - chainANode.BankSend(coinBIn, chainA.NodeConfigs[0].PublicAddress, config.StableswapWallet) + chainABNode.BankSend(initialization.WalletFeeTokens.String(), sender, config.StableswapWallet[index]) + chainABNode.BankSend(coinAIn, sender, config.StableswapWallet[index]) + chainABNode.BankSend(coinBIn, sender, config.StableswapWallet[index]) s.T().Log("performing swaps") - chainANode.SwapExactAmountIn(coinAIn, minAmountOut, fmt.Sprintf("%d", config.PreUpgradeStableSwapPoolId), denomB, config.StableswapWallet) - chainANode.SwapExactAmountIn(coinBIn, minAmountOut, fmt.Sprintf("%d", config.PreUpgradeStableSwapPoolId), denomA, config.StableswapWallet) + chainABNode.SwapExactAmountIn(coinAIn, minAmountOut, fmt.Sprintf("%d", config.PreUpgradeStableSwapPoolId[index]), denomB, config.StableswapWallet[index]) + chainABNode.SwapExactAmountIn(coinBIn, minAmountOut, fmt.Sprintf("%d", config.PreUpgradeStableSwapPoolId[index]), denomA, config.StableswapWallet[index]) } // TestGeometricTwapMigration tests that the geometric twap record @@ -905,24 +912,29 @@ func (s *IntegrationTestSuite) GeometricTwapMigration() { s.T().Skip("Skipping upgrade tests") } - const ( + var ( // Configurations for tests/e2e/scripts/pool1A.json // This pool gets initialized pre-upgrade. minAmountOut = "1" - otherDenom = "ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518" + otherDenom = []string{"ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518", "ibc/C053D637CCA2A2BA030E2C5EE1B28A16F71CCB0E45E8BE52766DC1B241B77878"} migrationWallet = "migration" ) - chainA, chainANode := s.getChainACfgs() + chainAB, chainABNode, err := s.getChainCfgs() + s.Require().NoError(err) + + index := s.getChainIndex(chainAB) + + sender := chainABNode.GetWallet(initialization.ValidatorWalletName) uosmoIn := fmt.Sprintf("1000000%s", "uosmo") - swapWalletAddr := chainANode.CreateWallet(migrationWallet) + swapWalletAddr := chainABNode.CreateWallet(migrationWallet, chainAB) - chainANode.BankSend(uosmoIn, chainA.NodeConfigs[0].PublicAddress, swapWalletAddr) + chainABNode.BankSend(uosmoIn, sender, swapWalletAddr) // Swap to create new twap records on the pool that was created pre-upgrade. - chainANode.SwapExactAmountIn(uosmoIn, minAmountOut, fmt.Sprintf("%d", config.PreUpgradePoolId), otherDenom, swapWalletAddr) + chainABNode.SwapExactAmountIn(uosmoIn, minAmountOut, fmt.Sprintf("%d", config.PreUpgradePoolId[index]), otherDenom[index], swapWalletAddr) } // TestIBCTokenTransfer tests that IBC token transfers work as expected. @@ -931,17 +943,15 @@ func (s *IntegrationTestSuite) IBCTokenTransferAndCreatePool() { if s.skipIBC { s.T().Skip("Skipping IBC tests") } - chainA := s.configurer.GetChainConfig(0) - chainANode, err := chainA.GetNodeAtIndex(1) + chainA, chainANode, err := s.getChainACfgs() s.Require().NoError(err) - chainB := s.configurer.GetChainConfig(1) - chainBNode, err := chainB.GetNodeAtIndex(1) + chainB, chainBNode, err := s.getChainBCfgs() s.Require().NoError(err) - chainANode.SendIBC(chainB, chainB.NodeConfigs[1].PublicAddress, initialization.OsmoToken) - chainBNode.SendIBC(chainA, chainA.NodeConfigs[1].PublicAddress, initialization.OsmoToken) - chainANode.SendIBC(chainB, chainB.NodeConfigs[1].PublicAddress, initialization.StakeToken) - chainBNode.SendIBC(chainA, chainA.NodeConfigs[1].PublicAddress, initialization.StakeToken) + chainANode.SendIBC(chainA, chainB, chainBNode.PublicAddress, initialization.OsmoToken) + chainBNode.SendIBC(chainB, chainA, chainANode.PublicAddress, initialization.OsmoToken) + chainANode.SendIBC(chainA, chainB, chainBNode.PublicAddress, initialization.StakeToken) + chainBNode.SendIBC(chainB, chainA, chainANode.PublicAddress, initialization.StakeToken) chainANode.CreateBalancerPool("ibcDenomPool.json", initialization.ValidatorWalletName) } @@ -954,46 +964,42 @@ func (s *IntegrationTestSuite) IBCTokenTransferAndCreatePool() { // - voting no on the proposal from the delegator wallet // - ensuring that delegator's wallet overwrites the validator's vote func (s *IntegrationTestSuite) SuperfluidVoting() { - chainA := s.configurer.GetChainConfig(0) - chainANode, err := chainA.GetNodeAtIndex(2) + chainAB, chainABNode, err := s.getChainCfgs() s.Require().NoError(err) - poolId := chainANode.CreateBalancerPool("nativeDenomPool.json", chainA.NodeConfigs[2].PublicAddress) + poolId := chainABNode.CreateBalancerPool("nativeDenomPool.json", initialization.ValidatorWalletName) // enable superfluid assets - chainANode.EnableSuperfluidAsset(chainA, fmt.Sprintf("gamm/pool/%d", poolId)) + chainABNode.EnableSuperfluidAsset(chainAB, fmt.Sprintf("gamm/pool/%d", poolId)) // setup wallets and send gamm tokens to these wallets (both chains) - superfluidVotingWallet := chainANode.CreateWallet("TestSuperfluidVoting") - chainANode.BankSend(fmt.Sprintf("10000000000000000000gamm/pool/%d", poolId), chainA.NodeConfigs[2].PublicAddress, superfluidVotingWallet) - lockId := chainANode.LockTokens(fmt.Sprintf("%v%s", sdk.NewInt(1000000000000000000), fmt.Sprintf("gamm/pool/%d", poolId)), "240s", superfluidVotingWallet) - chainA.LatestLockNumber += 1 - chainANode.SuperfluidDelegate(lockId, chainA.NodeConfigs[2].OperatorAddress, superfluidVotingWallet) + superfluidVotingWallet := chainABNode.CreateWallet("TestSuperfluidVoting", chainAB) + chainABNode.BankSend(fmt.Sprintf("10000000000000000000gamm/pool/%d", poolId), initialization.ValidatorWalletName, superfluidVotingWallet) + lockId := chainABNode.LockTokens(fmt.Sprintf("%v%s", sdk.NewInt(1000000000000000000), fmt.Sprintf("gamm/pool/%d", poolId)), "240s", superfluidVotingWallet) + chainABNode.SuperfluidDelegate(lockId, chainABNode.OperatorAddress, superfluidVotingWallet) // create a text prop, deposit and vote yes - chainANode.SubmitTextProposal("superfluid vote overwrite test", sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(config.InitialMinDeposit)), false) - chainA.LatestProposalNumber += 1 + propNumber := chainABNode.SubmitTextProposal("superfluid vote overwrite test", sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(config.InitialMinDeposit)), false) + chainABNode.DepositProposal(propNumber, false) - chainANode.DepositProposal(chainA.LatestProposalNumber, false) - latestPropNumber := chainA.LatestProposalNumber var wg sync.WaitGroup - for _, n := range chainA.NodeConfigs { + for _, n := range chainAB.NodeConfigs { wg.Add(1) go func(nodeConfig *chain.NodeConfig) { defer wg.Done() - nodeConfig.VoteYesProposal(initialization.ValidatorWalletName, latestPropNumber) + nodeConfig.VoteYesProposal(initialization.ValidatorWalletName, propNumber) }(n) } wg.Wait() // set delegator vote to no - chainANode.VoteNoProposal(superfluidVotingWallet, latestPropNumber) + chainABNode.VoteNoProposal(superfluidVotingWallet, propNumber) s.Eventually( func() bool { - noTotal, yesTotal, noWithVetoTotal, abstainTotal, err := chainANode.QueryPropTally(latestPropNumber) + noTotal, yesTotal, noWithVetoTotal, abstainTotal, err := chainABNode.QueryPropTally(propNumber) if err != nil { return false } @@ -1006,13 +1012,13 @@ func (s *IntegrationTestSuite) SuperfluidVoting() { 10*time.Millisecond, "Osmosis node failed to retrieve prop tally", ) - noTotal, _, _, _, _ := chainANode.QueryPropTally(latestPropNumber) + noTotal, _, _, _, _ := chainABNode.QueryPropTally(propNumber) noTotalFinal, err := strconv.Atoi(noTotal.String()) s.NoError(err) s.Eventually( func() bool { - intAccountBalance, err := chainANode.QueryIntermediaryAccount(fmt.Sprintf("gamm/pool/%d", poolId), chainA.NodeConfigs[2].OperatorAddress) + intAccountBalance, err := chainABNode.QueryIntermediaryAccount(fmt.Sprintf("gamm/pool/%d", poolId), chainABNode.OperatorAddress) s.Require().NoError(err) if err != nil { return false @@ -1030,9 +1036,10 @@ func (s *IntegrationTestSuite) SuperfluidVoting() { } func (s *IntegrationTestSuite) CreateConcentratedLiquidityPoolVoting_And_TWAP() { - chainA, chainANode := s.getChainACfgs() + chainAB, chainABNode, err := s.getChainCfgs() + s.Require().NoError(err) - poolId, err := chainA.SubmitCreateConcentratedPoolProposal() + poolId, err := chainAB.SubmitCreateConcentratedPoolProposal(chainABNode) s.NoError(err) fmt.Println("poolId", poolId) @@ -1046,7 +1053,7 @@ func (s *IntegrationTestSuite) CreateConcentratedLiquidityPoolVoting_And_TWAP() var concentratedPool cltypes.ConcentratedPoolExtension s.Eventually( func() bool { - concentratedPool = s.updatedConcentratedPool(chainANode, poolId) + concentratedPool = s.updatedConcentratedPool(chainABNode, poolId) s.Require().Equal(poolmanagertypes.Concentrated, concentratedPool.GetType()) s.Require().Equal(expectedDenom0, concentratedPool.GetToken0()) s.Require().Equal(expectedDenom1, concentratedPool.GetToken1()) @@ -1063,45 +1070,45 @@ func (s *IntegrationTestSuite) CreateConcentratedLiquidityPoolVoting_And_TWAP() fundTokens := []string{"100000000stake", "100000000uosmo"} // Get address to create positions - address1 := chainANode.CreateWalletAndFund("address1", fundTokens) + address1 := chainABNode.CreateWalletAndFund("address1", fundTokens, chainAB) // We add 5 ms to avoid landing directly on block time in twap. If block time // is provided as start time, the latest spot price is used. Otherwise // interpolation is done. - timeBeforePositionCreationBeforeSwap := chainANode.QueryLatestBlockTime().Add(5 * time.Millisecond) + timeBeforePositionCreationBeforeSwap := chainABNode.QueryLatestBlockTime().Add(5 * time.Millisecond) s.T().Log("geometric twap, start time ", timeBeforePositionCreationBeforeSwap.Unix()) // Wait for the next height so that the requested twap // start time (timeBeforePositionCreationBeforeSwap) is not equal to the block time. - chainA.WaitForNumHeights(1) + chainAB.WaitForNumHeights(1) // Check initial TWAP // We expect this to error since there is no spot price yet. s.T().Log("initial twap check") - initialTwapBOverA, err := chainANode.QueryGeometricTwapToNow(concentratedPool.GetId(), concentratedPool.GetToken1(), concentratedPool.GetToken0(), timeBeforePositionCreationBeforeSwap) + initialTwapBOverA, err := chainABNode.QueryGeometricTwapToNow(concentratedPool.GetId(), concentratedPool.GetToken1(), concentratedPool.GetToken0(), timeBeforePositionCreationBeforeSwap) s.Require().Error(err) s.Require().Equal(sdk.Dec{}, initialTwapBOverA) // Create a position and check that TWAP now returns a value. s.T().Log("creating first position") - chainANode.CreateConcentratedPosition(address1, "[-120000]", "40000", fmt.Sprintf("10000000%s,20000000%s", concentratedPool.GetToken0(), concentratedPool.GetToken1()), 0, 0, concentratedPool.GetId()) - timeAfterPositionCreationBeforeSwap := chainANode.QueryLatestBlockTime() - chainA.WaitForNumHeights(2) - firstPositionTwapBOverA, err := chainANode.QueryGeometricTwapToNow(concentratedPool.GetId(), concentratedPool.GetToken1(), concentratedPool.GetToken0(), timeAfterPositionCreationBeforeSwap) + chainABNode.CreateConcentratedPosition(address1, "[-120000]", "40000", fmt.Sprintf("10000000%s,20000000%s", concentratedPool.GetToken0(), concentratedPool.GetToken1()), 0, 0, concentratedPool.GetId()) + timeAfterPositionCreationBeforeSwap := chainABNode.QueryLatestBlockTime() + chainAB.WaitForNumHeights(2) + firstPositionTwapBOverA, err := chainABNode.QueryGeometricTwapToNow(concentratedPool.GetId(), concentratedPool.GetToken1(), concentratedPool.GetToken0(), timeAfterPositionCreationBeforeSwap) s.Require().NoError(err) s.Require().Equal(sdk.MustNewDecFromStr("0.5"), firstPositionTwapBOverA) // Run a swap and check that the TWAP updates. s.T().Log("run swap") coinAIn := fmt.Sprintf("1000000%s", concentratedPool.GetToken0()) - chainANode.SwapExactAmountIn(coinAIn, "1", fmt.Sprintf("%d", concentratedPool.GetId()), concentratedPool.GetToken1(), address1) + chainABNode.SwapExactAmountIn(coinAIn, "1", fmt.Sprintf("%d", concentratedPool.GetId()), concentratedPool.GetToken1(), address1) - timeAfterSwap := chainANode.QueryLatestBlockTime() - chainA.WaitForNumHeights(1) - timeAfterSwapPlus1Height := chainANode.QueryLatestBlockTime() + timeAfterSwap := chainABNode.QueryLatestBlockTime() + chainAB.WaitForNumHeights(1) + timeAfterSwapPlus1Height := chainABNode.QueryLatestBlockTime() s.T().Log("querying for the TWAP after swap") - afterSwapTwapBOverA, err := chainANode.QueryGeometricTwap(concentratedPool.GetId(), concentratedPool.GetToken1(), concentratedPool.GetToken0(), timeAfterSwap, timeAfterSwapPlus1Height) + afterSwapTwapBOverA, err := chainABNode.QueryGeometricTwap(concentratedPool.GetId(), concentratedPool.GetToken1(), concentratedPool.GetToken0(), timeAfterSwap, timeAfterSwapPlus1Height) s.Require().NoError(err) // We swap stake so uosmo's supply will decrease and stake will increase. @@ -1110,22 +1117,22 @@ func (s *IntegrationTestSuite) CreateConcentratedLiquidityPoolVoting_And_TWAP() // Remove the position and check that TWAP returns an error. s.T().Log("removing first position (pool is drained)") - positions := chainANode.QueryConcentratedPositions(address1) - chainANode.WithdrawPosition(address1, positions[0].Position.Liquidity.String(), positions[0].Position.PositionId) - chainA.WaitForNumHeights(1) + positions := chainABNode.QueryConcentratedPositions(address1) + chainABNode.WithdrawPosition(address1, positions[0].Position.Liquidity.String(), positions[0].Position.PositionId) + chainAB.WaitForNumHeights(1) s.T().Log("querying for the TWAP from after pool drained") - afterRemoveTwapBOverA, err := chainANode.QueryGeometricTwapToNow(concentratedPool.GetId(), concentratedPool.GetToken1(), concentratedPool.GetToken0(), timeAfterSwapPlus1Height) + afterRemoveTwapBOverA, err := chainABNode.QueryGeometricTwapToNow(concentratedPool.GetId(), concentratedPool.GetToken1(), concentratedPool.GetToken0(), timeAfterSwapPlus1Height) s.Require().Error(err) s.Require().Equal(sdk.Dec{}, afterRemoveTwapBOverA) // Create a position and check that TWAP now returns a value. // Should be equal to 1 since the position contains equal amounts of both tokens. s.T().Log("creating position") - chainANode.CreateConcentratedPosition(address1, "[-120000]", "40000", fmt.Sprintf("10000000%s,10000000%s", concentratedPool.GetToken0(), concentratedPool.GetToken1()), 0, 0, concentratedPool.GetId()) - chainA.WaitForNumHeights(1) - timeAfterSwapRemoveAndCreatePlus1Height := chainANode.QueryLatestBlockTime() - secondTwapBOverA, err := chainANode.QueryGeometricTwapToNow(concentratedPool.GetId(), concentratedPool.GetToken1(), concentratedPool.GetToken0(), timeAfterSwapRemoveAndCreatePlus1Height) + chainABNode.CreateConcentratedPosition(address1, "[-120000]", "40000", fmt.Sprintf("10000000%s,10000000%s", concentratedPool.GetToken0(), concentratedPool.GetToken1()), 0, 0, concentratedPool.GetId()) + chainAB.WaitForNumHeights(1) + timeAfterSwapRemoveAndCreatePlus1Height := chainABNode.QueryLatestBlockTime() + secondTwapBOverA, err := chainABNode.QueryGeometricTwapToNow(concentratedPool.GetId(), concentratedPool.GetToken1(), concentratedPool.GetToken0(), timeAfterSwapRemoveAndCreatePlus1Height) s.Require().NoError(err) s.Require().Equal(sdk.NewDec(1), secondTwapBOverA) } @@ -1134,11 +1141,12 @@ func (s *IntegrationTestSuite) IBCTokenTransferRateLimiting() { if s.skipIBC { s.T().Skip("Skipping IBC tests") } - chainA := s.configurer.GetChainConfig(0) - chainB := s.configurer.GetChainConfig(1) - - chainANode, err := chainA.GetNodeAtIndex(1) + chainA, chainANode, err := s.getChainACfgs() s.Require().NoError(err) + chainB, chainBNode, err := s.getChainBCfgs() + s.Require().NoError(err) + + receiver := chainBNode.GetWallet(initialization.ValidatorWalletName) // If the RL param is already set. Remember it to set it back at the end param := chainANode.QueryParams(ibcratelimittypes.ModuleName, string(ibcratelimittypes.KeyContractAddress)) @@ -1156,9 +1164,9 @@ func (s *IntegrationTestSuite) IBCTokenTransferRateLimiting() { // Sending >1% fmt.Println("Sending >1%") - chainANode.SendIBC(chainB, chainB.NodeConfigs[0].PublicAddress, sdk.NewInt64Coin(initialization.OsmoDenom, int64(over))) + chainANode.SendIBC(chainA, chainB, receiver, sdk.NewInt64Coin(initialization.OsmoDenom, int64(over))) - contract, err := chainANode.SetupRateLimiting(paths, chainA.NodeConfigs[1].PublicAddress, chainA) + contract, err := chainANode.SetupRateLimiting(paths, chainANode.PublicAddress, chainA) s.Require().NoError(err) s.Eventually( @@ -1173,10 +1181,10 @@ func (s *IntegrationTestSuite) IBCTokenTransferRateLimiting() { // Sending <1%. Should work fmt.Println("Sending <1%. Should work") - chainANode.SendIBC(chainB, chainB.NodeConfigs[0].PublicAddress, sdk.NewInt64Coin(initialization.OsmoDenom, 1)) + chainANode.SendIBC(chainA, chainB, receiver, sdk.NewInt64Coin(initialization.OsmoDenom, 1)) // Sending >1%. Should fail fmt.Println("Sending >1%. Should fail") - chainANode.FailIBCTransfer(initialization.ValidatorWalletName, chainB.NodeConfigs[1].PublicAddress, fmt.Sprintf("%duosmo", int(over))) + chainANode.FailIBCTransfer(initialization.ValidatorWalletName, receiver, fmt.Sprintf("%duosmo", int(over))) // Removing the rate limit so it doesn't affect other tests chainANode.WasmExecute(contract, `{"remove_path": {"channel_id": "channel-0", "denom": "uosmo"}}`, initialization.ValidatorWalletName) @@ -1192,25 +1200,24 @@ func (s *IntegrationTestSuite) IBCTokenTransferRateLimiting() { s.Eventually(func() bool { val := chainANode.QueryParams(ibcratelimittypes.ModuleName, string(ibcratelimittypes.KeyContractAddress)) return strings.Contains(val, param) - }, time.Second*30, time.Second) + }, time.Second*30, 10*time.Millisecond) } } func (s *IntegrationTestSuite) LargeWasmUpload() { - chainA := s.configurer.GetChainConfig(0) - chainANode, err := chainA.GetDefaultNode() + _, chainNode, err := s.getChainCfgs() s.Require().NoError(err) - validatorAddr := chainANode.GetWallet(initialization.ValidatorWalletName) - chainANode.StoreWasmCode("bytecode/large.wasm", validatorAddr) + validatorAddr := chainNode.GetWallet(initialization.ValidatorWalletName) + chainNode.StoreWasmCode("bytecode/large.wasm", validatorAddr) } func (s *IntegrationTestSuite) IBCWasmHooks() { if s.skipIBC { s.T().Skip("Skipping IBC tests") } - chainA, chainANode := s.getChainACfgs() - chainB := s.configurer.GetChainConfig(1) - chainBNode, err := chainB.GetDefaultNode() + chainA, chainANode, err := s.getChainACfgs() + s.Require().NoError(err) + _, chainBNode, err := s.getChainBCfgs() s.Require().NoError(err) contractAddr := s.UploadAndInstantiateCounter(chainA) @@ -1272,12 +1279,13 @@ func (s *IntegrationTestSuite) PacketForwarding() { if s.skipIBC { s.T().Skip("Skipping IBC tests") } - chainA, chainANode := s.getChainACfgs() - chainB := s.configurer.GetChainConfig(1) + chainA, chainANode, err := s.getChainACfgs() + s.Require().NoError(err) + chainB, _, err := s.getChainBCfgs() + s.Require().NoError(err) // Instantiate the counter contract on chain A contractAddr := s.UploadAndInstantiateCounter(chainA) - fmt.Println("contractAddr PacketForwarding", contractAddr) transferAmount := int64(10) validatorAddr := chainANode.GetWallet(initialization.ValidatorWalletName) @@ -1300,9 +1308,18 @@ func (s *IntegrationTestSuite) PacketForwarding() { // check the balance of the contract s.CallCheckBalance(chainANode, contractAddr, "uosmo", transferAmount) + // Getting the sender as set by PFM + senderStr := fmt.Sprintf("channel-0/%s", validatorAddr) + senderHash32 := address.Hash(packetforwardingtypes.ModuleName, []byte(senderStr)) // typo intended + sender := sdk.AccAddress(senderHash32[:20]) + bech32Prefix := "osmo" + pfmSender, err := sdk.Bech32ifyAddressBytes(bech32Prefix, sender) + s.Require().NoError(err) + // sender wasm addr - senderBech32, err := ibchookskeeper.DeriveIntermediateSender("channel-0", validatorAddr, "osmo") + senderBech32, err := ibchookskeeper.DeriveIntermediateSender("channel-0", pfmSender, "osmo") s.Require().NoError(err) + s.Require().Eventually(func() bool { response, err := chainANode.QueryWasmSmartObject(contractAddr, fmt.Sprintf(`{"get_count": {"addr": "%s"}}`, senderBech32)) if err != nil { @@ -1324,35 +1341,39 @@ func (s *IntegrationTestSuite) AddToExistingLockPostUpgrade() { if s.skipUpgrade { s.T().Skip("Skipping AddToExistingLockPostUpgrade test") } - _, chainANode := s.getChainACfgs() + + chainAB, chainABNode, err := s.getChainCfgs() + s.Require().NoError(err) + index := s.getChainIndex(chainAB) + // ensure we can add to existing locks and superfluid locks that existed pre upgrade on chainA // we use the hardcoded gamm/pool/1 and these specific wallet names to match what was created pre upgrade - preUpgradePoolShareDenom := fmt.Sprintf("gamm/pool/%d", config.PreUpgradePoolId) + preUpgradePoolShareDenom := fmt.Sprintf("gamm/pool/%d", config.PreUpgradePoolId[index]) - lockupWalletAddr, lockupWalletSuperfluidAddr := chainANode.GetWallet("lockup-wallet"), chainANode.GetWallet("lockup-wallet-superfluid") - chainANode.AddToExistingLock(sdk.NewInt(1000000000000000000), preUpgradePoolShareDenom, "240s", lockupWalletAddr, 1) - chainANode.AddToExistingLock(sdk.NewInt(1000000000000000000), preUpgradePoolShareDenom, "240s", lockupWalletSuperfluidAddr, 2) + lockupWalletAddr, lockupWalletSuperfluidAddr := chainABNode.GetWallet("lockup-wallet"), chainABNode.GetWallet("lockup-wallet-superfluid") + chainABNode.AddToExistingLock(sdk.NewInt(1000000000000000000), preUpgradePoolShareDenom, "240s", lockupWalletAddr, 1) + chainABNode.AddToExistingLock(sdk.NewInt(1000000000000000000), preUpgradePoolShareDenom, "240s", lockupWalletSuperfluidAddr, 2) } // TestAddToExistingLock tests lockups to both regular and superfluid locks. func (s *IntegrationTestSuite) AddToExistingLock() { - chainA := s.configurer.GetChainConfig(0) - chainANode, err := chainA.GetNodeAtIndex(2) + chainAB, chainABNode, err := s.getChainCfgs() s.Require().NoError(err) - funder := chainANode.PublicAddress + + funder := chainABNode.GetWallet(initialization.ValidatorWalletName) // ensure we can add to new locks and superfluid locks // create pool and enable superfluid assets - poolId := chainANode.CreateBalancerPool("nativeDenomPool.json", funder) - chainANode.EnableSuperfluidAsset(chainA, fmt.Sprintf("gamm/pool/%d", poolId)) + poolId := chainABNode.CreateBalancerPool("nativeDenomPool.json", funder) + chainABNode.EnableSuperfluidAsset(chainAB, fmt.Sprintf("gamm/pool/%d", poolId)) // setup wallets and send gamm tokens to these wallets on chainA gammShares := fmt.Sprintf("10000000000000000000gamm/pool/%d", poolId) fundTokens := []string{gammShares, initialization.WalletFeeTokens.String()} - lockupWalletAddr := chainANode.CreateWalletAndFundFrom("TestAddToExistingLock", funder, fundTokens) - lockupWalletSuperfluidAddr := chainANode.CreateWalletAndFundFrom("TestAddToExistingLockSuperfluid", funder, fundTokens) + lockupWalletAddr := chainABNode.CreateWalletAndFundFrom("TestAddToExistingLock", funder, fundTokens, chainAB) + lockupWalletSuperfluidAddr := chainABNode.CreateWalletAndFundFrom("TestAddToExistingLockSuperfluid", funder, fundTokens, chainAB) // ensure we can add to new locks and superfluid locks on chainA - chainANode.LockAndAddToExistingLock(chainA, sdk.NewInt(1000000000000000000), fmt.Sprintf("gamm/pool/%d", poolId), lockupWalletAddr, lockupWalletSuperfluidAddr) + chainABNode.LockAndAddToExistingLock(chainAB, sdk.NewInt(1000000000000000000), fmt.Sprintf("gamm/pool/%d", poolId), lockupWalletAddr, lockupWalletSuperfluidAddr) } // TestArithmeticTWAP tests TWAP by creating a pool, performing a swap. @@ -1379,35 +1400,38 @@ func (s *IntegrationTestSuite) ArithmeticTWAP() { coinAIn, coinBIn, coinCIn := fmt.Sprintf("2000000%s", denomA), fmt.Sprintf("2000000%s", denomB), fmt.Sprintf("2000000%s", denomC) - chainA, chainANode := s.getChainACfgs() + chainAB, chainABNode, err := s.getChainCfgs() + s.Require().NoError(err) + + sender := chainABNode.GetWallet(initialization.ValidatorWalletName) // Triggers the creation of TWAP records. - poolId := chainANode.CreateBalancerPool(poolFile, initialization.ValidatorWalletName) - swapWalletAddr := chainANode.CreateWalletAndFund(walletName, []string{initialization.WalletFeeTokens.String()}) + poolId := chainABNode.CreateBalancerPool(poolFile, initialization.ValidatorWalletName) + swapWalletAddr := chainABNode.CreateWalletAndFund(walletName, []string{initialization.WalletFeeTokens.String()}, chainAB) - timeBeforeSwap := chainANode.QueryLatestBlockTime() + timeBeforeSwap := chainABNode.QueryLatestBlockTime() // Wait for the next height so that the requested twap // start time (timeBeforeSwap) is not equal to the block time. - chainA.WaitForNumHeights(2) + chainAB.WaitForNumHeights(2) s.T().Log("querying for the first TWAP to now before swap") - twapFromBeforeSwapToBeforeSwapOneAB, err := chainANode.QueryArithmeticTwapToNow(poolId, denomA, denomB, timeBeforeSwap) + twapFromBeforeSwapToBeforeSwapOneAB, err := chainABNode.QueryArithmeticTwapToNow(poolId, denomA, denomB, timeBeforeSwap) s.Require().NoError(err) - twapFromBeforeSwapToBeforeSwapOneBC, err := chainANode.QueryArithmeticTwapToNow(poolId, denomB, denomC, timeBeforeSwap) + twapFromBeforeSwapToBeforeSwapOneBC, err := chainABNode.QueryArithmeticTwapToNow(poolId, denomB, denomC, timeBeforeSwap) s.Require().NoError(err) - twapFromBeforeSwapToBeforeSwapOneCA, err := chainANode.QueryArithmeticTwapToNow(poolId, denomC, denomA, timeBeforeSwap) + twapFromBeforeSwapToBeforeSwapOneCA, err := chainABNode.QueryArithmeticTwapToNow(poolId, denomC, denomA, timeBeforeSwap) s.Require().NoError(err) - chainANode.BankSend(coinAIn, chainA.NodeConfigs[0].PublicAddress, swapWalletAddr) - chainANode.BankSend(coinBIn, chainA.NodeConfigs[0].PublicAddress, swapWalletAddr) - chainANode.BankSend(coinCIn, chainA.NodeConfigs[0].PublicAddress, swapWalletAddr) + chainABNode.BankSend(coinAIn, sender, swapWalletAddr) + chainABNode.BankSend(coinBIn, sender, swapWalletAddr) + chainABNode.BankSend(coinCIn, sender, swapWalletAddr) s.T().Log("querying for the second TWAP to now before swap, must equal to first") - twapFromBeforeSwapToBeforeSwapTwoAB, err := chainANode.QueryArithmeticTwapToNow(poolId, denomA, denomB, timeBeforeSwap.Add(50*time.Millisecond)) + twapFromBeforeSwapToBeforeSwapTwoAB, err := chainABNode.QueryArithmeticTwapToNow(poolId, denomA, denomB, timeBeforeSwap.Add(50*time.Millisecond)) s.Require().NoError(err) - twapFromBeforeSwapToBeforeSwapTwoBC, err := chainANode.QueryArithmeticTwapToNow(poolId, denomB, denomC, timeBeforeSwap.Add(50*time.Millisecond)) + twapFromBeforeSwapToBeforeSwapTwoBC, err := chainABNode.QueryArithmeticTwapToNow(poolId, denomB, denomC, timeBeforeSwap.Add(50*time.Millisecond)) s.Require().NoError(err) - twapFromBeforeSwapToBeforeSwapTwoCA, err := chainANode.QueryArithmeticTwapToNow(poolId, denomC, denomA, timeBeforeSwap.Add(50*time.Millisecond)) + twapFromBeforeSwapToBeforeSwapTwoCA, err := chainABNode.QueryArithmeticTwapToNow(poolId, denomC, denomA, timeBeforeSwap.Add(50*time.Millisecond)) s.Require().NoError(err) // Since there were no swaps between the two queries, the TWAPs should be the same. @@ -1416,30 +1440,30 @@ func (s *IntegrationTestSuite) ArithmeticTWAP() { osmoassert.DecApproxEq(s.T(), twapFromBeforeSwapToBeforeSwapOneCA, twapFromBeforeSwapToBeforeSwapTwoCA, sdk.NewDecWithPrec(1, 3)) s.T().Log("performing swaps") - chainANode.SwapExactAmountIn(coinAIn, minAmountOut, fmt.Sprintf("%d", poolId), denomB, swapWalletAddr) - chainANode.SwapExactAmountIn(coinBIn, minAmountOut, fmt.Sprintf("%d", poolId), denomC, swapWalletAddr) - chainANode.SwapExactAmountIn(coinCIn, minAmountOut, fmt.Sprintf("%d", poolId), denomA, swapWalletAddr) + chainABNode.SwapExactAmountIn(coinAIn, minAmountOut, fmt.Sprintf("%d", poolId), denomB, swapWalletAddr) + chainABNode.SwapExactAmountIn(coinBIn, minAmountOut, fmt.Sprintf("%d", poolId), denomC, swapWalletAddr) + chainABNode.SwapExactAmountIn(coinCIn, minAmountOut, fmt.Sprintf("%d", poolId), denomA, swapWalletAddr) keepPeriodCountDown := time.NewTimer(initialization.TWAPPruningKeepPeriod) // Make sure that we are still producing blocks and move far enough for the swap TWAP record to be created // so that we can measure start time post-swap (timeAfterSwap). - chainA.WaitForNumHeights(2) + chainAB.WaitForNumHeights(2) // Measure time after swap and wait for a few blocks to be produced. // This is needed to ensure that start time is before the block time // when we query for TWAP. - timeAfterSwap := chainANode.QueryLatestBlockTime() - chainA.WaitForNumHeights(2) + timeAfterSwap := chainABNode.QueryLatestBlockTime() + chainAB.WaitForNumHeights(2) // TWAP "from before to after swap" should be different from "from before to before swap" // because swap introduces a new record with a different spot price. s.T().Log("querying for the TWAP from before swap to now after swap") - twapFromBeforeSwapToAfterSwapAB, err := chainANode.QueryArithmeticTwapToNow(poolId, denomA, denomB, timeBeforeSwap) + twapFromBeforeSwapToAfterSwapAB, err := chainABNode.QueryArithmeticTwapToNow(poolId, denomA, denomB, timeBeforeSwap) s.Require().NoError(err) - twapFromBeforeSwapToAfterSwapBC, err := chainANode.QueryArithmeticTwapToNow(poolId, denomB, denomC, timeBeforeSwap) + twapFromBeforeSwapToAfterSwapBC, err := chainABNode.QueryArithmeticTwapToNow(poolId, denomB, denomC, timeBeforeSwap) s.Require().NoError(err) - twapFromBeforeSwapToAfterSwapCA, err := chainANode.QueryArithmeticTwapToNow(poolId, denomC, denomA, timeBeforeSwap) + twapFromBeforeSwapToAfterSwapCA, err := chainABNode.QueryArithmeticTwapToNow(poolId, denomC, denomA, timeBeforeSwap) s.Require().NoError(err) // We had a swap of 2000000stake for some amount of uion, // 2000000uion for some amount of uosmo, and @@ -1452,11 +1476,11 @@ func (s *IntegrationTestSuite) ArithmeticTWAP() { s.Require().True(twapFromBeforeSwapToAfterSwapCA.GT(twapFromBeforeSwapToBeforeSwapOneCA)) s.T().Log("querying for the TWAP from after swap to now") - twapFromAfterToNowAB, err := chainANode.QueryArithmeticTwapToNow(poolId, denomA, denomB, timeAfterSwap) + twapFromAfterToNowAB, err := chainABNode.QueryArithmeticTwapToNow(poolId, denomA, denomB, timeAfterSwap) s.Require().NoError(err) - twapFromAfterToNowBC, err := chainANode.QueryArithmeticTwapToNow(poolId, denomB, denomC, timeAfterSwap) + twapFromAfterToNowBC, err := chainABNode.QueryArithmeticTwapToNow(poolId, denomB, denomC, timeAfterSwap) s.Require().NoError(err) - twapFromAfterToNowCA, err := chainANode.QueryArithmeticTwapToNow(poolId, denomC, denomA, timeAfterSwap) + twapFromAfterToNowCA, err := chainABNode.QueryArithmeticTwapToNow(poolId, denomC, denomA, timeAfterSwap) s.Require().NoError(err) // Because twapFromAfterToNow has a higher time weight for the after swap period, // we expect the results to be flipped from the previous comparison to twapFromBeforeSwapToBeforeSwapOne @@ -1465,11 +1489,11 @@ func (s *IntegrationTestSuite) ArithmeticTWAP() { s.Require().True(twapFromBeforeSwapToAfterSwapCA.LT(twapFromAfterToNowCA)) s.T().Log("querying for the TWAP from after swap to after swap + 10ms") - twapAfterSwapBeforePruning10MsAB, err := chainANode.QueryArithmeticTwap(poolId, denomA, denomB, timeAfterSwap, timeAfterSwap.Add(10*time.Millisecond)) + twapAfterSwapBeforePruning10MsAB, err := chainABNode.QueryArithmeticTwap(poolId, denomA, denomB, timeAfterSwap, timeAfterSwap.Add(10*time.Millisecond)) s.Require().NoError(err) - twapAfterSwapBeforePruning10MsBC, err := chainANode.QueryArithmeticTwap(poolId, denomB, denomC, timeAfterSwap, timeAfterSwap.Add(10*time.Millisecond)) + twapAfterSwapBeforePruning10MsBC, err := chainABNode.QueryArithmeticTwap(poolId, denomB, denomC, timeAfterSwap, timeAfterSwap.Add(10*time.Millisecond)) s.Require().NoError(err) - twapAfterSwapBeforePruning10MsCA, err := chainANode.QueryArithmeticTwap(poolId, denomC, denomA, timeAfterSwap, timeAfterSwap.Add(10*time.Millisecond)) + twapAfterSwapBeforePruning10MsCA, err := chainABNode.QueryArithmeticTwap(poolId, denomC, denomA, timeAfterSwap, timeAfterSwap.Add(10*time.Millisecond)) s.Require().NoError(err) // Again, because twapAfterSwapBeforePruning10Ms has a higher time weight for the after swap period between the two, // we expect no change in the inequality @@ -1489,24 +1513,24 @@ func (s *IntegrationTestSuite) ArithmeticTWAP() { // Epoch end triggers the prunning of TWAP records. // Records before swap should be pruned. - chainA.WaitForNumEpochs(1, epochIdentifier) + chainAB.WaitForNumEpochs(1, epochIdentifier) // We should not be able to get TWAP before swap since it should have been pruned. s.T().Log("pruning is now complete, querying TWAP for period that should be pruned") - _, err = chainANode.QueryArithmeticTwapToNow(poolId, denomA, denomB, timeBeforeSwap) + _, err = chainABNode.QueryArithmeticTwapToNow(poolId, denomA, denomB, timeBeforeSwap) s.Require().ErrorContains(err, "too old") - _, err = chainANode.QueryArithmeticTwapToNow(poolId, denomB, denomC, timeBeforeSwap) + _, err = chainABNode.QueryArithmeticTwapToNow(poolId, denomB, denomC, timeBeforeSwap) s.Require().ErrorContains(err, "too old") - _, err = chainANode.QueryArithmeticTwapToNow(poolId, denomC, denomA, timeBeforeSwap) + _, err = chainABNode.QueryArithmeticTwapToNow(poolId, denomC, denomA, timeBeforeSwap) s.Require().ErrorContains(err, "too old") // TWAPs for the same time range should be the same when we query for them before and after pruning. s.T().Log("querying for TWAP for period before pruning took place but should not have been pruned") - twapAfterPruning10msAB, err := chainANode.QueryArithmeticTwap(poolId, denomA, denomB, timeAfterSwap, timeAfterSwap.Add(10*time.Millisecond)) + twapAfterPruning10msAB, err := chainABNode.QueryArithmeticTwap(poolId, denomA, denomB, timeAfterSwap, timeAfterSwap.Add(10*time.Millisecond)) s.Require().NoError(err) - twapAfterPruning10msBC, err := chainANode.QueryArithmeticTwap(poolId, denomB, denomC, timeAfterSwap, timeAfterSwap.Add(10*time.Millisecond)) + twapAfterPruning10msBC, err := chainABNode.QueryArithmeticTwap(poolId, denomB, denomC, timeAfterSwap, timeAfterSwap.Add(10*time.Millisecond)) s.Require().NoError(err) - twapAfterPruning10msCA, err := chainANode.QueryArithmeticTwap(poolId, denomC, denomA, timeAfterSwap, timeAfterSwap.Add(10*time.Millisecond)) + twapAfterPruning10msCA, err := chainABNode.QueryArithmeticTwap(poolId, denomC, denomA, timeAfterSwap, timeAfterSwap.Add(10*time.Millisecond)) s.Require().NoError(err) s.Require().Equal(twapAfterSwapBeforePruning10MsAB, twapAfterPruning10msAB) s.Require().Equal(twapAfterSwapBeforePruning10MsBC, twapAfterPruning10msBC) @@ -1514,13 +1538,13 @@ func (s *IntegrationTestSuite) ArithmeticTWAP() { // TWAP "from after to after swap" should equal to "from after swap to after pruning" // These must be equal because they are calculated over time ranges with the stable and equal spot price. - timeAfterPruning := chainANode.QueryLatestBlockTime() + timeAfterPruning := chainABNode.QueryLatestBlockTime() s.T().Log("querying for TWAP from after swap to after pruning") - twapToNowPostPruningAB, err := chainANode.QueryArithmeticTwap(poolId, denomA, denomB, timeAfterSwap, timeAfterPruning) + twapToNowPostPruningAB, err := chainABNode.QueryArithmeticTwap(poolId, denomA, denomB, timeAfterSwap, timeAfterPruning) s.Require().NoError(err) - twapToNowPostPruningBC, err := chainANode.QueryArithmeticTwap(poolId, denomB, denomC, timeAfterSwap, timeAfterPruning) + twapToNowPostPruningBC, err := chainABNode.QueryArithmeticTwap(poolId, denomB, denomC, timeAfterSwap, timeAfterPruning) s.Require().NoError(err) - twapToNowPostPruningCA, err := chainANode.QueryArithmeticTwap(poolId, denomC, denomA, timeAfterSwap, timeAfterPruning) + twapToNowPostPruningCA, err := chainABNode.QueryArithmeticTwap(poolId, denomC, denomA, timeAfterSwap, timeAfterPruning) s.Require().NoError(err) // There are potential rounding errors requiring us to approximate the comparison. osmoassert.DecApproxEq(s.T(), twapToNowPostPruningAB, twapAfterSwapBeforePruning10MsAB, sdk.NewDecWithPrec(1, 3)) @@ -1533,7 +1557,11 @@ func (s *IntegrationTestSuite) StateSync() { s.T().Skip() } - chainA, chainANode := s.getChainACfgs() + // This test benefits from the use of chainA's default node, since it has + // the shortest snapshot interval. + chainA := s.configurer.GetChainConfig(0) + chainANode, err := chainA.GetDefaultNode() + s.Require().NoError(err) persistentPeers := chainA.GetPersistentPeers() @@ -1613,7 +1641,7 @@ func (s *IntegrationTestSuite) StateSync() { return stateSyncNodeHeight == runningNodeHeight }, 1*time.Minute, - time.Second, + 10*time.Millisecond, ) // stop the state synching node. @@ -1622,22 +1650,22 @@ func (s *IntegrationTestSuite) StateSync() { } func (s *IntegrationTestSuite) ExpeditedProposals() { - chainA, chainANode := s.getChainACfgs() + chainAB, chainABNode, err := s.getChainCfgs() + s.Require().NoError(err) - latestPropNumber := chainANode.SubmitTextProposal("expedited text proposal", sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(config.InitialMinExpeditedDeposit)), true) - chainA.LatestProposalNumber += 1 + propNumber := chainABNode.SubmitTextProposal("expedited text proposal", sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(config.InitialMinExpeditedDeposit)), true) - chainANode.DepositProposal(latestPropNumber, true) + chainABNode.DepositProposal(propNumber, true) totalTimeChan := make(chan time.Duration, 1) - go chainANode.QueryPropStatusTimed(latestPropNumber, "PROPOSAL_STATUS_PASSED", totalTimeChan) + go chainABNode.QueryPropStatusTimed(propNumber, "PROPOSAL_STATUS_PASSED", totalTimeChan) var wg sync.WaitGroup - for _, n := range chainA.NodeConfigs { + for _, n := range chainAB.NodeConfigs { wg.Add(1) go func(nodeConfig *chain.NodeConfig) { defer wg.Done() - nodeConfig.VoteYesProposal(initialization.ValidatorWalletName, latestPropNumber) + nodeConfig.VoteYesProposal(initialization.ValidatorWalletName, propNumber) }(n) } @@ -1654,7 +1682,7 @@ func (s *IntegrationTestSuite) ExpeditedProposals() { } // compare the time it took to reach pass status to expected expedited voting period - expeditedVotingPeriodDuration := time.Duration(chainA.ExpeditedVotingPeriod * float32(time.Second)) + expeditedVotingPeriodDuration := time.Duration(chainAB.ExpeditedVotingPeriod * float32(time.Second)) timeDelta := elapsed - expeditedVotingPeriodDuration // ensure delta is within two seconds of expected time s.Require().Less(timeDelta, 2*time.Second) @@ -1682,43 +1710,45 @@ func (s *IntegrationTestSuite) GeometricTWAP() { minAmountOut = "1" ) - chainA, chainANode := s.getChainACfgs() + chainAB, chainABNode, err := s.getChainCfgs() + s.Require().NoError(err) + + sender := chainABNode.GetWallet(initialization.ValidatorWalletName) // Triggers the creation of TWAP records. - poolId := chainANode.CreateBalancerPool(poolFile, initialization.ValidatorWalletName) - fmt.Println("poolId", poolId) - swapWalletAddr := chainANode.CreateWalletAndFund(walletName, []string{initialization.WalletFeeTokens.String()}) + poolId := chainABNode.CreateBalancerPool(poolFile, initialization.ValidatorWalletName) + swapWalletAddr := chainABNode.CreateWalletAndFund(walletName, []string{initialization.WalletFeeTokens.String()}, chainAB) // We add 5 ms to avoid landing directly on block time in twap. If block time // is provided as start time, the latest spot price is used. Otherwise // interpolation is done. - timeBeforeSwapPlus5ms := chainANode.QueryLatestBlockTime().Add(5 * time.Millisecond) + timeBeforeSwapPlus5ms := chainABNode.QueryLatestBlockTime().Add(5 * time.Millisecond) s.T().Log("geometric twap, start time ", timeBeforeSwapPlus5ms.Unix()) // Wait for the next height so that the requested twap // start time (timeBeforeSwap) is not equal to the block time. - chainA.WaitForNumHeights(2) + chainAB.WaitForNumHeights(2) s.T().Log("querying for the first geometric TWAP to now (before swap)") // Assume base = uosmo, quote = stake // At pool creation time, the twap should be: // quote assset supply / base asset supply = 2_000_000 / 1_000_000 = 2 - curBlockTime := chainANode.QueryLatestBlockTime().Unix() + curBlockTime := chainABNode.QueryLatestBlockTime().Unix() s.T().Log("geometric twap, end time ", curBlockTime) - initialTwapBOverA, err := chainANode.QueryGeometricTwapToNow(poolId, denomA, denomB, timeBeforeSwapPlus5ms) + initialTwapBOverA, err := chainABNode.QueryGeometricTwapToNow(poolId, denomA, denomB, timeBeforeSwapPlus5ms) s.Require().NoError(err) s.Require().Equal(sdk.NewDec(2), initialTwapBOverA) // Assume base = stake, quote = uosmo // At pool creation time, the twap should be: // quote assset supply / base asset supply = 1_000_000 / 2_000_000 = 0.5 - initialTwapAOverB, err := chainANode.QueryGeometricTwapToNow(poolId, denomB, denomA, timeBeforeSwapPlus5ms) + initialTwapAOverB, err := chainABNode.QueryGeometricTwapToNow(poolId, denomB, denomA, timeBeforeSwapPlus5ms) s.Require().NoError(err) s.Require().Equal(sdk.NewDecWithPrec(5, 1), initialTwapAOverB) coinAIn := fmt.Sprintf("1000000%s", denomA) - chainANode.BankSend(coinAIn, chainA.NodeConfigs[0].PublicAddress, swapWalletAddr) + chainABNode.BankSend(coinAIn, sender, swapWalletAddr) s.T().Logf("performing swap of %s for %s", coinAIn, denomB) @@ -1726,18 +1756,18 @@ func (s *IntegrationTestSuite) GeometricTWAP() { // = 2_000_000 * (1 - (1_000_000 / 2_000_000)^1) // = 2_000_000 * 0.5 // = 1_000_000 - chainANode.SwapExactAmountIn(coinAIn, minAmountOut, fmt.Sprintf("%d", poolId), denomB, swapWalletAddr) + chainABNode.SwapExactAmountIn(coinAIn, minAmountOut, fmt.Sprintf("%d", poolId), denomB, swapWalletAddr) // New supply post swap: // stake = 2_000_000 - 1_000_000 - 1_000_000 // uosmo = 1_000_000 + 1_000_000 = 2_000_000 - timeAfterSwap := chainANode.QueryLatestBlockTime() - chainA.WaitForNumHeights(1) - timeAfterSwapPlus1Height := chainANode.QueryLatestBlockTime() + timeAfterSwap := chainABNode.QueryLatestBlockTime() + chainAB.WaitForNumHeights(1) + timeAfterSwapPlus1Height := chainABNode.QueryLatestBlockTime() s.T().Log("querying for the TWAP from after swap to now") - afterSwapTwapBOverA, err := chainANode.QueryGeometricTwap(poolId, denomA, denomB, timeAfterSwap, timeAfterSwapPlus1Height) + afterSwapTwapBOverA, err := chainABNode.QueryGeometricTwap(poolId, denomA, denomB, timeAfterSwap, timeAfterSwapPlus1Height) s.Require().NoError(err) // We swap uosmo so uosmo's supply will increase and stake will decrease. @@ -1754,61 +1784,3 @@ func (s *IntegrationTestSuite) GeometricTWAP() { // quote assset supply / base asset supply = 1_000_000 / 2_000_000 = 0.5 osmoassert.DecApproxEq(s.T(), sdk.NewDecWithPrec(5, 1), afterSwapTwapBOverA, sdk.NewDecWithPrec(1, 2)) } - -// START: CAN REMOVE POST v17 UPGRADE - -// Tests that v17 upgrade correctly creates the canonical pools in the upgrade handler. -func (s *IntegrationTestSuite) ConcentratedLiquidity_CanonicalPools() { - if s.skipUpgrade { - s.T().Skip("Skipping v17 canonical pools creation test because upgrade is not enabled") - } - - _, chainANode := s.getChainACfgs() - - for _, assetPair := range v17.AssetPairsForTestsOnly { - expectedSpreadFactor := assetPair.SpreadFactor - concentratedPoolId := chainANode.QueryConcentratedPooIdLinkFromCFMM(assetPair.LinkedClassicPool) - concentratedPool := s.updatedConcentratedPool(chainANode, concentratedPoolId) - - s.Require().Equal(poolmanagertypes.Concentrated, concentratedPool.GetType()) - s.Require().Equal(assetPair.BaseAsset, concentratedPool.GetToken0()) - s.Require().Equal(v17.QuoteAsset, concentratedPool.GetToken1()) - s.Require().Equal(uint64(v17.TickSpacing), concentratedPool.GetTickSpacing()) - s.Require().Equal(expectedSpreadFactor.String(), concentratedPool.GetSpreadFactor(sdk.Context{}).String()) - - superfluidAssets := chainANode.QueryAllSuperfluidAssets() - - found := false - for _, superfluidAsset := range superfluidAssets { - if superfluidAsset.Denom == cltypes.GetConcentratedLockupDenomFromPoolId(concentratedPoolId) { - found = true - break - } - } - - if assetPair.Superfluid { - s.Require().True(found, "concentrated liquidity pool denom not found in superfluid assets") - } else { - s.Require().False(found, "concentrated liquidity pool denom found in superfluid assets") - } - - // This spot price is taken from the balancer pool that was initiated pre upgrade. - balancerPool := s.updatedCFMMPool(chainANode, assetPair.LinkedClassicPool) - expectedSpotPrice, err := balancerPool.SpotPrice(sdk.Context{}, v17.QuoteAsset, assetPair.BaseAsset) - s.Require().NoError(err) - - // Allow 0.1% margin of error. - multiplicativeTolerance := osmomath.ErrTolerance{ - MultiplicativeTolerance: sdk.MustNewDecFromStr("0.001"), - } - - s.Require().Equal(0, multiplicativeTolerance.CompareBigDec(osmomath.BigDecFromSDKDec(expectedSpotPrice), concentratedPool.GetCurrentSqrtPrice().PowerInteger(2))) - } - - // Check that the community pool module account possesses positions for all the canonical pools. - communityPoolAddress := chainANode.QueryCommunityPoolModuleAccount() - positions := chainANode.QueryConcentratedPositions(communityPoolAddress) - s.Require().Len(positions, len(v17.AssetPairsForTestsOnly)) -} - -// END: CAN REMOVE POST v17 UPGRADE diff --git a/tests/e2e/helpers_e2e_test.go b/tests/e2e/helpers_e2e_test.go index 728b0223036..95eab2f110b 100644 --- a/tests/e2e/helpers_e2e_test.go +++ b/tests/e2e/helpers_e2e_test.go @@ -62,11 +62,40 @@ func (s *IntegrationTestSuite) addrBalance(node *chain.NodeConfig, address strin return addrBalances } -func (s *IntegrationTestSuite) getChainACfgs() (*chain.Config, *chain.NodeConfig) { +var currentNodeIndexA int + +func (s *IntegrationTestSuite) getChainACfgs() (*chain.Config, *chain.NodeConfig, error) { chainA := s.configurer.GetChainConfig(0) - chainANode, err := chainA.GetDefaultNode() - s.Require().NoError(err) - return chainA, chainANode + + chainANodes := chainA.GetAllChainNodes() + + chosenNode := chainANodes[currentNodeIndexA] + currentNodeIndexA = (currentNodeIndexA + 1) % len(chainANodes) + return chainA, chosenNode, nil +} + +var currentNodeIndexB int + +func (s *IntegrationTestSuite) getChainBCfgs() (*chain.Config, *chain.NodeConfig, error) { + chainB := s.configurer.GetChainConfig(1) + + chainBNodes := chainB.GetAllChainNodes() + + chosenNode := chainBNodes[currentNodeIndexB] + currentNodeIndexB = (currentNodeIndexB + 1) % len(chainBNodes) + return chainB, chosenNode, nil +} + +var useChainA bool + +func (s *IntegrationTestSuite) getChainCfgs() (*chain.Config, *chain.NodeConfig, error) { + if useChainA { + useChainA = false + return s.getChainACfgs() + } else { + useChainA = true + return s.getChainBCfgs() + } } // Helper function for calculating uncollected spread rewards since the time that spreadRewardGrowthInsideLast corresponds to @@ -160,3 +189,11 @@ func (s *IntegrationTestSuite) UploadAndInstantiateCounter(chain *chain.Config) contractAddr := contracts[0] return contractAddr } + +func (s *IntegrationTestSuite) getChainIndex(chain *chain.Config) int { + if chain.Id == "osmo-test-a" { + return 0 + } else { + return 1 + } +} diff --git a/tests/e2e/initialization/chain.go b/tests/e2e/initialization/chain.go index 690503b78a5..d782f1401ae 100644 --- a/tests/e2e/initialization/chain.go +++ b/tests/e2e/initialization/chain.go @@ -12,14 +12,14 @@ type internalChain struct { nodes []*internalNode } -func new(id, dataDir string) (*internalChain, error) { +func new(id, dataDir string) *internalChain { chainMeta := ChainMeta{ Id: id, DataDir: dataDir, } return &internalChain{ chainMeta: chainMeta, - }, nil + } } func (c *internalChain) export() *Chain { diff --git a/tests/e2e/initialization/config.go b/tests/e2e/initialization/config.go index e0327aacc1f..35345b85eb7 100644 --- a/tests/e2e/initialization/config.go +++ b/tests/e2e/initialization/config.go @@ -438,10 +438,10 @@ func updatePoolManagerGenesis(appGenState map[string]json.RawMessage) func(*pool func updateEpochGenesis(epochGenState *epochtypes.GenesisState) { epochGenState.Epochs = []epochtypes.EpochInfo{ - // override week epochs which are in default integrations, to be 2min - epochtypes.NewGenesisEpochInfo("week", time.Second*120), - // override day epochs which are in default integrations, to be 1min - epochtypes.NewGenesisEpochInfo("day", time.Second*60), + // override week epochs which are in default integrations, to be 60 seconds + epochtypes.NewGenesisEpochInfo("week", time.Second*60), + // override day epochs which are in default integrations, to be 5 seconds + epochtypes.NewGenesisEpochInfo("day", time.Second*5), } } diff --git a/tests/e2e/initialization/init.go b/tests/e2e/initialization/init.go index 570d2bed588..a69056835e6 100644 --- a/tests/e2e/initialization/init.go +++ b/tests/e2e/initialization/init.go @@ -10,10 +10,7 @@ import ( ) func InitChain(id, dataDir string, nodeConfigs []*NodeConfig, votingPeriod, expeditedVotingPeriod time.Duration, forkHeight int) (*Chain, error) { - chain, err := new(id, dataDir) - if err != nil { - return nil, err - } + chain := new(id, dataDir) for _, nodeConfig := range nodeConfigs { newNode, err := newNode(chain, nodeConfig) @@ -49,11 +46,7 @@ func InitSingleNode(chainId, dataDir string, existingGenesisDir string, nodeConf return nil, errors.New("creating individual validator nodes after starting up chain is not currently supported") } - chain, err := new(chainId, dataDir) - if err != nil { - return nil, err - } - + chain := new(chainId, dataDir) newNode, err := newNode(chain, nodeConfig) if err != nil { return nil, err diff --git a/tests/ibc-hooks/async_acks_test.go b/tests/ibc-hooks/async_acks_test.go new file mode 100644 index 00000000000..b43313c4c0f --- /dev/null +++ b/tests/ibc-hooks/async_acks_test.go @@ -0,0 +1,120 @@ +package ibc_hooks_test + +import ( + "encoding/base64" + "encoding/json" + "fmt" + + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + sdk "github.com/cosmos/cosmos-sdk/types" + channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/tidwall/gjson" + + "github.com/osmosis-labs/osmosis/osmoutils" + "github.com/osmosis-labs/osmosis/v17/app" + "github.com/osmosis-labs/osmosis/x/ibc-hooks/types" +) + +func (suite *HooksTestSuite) forceContractToEmitAckForPacket(osmosisApp *app.OsmosisApp, ctx sdk.Context, contractAddr sdk.AccAddress, packet channeltypes.Packet, success bool) ([]byte, error) { + packetJson, err := json.Marshal(packet) + suite.Require().NoError(err) + + msg := fmt.Sprintf(`{"force_emit_ibc_ack": {"packet": %s, "channel": "channel-0", "success": %v }}`, packetJson, success) + contractKeeper := wasmkeeper.NewDefaultPermissionKeeper(osmosisApp.WasmKeeper) + return contractKeeper.Execute(ctx, contractAddr, suite.chainA.SenderAccount.GetAddress(), []byte(msg), sdk.NewCoins()) +} + +func (suite *HooksTestSuite) TestWasmHooksAsyncAcks() { + sender := suite.chainB.SenderAccount.GetAddress() + osmosisApp := suite.chainA.GetOsmosisApp() + + // Instantiate a contract that knows how to send async Acks + suite.chainA.StoreContractCode(&suite.Suite, "./bytecode/echo.wasm") + contractAddr := suite.chainA.InstantiateContract(&suite.Suite, "{}", 1) + + // Calls that don't specify async acks work as expected + memo := fmt.Sprintf(`{"wasm": {"contract": "%s", "msg": {"async": {"use_async": false}}}}`, contractAddr) + suite.fundAccount(suite.chainB, sender) + transferMsg := NewMsgTransfer(sdk.NewCoin("token0", sdk.NewInt(2000)), sender.String(), contractAddr.String(), "channel-0", memo) + sendResult, receiveResult, ack, err := suite.FullSend(transferMsg, BtoA) + suite.Require().NoError(err) + suite.Require().NotNil(sendResult) + suite.Require().NotNil(receiveResult) + suite.Require().NotNil(ack) + + // the ack has been written + allAcks := osmosisApp.IBCKeeper.ChannelKeeper.GetAllPacketAcks(suite.chainA.GetContext()) + suite.Require().Equal(1, len(allAcks)) + + // Try to emit an ack for a packet that already has been acked. This should fail + + // we extract the packet that has been acked here to test later that our contract can't emit an ack for it + alreadyAckedPacket, err := ibctesting.ParsePacketFromEvents(sendResult.GetEvents()) + suite.Require().NoError(err) + + _, err = suite.forceContractToEmitAckForPacket(osmosisApp, suite.chainA.GetContext(), contractAddr, alreadyAckedPacket, true) + suite.Require().Error(err) + suite.Require().Contains(err.Error(), "no ack actor set for channel channel-0 packet 1") + + params := types.DefaultParams() + params.AllowedAsyncAckContracts = []string{contractAddr.String()} + osmosisApp.IBCHooksKeeper.SetParams(suite.chainA.GetContext(), params) + + totalExpectedAcks := 1 + testCases := []struct { + success bool + }{ + {true}, + {false}, + } + for _, tc := range testCases { + // Calls that specify async Acks work and no Acks are sent + memo = fmt.Sprintf(`{"wasm": {"contract": "%s", "msg": {"async": {"use_async": true}}}}`, contractAddr) + suite.fundAccount(suite.chainB, sender) + transferMsg = NewMsgTransfer(sdk.NewCoin("token0", sdk.NewInt(2000)), sender.String(), contractAddr.String(), "channel-0", memo) + + sendResult, err = suite.chainB.SendMsgsNoCheck(transferMsg) + suite.Require().NoError(err) + + packet, err := ibctesting.ParsePacketFromEvents(sendResult.GetEvents()) + suite.Require().NoError(err) + + receiveResult = suite.RelayPacketNoAck(packet, BtoA) + newAck, err := ibctesting.ParseAckFromEvents(receiveResult.GetEvents()) + suite.Require().Error(err) // No ack! + suite.Require().Nil(newAck) + + // No new ack has been written + allAcks = osmosisApp.IBCKeeper.ChannelKeeper.GetAllPacketAcks(suite.chainA.GetContext()) + suite.Require().Equal(totalExpectedAcks, len(allAcks)) + + // Store a second contract and ask that one to emit an ack for the packet that the first contract sent + contractAddr2 := suite.chainA.InstantiateContract(&suite.Suite, "{}", 1) + _, err = suite.forceContractToEmitAckForPacket(osmosisApp, suite.chainA.GetContext(), contractAddr2, packet, tc.success) + // This should fail because the new contract is not authorized to emit acks for that packet + suite.Require().Error(err) + suite.Require().Contains(err.Error(), "is not allowed to send an ack for channel channel-0 packet") + + // only the contract that sent the packet can send an ack for that packet sequence + ctx := suite.chainA.GetContext() + _, err = suite.forceContractToEmitAckForPacket(osmosisApp, ctx, contractAddr, packet, tc.success) + totalExpectedAcks++ + suite.Require().NoError(err) + writtenAck, err := ibctesting.ParseAckFromEvents(ctx.EventManager().Events()) + suite.Require().NoError(err) + + allAcks = osmosisApp.IBCKeeper.ChannelKeeper.GetAllPacketAcks(suite.chainA.GetContext()) + suite.Require().Equal(totalExpectedAcks, len(allAcks)) + suite.Require().False(osmoutils.IsAckError(writtenAck)) + ackBase64 := gjson.ParseBytes(writtenAck).Get("result").String() + // decode base64 + ackBytes, err := base64.StdEncoding.DecodeString(ackBase64) + suite.Require().NoError(err) + if tc.success { + suite.Require().Equal("YWNr", gjson.ParseBytes(ackBytes).Get("ibc_ack").String()) + } else { + suite.Require().Equal("forced error", gjson.ParseBytes(ackBytes).Get("error").String()) + } + } +} diff --git a/tests/ibc-hooks/bytecode/counter.wasm b/tests/ibc-hooks/bytecode/counter.wasm index 938171ef848..eb667380ce2 100644 Binary files a/tests/ibc-hooks/bytecode/counter.wasm and b/tests/ibc-hooks/bytecode/counter.wasm differ diff --git a/tests/ibc-hooks/bytecode/crosschain_registry.wasm b/tests/ibc-hooks/bytecode/crosschain_registry.wasm index 4248452bd5a..2a648d683c8 100644 Binary files a/tests/ibc-hooks/bytecode/crosschain_registry.wasm and b/tests/ibc-hooks/bytecode/crosschain_registry.wasm differ diff --git a/tests/ibc-hooks/bytecode/crosschain_swaps.wasm b/tests/ibc-hooks/bytecode/crosschain_swaps.wasm index 910814081e3..bea2852da76 100644 Binary files a/tests/ibc-hooks/bytecode/crosschain_swaps.wasm and b/tests/ibc-hooks/bytecode/crosschain_swaps.wasm differ diff --git a/tests/ibc-hooks/bytecode/echo.wasm b/tests/ibc-hooks/bytecode/echo.wasm index 4feb4554e7c..93e7814b909 100644 Binary files a/tests/ibc-hooks/bytecode/echo.wasm and b/tests/ibc-hooks/bytecode/echo.wasm differ diff --git a/tests/ibc-hooks/bytecode/outpost.wasm b/tests/ibc-hooks/bytecode/outpost.wasm index 0b1f86d5cb3..dceb3dd8d65 100644 Binary files a/tests/ibc-hooks/bytecode/outpost.wasm and b/tests/ibc-hooks/bytecode/outpost.wasm differ diff --git a/tests/ibc-hooks/ibc_middleware_test.go b/tests/ibc-hooks/ibc_middleware_test.go index 77dfc2905ed..d6b9d6feeff 100644 --- a/tests/ibc-hooks/ibc_middleware_test.go +++ b/tests/ibc-hooks/ibc_middleware_test.go @@ -69,6 +69,7 @@ func TestIBCHooksTestSuite(t *testing.T) { func (suite *HooksTestSuite) SetupTest() { // TODO: This needs to get removed. Waiting on https://github.com/cosmos/ibc-go/issues/3123 txfeetypes.ConsensusMinFee = sdk.ZeroDec() + suite.Setup() ibctesting.DefaultTestingAppInit = osmosisibctesting.SetupTestingApp suite.coordinator = ibctesting.NewCoordinator(suite.T(), 3) @@ -201,6 +202,49 @@ func (suite *HooksTestSuite) GetReceiverChannel(chainA, chainB Chain) string { return receiver.ChannelID } +func (suite *HooksTestSuite) TestDeriveIntermediateSender() { + testCases := []struct { + channel string + originalSender string + bech32Prefix string + expectedAddress string + }{ + { + channel: "channel-0", + originalSender: "cosmos1tfejvgp5yzd8ypvn9t0e2uv2kcjf2laa8upya8", + bech32Prefix: "osmo", + expectedAddress: "osmo1sguz3gtyl2tjsdulwxmtprd68xtd43yyep6g5c554utz642sr8rqcgw0q6", + }, + { + channel: "channel-1", + originalSender: "cosmos1tfejvgp5yzd8ypvn9t0e2uv2kcjf2laa8upya8", + bech32Prefix: "osmo", + expectedAddress: "osmo1svnare87kluww5hnltv24m4dg72hst0qqwm5xslsvnwd22gftcussaz5l7", + }, + { + channel: "channel-0", + originalSender: "osmo12smx2wdlyttvyzvzg54y2vnqwq2qjateuf7thj", + bech32Prefix: "osmo", + expectedAddress: "osmo1vz8evs4ek3vnz4f8wy86nw9ayzn67y28vtxzjgxv6achc4pa8gesqldfz0", + }, + { + channel: "channel-0", + originalSender: "osmo12smx2wdlyttvyzvzg54y2vnqwq2qjateuf7thj", + bech32Prefix: "cosmos", + expectedAddress: "cosmos1vz8evs4ek3vnz4f8wy86nw9ayzn67y28vtxzjgxv6achc4pa8ges4z434f", + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Test failed for case (channel=%s, originalSender=%s, bech32Prefix=%s).", + tc.channel, tc.originalSender, tc.bech32Prefix), func() { + actualAddress, err := ibchookskeeper.DeriveIntermediateSender(tc.channel, tc.originalSender, tc.bech32Prefix) + suite.Require().NoError(err) + suite.Require().Equal(tc.expectedAddress, actualAddress) + }) + } +} + func (suite *HooksTestSuite) TestOnRecvPacketHooks() { var ( trace transfertypes.DenomTrace @@ -696,13 +740,16 @@ func (suite *HooksTestSuite) SetupPools(chainName Chain, multipliers []sdk.Dec) return pools } -func (suite *HooksTestSuite) SetupCrosschainSwaps(chainName Chain) (sdk.AccAddress, sdk.AccAddress) { +func (suite *HooksTestSuite) SetupCrosschainSwaps(chainName Chain, setupForwarding bool) (sdk.AccAddress, sdk.AccAddress) { chain := suite.GetChain(chainName) owner := chain.SenderAccount.GetAddress() registryAddr, _, _, _ := suite.SetupCrosschainRegistry(chainName) suite.setChainChannelLinks(registryAddr, chainName) - fmt.Println("registryAddr", registryAddr) + suite.setAllPrefixesToOsmo(registryAddr, chainName) + if setupForwarding { + suite.setForwardingOnAllChains(registryAddr) + } // Fund the account with some uosmo and some stake bankKeeper := chain.GetOsmosisApp().BankKeeper @@ -730,23 +777,7 @@ func (suite *HooksTestSuite) SetupCrosschainSwaps(chainName Chain) (sdk.AccAddre contractKeeper := wasmkeeper.NewDefaultPermissionKeeper(osmosisApp.WasmKeeper) ctx := chain.GetContext() - // Configuring two prefixes for the same channel here. This is so that we can test bad acks when the receiver can't handle the receiving addr - msg := `{ - "modify_bech32_prefixes": { - "operations": [ - {"operation": "set", "chain_name": "osmosis", "prefix": "osmo"}, - {"operation": "set", "chain_name": "chainB", "prefix": "osmo"}, - {"operation": "set", "chain_name": "chainB-cw20", "prefix": "osmo"}, - {"operation": "set", "chain_name": "chainC", "prefix": "osmo"} - ] - } - } - ` - _, err = contractKeeper.Execute(ctx, registryAddr, owner, []byte(msg), sdk.NewCoins()) - suite.Require().NoError(err) - - // ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, msg []byte, coins sdk.Coins - msg = fmt.Sprintf(`{"set_route":{"input_denom":"token0","output_denom":"token1","pool_route":[{"pool_id":"1","token_out_denom":"%s"},{"pool_id":"2","token_out_denom":"token1"}]}}`, sdk.DefaultBondDenom) + msg := `{"set_route":{"input_denom":"token0","output_denom":"token1","pool_route":[{"pool_id":"1","token_out_denom":"stake"},{"pool_id":"2","token_out_denom":"token1"}]}}` _, err = contractKeeper.Execute(ctx, swaprouterAddr, owner, []byte(msg), sdk.NewCoins()) suite.Require().NoError(err) @@ -857,6 +888,34 @@ func (suite *HooksTestSuite) setChainChannelLinks(registryAddr sdk.AccAddress, c suite.Require().NoError(err) } +func (suite *HooksTestSuite) setAllPrefixesToOsmo(registryAddr sdk.AccAddress, chainName Chain) { + chain := suite.GetChain(chainName) + ctx := chain.GetContext() + owner := chain.SenderAccount.GetAddress() + osmosisApp := chain.GetOsmosisApp() + contractKeeper := wasmkeeper.NewDefaultPermissionKeeper(osmosisApp.WasmKeeper) + + // Add all chain channel links in a single message + msg := `{ + "modify_bech32_prefixes": { + "operations": [ + {"operation": "set", "chain_name": "osmosis", "prefix": "osmo"}, + {"operation": "set", "chain_name": "chainB", "prefix": "osmo"}, + {"operation": "set", "chain_name": "chainB-cw20", "prefix": "osmo"}, + {"operation": "set", "chain_name": "chainC", "prefix": "osmo"} + ] + } + } + ` + _, err := contractKeeper.Execute(ctx, registryAddr, owner, []byte(msg), sdk.NewCoins()) + suite.Require().NoError(err) +} + +func (suite *HooksTestSuite) setForwardingOnAllChains(registryAddr sdk.AccAddress) { + suite.SetupAndTestPFM(ChainB, "chainB", registryAddr) + suite.SetupAndTestPFM(ChainC, "chainC", registryAddr) +} + // modifyChainChannelLinks modifies the chain channel links in the crosschain registry utilizing set, remove, and change operations func (suite *HooksTestSuite) modifyChainChannelLinks(registryAddr sdk.AccAddress, chainName Chain) { chain := suite.GetChain(chainName) @@ -948,29 +1007,14 @@ func (suite *HooksTestSuite) TestUnwrapToken() { // Instantiate contract and set up three chains with funds sent between each registryAddr, _, token0CBA, _ := suite.SetupCrosschainRegistry(ChainA) suite.setChainChannelLinks(registryAddr, ChainA) + suite.setAllPrefixesToOsmo(registryAddr, ChainA) + suite.setForwardingOnAllChains(registryAddr) chain := suite.GetChain(ChainA) - ctx := chain.GetContext() owner := chain.SenderAccount.GetAddress() receiver := chain.SenderAccounts[1].SenderAccount.GetAddress() osmosisApp := chain.GetOsmosisApp() - contractKeeper := wasmkeeper.NewDefaultPermissionKeeper(osmosisApp.WasmKeeper) - - msg := `{ - "modify_bech32_prefixes": { - "operations": [ - {"operation": "set", "chain_name": "osmosis", "prefix": "osmo"}, - {"operation": "set", "chain_name": "chainA", "prefix": "osmo"}, - {"operation": "set", "chain_name": "chainB", "prefix": "osmo"}, - {"operation": "set", "chain_name": "chainC", "prefix": "osmo"} - ] - } - } - ` - _, err := contractKeeper.Execute(ctx, registryAddr, owner, []byte(msg), sdk.NewCoins()) - suite.Require().NoError(err) - // Check that the balances are correct: token0CB should be >100, token0CBA should be 0 denomTrace0CA := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", suite.pathAC.EndpointA.ChannelID, "token0")) token0CA := denomTrace0CA.IBCDenom() @@ -1003,7 +1047,7 @@ func (suite *HooksTestSuite) TestUnwrapToken() { initialReceiverBalance := receiverApp.BankKeeper.GetBalance(receiverChain.GetContext(), receiver, tc.receivedToken) suite.Require().Equal(sdk.NewInt(0), initialReceiverBalance.Amount) - msg = fmt.Sprintf(`{ + msg := fmt.Sprintf(`{ "unwrap_coin": { "receiver": "%s", "into_chain": "%s" @@ -1035,7 +1079,7 @@ func (suite *HooksTestSuite) TestUnwrapToken() { func (suite *HooksTestSuite) TestCrosschainSwaps() { owner := suite.chainA.SenderAccount.GetAddress() - _, crosschainAddr := suite.SetupCrosschainSwaps(ChainA) + _, crosschainAddr := suite.SetupCrosschainSwaps(ChainA, true) osmosisApp := suite.chainA.GetOsmosisApp() contractKeeper := wasmkeeper.NewDefaultPermissionKeeper(osmosisApp.WasmKeeper) @@ -1069,7 +1113,7 @@ func (suite *HooksTestSuite) TestCrosschainSwaps() { packetSequence, ok := responseJson["packet_sequence"].(float64) suite.Require().True(ok) - suite.Require().Equal(packetSequence, 1.0) + suite.Require().Equal(packetSequence, 2.0) balanceSender2 := osmosisApp.BankKeeper.GetBalance(suite.chainA.GetContext(), owner, "token0") suite.Require().Equal(int64(1000), balanceSender.Amount.Sub(balanceSender2.Amount).Int64()) @@ -1077,7 +1121,7 @@ func (suite *HooksTestSuite) TestCrosschainSwaps() { func (suite *HooksTestSuite) TestCrosschainSwapsViaIBCTest() { initializer := suite.chainB.SenderAccount.GetAddress() - _, crosschainAddr := suite.SetupCrosschainSwaps(ChainA) + _, crosschainAddr := suite.SetupCrosschainSwaps(ChainA, true) // Send some token0 tokens to B so that there are ibc tokens to send to A and crosschain-swap transferMsg := NewMsgTransfer(sdk.NewCoin("token0", sdk.NewInt(2000)), suite.chainA.SenderAccount.GetAddress().String(), initializer.String(), "channel-0", "") _, _, _, err := suite.FullSend(transferMsg, AtoB) @@ -1128,7 +1172,7 @@ func (suite *HooksTestSuite) TestCrosschainSwapsViaIBCTest() { // exist on chain B func (suite *HooksTestSuite) TestCrosschainSwapsViaIBCBadAck() { initializer := suite.chainB.SenderAccount.GetAddress() - _, crosschainAddr := suite.SetupCrosschainSwaps(ChainA) + _, crosschainAddr := suite.SetupCrosschainSwaps(ChainA, true) // Send some token0 tokens to B so that there are ibc tokens to send to A and crosschain-swap transferMsg := NewMsgTransfer(sdk.NewCoin("token0", sdk.NewInt(2000)), suite.chainA.SenderAccount.GetAddress().String(), initializer.String(), "channel-0", "") _, _, _, err := suite.FullSend(transferMsg, AtoB) @@ -1204,7 +1248,7 @@ func (suite *HooksTestSuite) TestCrosschainSwapsViaIBCBadAck() { &suite.Suite, crosschainAddr, []byte(fmt.Sprintf(`{"recoverable": {"addr": "%s"}}`, recoverAddr))) suite.Require().Contains(state, "token1") - suite.Require().Contains(state, `"sequence":2`) + suite.Require().Contains(state, `"sequence":3`) // Recover the stuck amount recoverMsg := `{"recover": {}}` @@ -1224,7 +1268,7 @@ func (suite *HooksTestSuite) TestCrosschainSwapsViaIBCBadAck() { // This is very similar to the two tests above, but the swap is done incorrectly func (suite *HooksTestSuite) TestCrosschainSwapsViaIBCBadSwap() { initializer := suite.chainB.SenderAccount.GetAddress() - _, crosschainAddr := suite.SetupCrosschainSwaps(ChainA) + _, crosschainAddr := suite.SetupCrosschainSwaps(ChainA, true) // Send some token0 tokens to B so that there are ibc tokens to send to A and crosschain-swap transferMsg := NewMsgTransfer(sdk.NewCoin("token0", sdk.NewInt(2000)), suite.chainA.SenderAccount.GetAddress().String(), initializer.String(), "channel-0", "") _, _, _, err := suite.FullSend(transferMsg, AtoB) @@ -1267,7 +1311,7 @@ func (suite *HooksTestSuite) TestCrosschainSwapsViaIBCBadSwap() { func (suite *HooksTestSuite) TestBadCrosschainSwapsNextMemoMessages() { initializer := suite.chainB.SenderAccount.GetAddress() - _, crosschainAddr := suite.SetupCrosschainSwaps(ChainA) + _, crosschainAddr := suite.SetupCrosschainSwaps(ChainA, true) // Send some token0 tokens to B so that there are ibc tokens to send to A and crosschain-swap transferMsg := NewMsgTransfer(sdk.NewCoin("token0", sdk.NewInt(20000)), suite.chainA.SenderAccount.GetAddress().String(), initializer.String(), "channel-0", "") _, _, _, err := suite.FullSend(transferMsg, AtoB) @@ -1416,8 +1460,8 @@ func (suite *HooksTestSuite) TestCrosschainForwardWithMemo() { initializer := suite.chainB.SenderAccount.GetAddress() receiver := suite.chainA.SenderAccounts[5].SenderAccount.GetAddress() - _, crosschainAddrA := suite.SetupCrosschainSwaps(ChainA) - swaprouterAddrB, crosschainAddrB := suite.SetupCrosschainSwaps(ChainB) + _, crosschainAddrA := suite.SetupCrosschainSwaps(ChainA, true) + swaprouterAddrB, crosschainAddrB := suite.SetupCrosschainSwaps(ChainB, false) // Send some token0 and token1 tokens to B so that there are ibc token0 to send to A and crosschain-swap, and token1 to create the pool transferMsg := NewMsgTransfer(sdk.NewCoin("token0", sdk.NewInt(500000)), suite.chainA.SenderAccount.GetAddress().String(), initializer.String(), "channel-0", "") _, _, _, err := suite.FullSend(transferMsg, AtoB) @@ -1488,7 +1532,7 @@ func (suite *HooksTestSuite) TestCrosschainSwapsViaIBCMultiHop() { accountB := suite.chainB.SenderAccount.GetAddress() accountC := suite.chainC.SenderAccount.GetAddress() - _, crosschainAddr := suite.SetupCrosschainSwaps(ChainA) + _, crosschainAddr := suite.SetupCrosschainSwaps(ChainA, true) // Send A's token0 all the way to B (A->C->B) transferMsg := NewMsgTransfer( @@ -1613,7 +1657,7 @@ type ChainActorDefinition struct { func (suite *HooksTestSuite) TestMultiHopXCS() { accountB := suite.chainB.SenderAccount.GetAddress() - swapRouterAddr, crosschainAddr := suite.SetupCrosschainSwaps(ChainA) + swapRouterAddr, crosschainAddr := suite.SetupCrosschainSwaps(ChainA, true) sendAmount := sdk.NewInt(100) @@ -1812,9 +1856,121 @@ func (suite *HooksTestSuite) TestMultiHopXCS() { } } +// This sends a packet (setup to use PFM) through a path and ensures acks are returned to the sender +func (suite *HooksTestSuite) SendAndAckPacketThroughPath(packetPath []Direction, initialPacket channeltypes.Packet) { + var res *sdk.Result + var err error + + packetStack := make([]channeltypes.Packet, 0) + packet := initialPacket + + for i, direction := range packetPath { + packetStack = append(packetStack, packet) + suite.Require().NoError(err) + res = suite.RelayPacketNoAck(packet, direction) + if i != len(packetPath)-1 { + packet, err = ibctesting.ParsePacketFromEvents(res.GetEvents()) + suite.Require().NoError(err) + } + + senderEndpoint, receiverEndpoint := suite.GetEndpoints(direction) + receiverEndpoint.Chain.NextBlock() + err = receiverEndpoint.UpdateClient() + suite.Require().NoError(err) + senderEndpoint.Chain.NextBlock() + err = senderEndpoint.UpdateClient() + suite.Require().NoError(err) + } + ack, err := ibctesting.ParseAckFromEvents(res.GetEvents()) + suite.Require().NoError(err) + + for i := range packetPath { + packet = packetStack[len(packetStack)-i-1] + direction := packetPath[len(packetPath)-i-1] + // sender Acknowledges + senderEndpoint, receiverEndpoint := suite.GetEndpoints(direction) + + senderEndpoint.Chain.NextBlock() + err = senderEndpoint.UpdateClient() + suite.Require().NoError(err) + receiverEndpoint.Chain.NextBlock() + err = receiverEndpoint.UpdateClient() + suite.Require().NoError(err) + err = senderEndpoint.AcknowledgePacket(packet, ack) + suite.Require().NoError(err) + } +} + +func (suite *HooksTestSuite) TestSwapErrorAfterPreSwapUnwind() { + // setup + accountB := suite.chainB.SenderAccount.GetAddress() + + swapRouterAddr, crosschainAddr := suite.SetupCrosschainSwaps(ChainA, true) + + sendAmount := sdk.NewInt(100) + + sender := ChainActorDefinition{ + Chain: ChainB, + name: "chainB", + address: accountB, + } + swapFor := suite.GetIBCDenom(ChainB, ChainA, "token0") + receiver := ChainActorDefinition{ + Chain: ChainC, + name: "chainC", + address: accountB, + } + + //setup initial tokens + suite.SimpleNativeTransfer("token0", sdk.NewInt(12000000), []Chain{ChainC, ChainA}) + suite.SimpleNativeTransfer("token0", sdk.NewInt(12000000), []Chain{ChainB, ChainA}) + suite.SimpleNativeTransfer("token0", sdk.NewInt(12000000), []Chain{ChainC, ChainB}) + + token0BA := suite.GetIBCDenom(ChainB, ChainA, "token0") + token0CB := suite.GetIBCDenom(ChainC, ChainB, "token0") + token0CA := suite.GetIBCDenom(ChainC, ChainA, "token0") + + // Setup pool + poolId := suite.CreateIBCPoolOnChain(ChainA, token0BA, token0CA, sdk.NewInt(defaultPoolAmount)) + suite.SetupIBCSimpleRouteOnChain(swapRouterAddr, suite.chainA.SenderAccount.GetAddress(), poolId, ChainA, token0BA, token0CA) + + initialToken := token0CB + + // execute + // In this test, we will send chainC's native from chain B to chain A. The XCS contract will then send it back + // for unwinding before executing the swap. The swap is setup to fail at this step. The contract should then + // allow users to recover these tokens when the ack of the unwind is received. + senderChain := suite.GetChain(sender.Chain) + + sentTokenBalance := senderChain.GetOsmosisApp().BankKeeper.GetBalance(senderChain.GetContext(), sender.address, initialToken) + suite.Require().True(sentTokenBalance.Amount.GTE(sendAmount)) + + // Generate swap instructions for the contract + swapMsg := fmt.Sprintf(`{"osmosis_swap":{"output_denom":"%s","slippage":{"twap": {"window_seconds": 1, "slippage_percentage":"0"}},"receiver":"%s/%s", "on_failed_delivery": {"local_recovery_addr": "%s"}, "next_memo":{}}}`, + swapFor, receiver.name, receiver.address, sender.address, + ) + // Generate full memo + msg := fmt.Sprintf(`{"wasm": {"contract": "%s", "msg": %s } }`, crosschainAddr, swapMsg) + // Send IBC transfer with the memo with crosschain-swap instructions + transferMsg := NewMsgTransfer(sdk.NewCoin(initialToken, sendAmount), sender.address.String(), crosschainAddr.String(), suite.GetSenderChannel(sender.Chain, ChainA), msg) + _, res, _, err := suite.FullSend(transferMsg, BtoA) + // We use the receive result here because the receive adds another packet to be sent back + suite.Require().NoError(err) + suite.Require().NotNil(res) + + packet, err := ibctesting.ParsePacketFromEvents(res.GetEvents()) + suite.SendAndAckPacketThroughPath([]Direction{AtoB, BtoC, CtoA}, packet) + + recoverableQuery := fmt.Sprintf(`{"recoverable": {"addr": "%s"}}`, sender.address) + recoverableQueryResponse := suite.chainA.QueryContractJson(&suite.Suite, crosschainAddr, []byte(recoverableQuery)) + suite.Require().Equal(1, len(recoverableQueryResponse.Array())) + suite.Require().Equal(sender.address.String(), recoverableQueryResponse.Get("0.recovery_addr").String()) + suite.Require().Equal(sendAmount.String(), recoverableQueryResponse.Get("0.amount").String()) +} + func (suite *HooksTestSuite) ExecuteOutpostSwap(initializer, receiverAddr sdk.AccAddress, receiver string) { // Setup - _, crosschainAddr := suite.SetupCrosschainSwaps(ChainA) + _, crosschainAddr := suite.SetupCrosschainSwaps(ChainA, true) // Store and instantiate the outpost on chainB suite.chainB.StoreContractCode(&suite.Suite, "./bytecode/outpost.wasm") outpostAddr := suite.chainB.InstantiateContract(&suite.Suite, diff --git a/tests/ibc-hooks/path_validation_test.go b/tests/ibc-hooks/path_validation_test.go new file mode 100644 index 00000000000..80f62479e61 --- /dev/null +++ b/tests/ibc-hooks/path_validation_test.go @@ -0,0 +1,102 @@ +package ibc_hooks_test + +import ( + "fmt" + + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + sdk "github.com/cosmos/cosmos-sdk/types" + ibctesting "github.com/cosmos/ibc-go/v4/testing" +) + +// This sets up PFM on chainB and tests that it works as expected. We assume ChainA is osmosis +func (suite *HooksTestSuite) SetupAndTestPFM(chainBId Chain, chainBName string, registryAddr sdk.AccAddress) { + targetChain := suite.GetChain(chainBId) + sendFrom := targetChain.SenderAccount.GetAddress() + direction := suite.GetDirection(ChainA, chainBId) + reverseDirection := suite.GetDirection(chainBId, ChainA) + sender, receiver := suite.GetEndpoints(suite.GetDirection(ChainA, chainBId)) + + osmosisApp := suite.chainA.GetOsmosisApp() + contractKeeper := wasmkeeper.NewDefaultPermissionKeeper(osmosisApp.WasmKeeper) + + pfm_msg := fmt.Sprintf(`{"has_packet_forwarding": {"chain": "%s"}}`, chainBName) + forwarding := suite.chainA.QueryContractJson(&suite.Suite, registryAddr, []byte(pfm_msg)) + suite.Require().False(forwarding.Bool()) + + transferMsg := NewMsgTransfer(sdk.NewCoin("token0", sdk.NewInt(2000)), targetChain.SenderAccount.GetAddress().String(), sendFrom.String(), suite.GetSenderChannel(chainBId, ChainA), "") + suite.FullSend(transferMsg, reverseDirection) + tokenBA := suite.GetIBCDenom(chainBId, ChainA, "token0") + + ctx := suite.chainA.GetContext() + + msg := fmt.Sprintf(`{"propose_pfm":{"chain": "%s"}}`, chainBName) + _, err := contractKeeper.Execute(ctx, registryAddr, sendFrom, []byte(msg), sdk.NewCoins(sdk.NewCoin(tokenBA, sdk.NewInt(1)))) + suite.Require().NoError(err) + + forwarding = suite.chainA.QueryContractJson(&suite.Suite, registryAddr, []byte(pfm_msg)) + suite.Require().False(forwarding.Bool()) + + // Move forward one block + suite.chainA.NextBlock() + suite.chainA.Coordinator.IncrementTime() + + // Update both clients + err = receiver.UpdateClient() + suite.Require().NoError(err) + err = sender.UpdateClient() + suite.Require().NoError(err) + + events := ctx.EventManager().Events() + packet0, err := ibctesting.ParsePacketFromEvents(events) + suite.Require().NoError(err) + result := suite.RelayPacketNoAck(packet0, direction) // No ack because it's a forward + + forwarding = suite.chainA.QueryContractJson(&suite.Suite, registryAddr, []byte(pfm_msg)) + suite.Require().False(forwarding.Bool()) + + packet1, err := ibctesting.ParsePacketFromEvents(result.GetEvents()) + suite.Require().NoError(err) + receiveResult, _ := suite.RelayPacket(packet1, reverseDirection) + + forwarding = suite.chainA.QueryContractJson(&suite.Suite, registryAddr, []byte(pfm_msg)) + suite.Require().False(forwarding.Bool()) + + err = sender.UpdateClient() + suite.Require().NoError(err) + err = receiver.UpdateClient() + suite.Require().NoError(err) + + ack, err := ibctesting.ParseAckFromEvents(receiveResult.GetEvents()) + suite.Require().NoError(err) + + err = sender.AcknowledgePacket(packet0, ack) + suite.Require().NoError(err) + + // After the ack fully travels back to the initial chain, we consider PFM to be properly set + forwarding = suite.chainA.QueryContractJson(&suite.Suite, registryAddr, []byte(pfm_msg)) + suite.Require().True(forwarding.Bool()) +} + +func (suite *HooksTestSuite) TestPathValidation() { + owner := suite.chainA.SenderAccount.GetAddress() + registryAddr, _, _, _ := suite.SetupCrosschainRegistry(ChainA) + suite.setChainChannelLinks(registryAddr, ChainA) + + osmosisApp := suite.chainA.GetOsmosisApp() + contractKeeper := wasmkeeper.NewDefaultPermissionKeeper(osmosisApp.WasmKeeper) + + msg := fmt.Sprintf(`{ + "modify_bech32_prefixes": { + "operations": [ + {"operation": "set", "chain_name": "osmosis", "prefix": "osmo"}, + {"operation": "set", "chain_name": "chainA", "prefix": "osmo"}, + {"operation": "set", "chain_name": "chainB", "prefix": "osmo"}, + {"operation": "set", "chain_name": "chainC", "prefix": "osmo"} + ] + } + } + `) + _, err := contractKeeper.Execute(suite.chainA.GetContext(), registryAddr, owner, []byte(msg), sdk.NewCoins()) + suite.Require().NoError(err) + suite.SetupAndTestPFM(ChainB, "chainB", registryAddr) +} diff --git a/tests/ibc-hooks/testutils/Cargo.lock b/tests/ibc-hooks/testutils/Cargo.lock index 52e7e6ca1ef..96450055624 100644 --- a/tests/ibc-hooks/testutils/Cargo.lock +++ b/tests/ibc-hooks/testutils/Cargo.lock @@ -19,6 +19,12 @@ version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "base16ct" version = "0.1.1" @@ -31,6 +37,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "base64ct" version = "1.5.3" @@ -73,6 +85,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "const-oid" version = "0.9.1" @@ -81,9 +103,9 @@ checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" [[package]] name = "cosmwasm-crypto" -version = "1.1.5" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28376836c7677e1ea6d6656a754582e88b91e544ce22fae42956d5fe5549a958" +checksum = "7fecd74d3a0041114110d1260f77fcb644c5d2403549b37096c44f0e643a5177" dependencies = [ "digest 0.10.5", "ed25519-zebra", @@ -94,9 +116,9 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.1.5" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb69f4f7a8a4bce68c8fbd3646238fede1e77056e4ea31c5b6bfc37b709eec3" +checksum = "1c9f7f0e51bfc7295f7b2664fe8513c966428642aa765dad8a74acdab5e0c773" dependencies = [ "syn", ] @@ -127,11 +149,11 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.1.5" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bf9157d060abbc55152aeadcace799d03dc630575daa66604079a1206cb060" +checksum = "5034c772c1369b160731aa00bb81f93733ab2884928edd8d588733d607ac5af4" dependencies = [ - "base64", + "base64 0.13.1", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -140,6 +162,7 @@ dependencies = [ "schemars", "serde", "serde-json-wasm", + "sha2 0.10.6", "thiserror", "uint", ] @@ -162,7 +185,7 @@ dependencies = [ "cosmwasm-std", "cosmwasm-storage", "cw-multi-test", - "cw-storage-plus", + "cw-storage-plus 0.16.0", "cw2", "schemars", "serde", @@ -227,11 +250,11 @@ checksum = "7192aec80d0c01a0e5941392eea7e2b7e212ee74ca7f430bfdc899420c055ef6" dependencies = [ "anyhow", "cosmwasm-std", - "cw-storage-plus", + "cw-storage-plus 0.16.0", "cw-utils", "derivative", "itertools", - "prost", + "prost 0.9.0", "schemars", "serde", "thiserror", @@ -248,6 +271,17 @@ dependencies = [ "serde", ] +[[package]] +name = "cw-storage-plus" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + [[package]] name = "cw-utils" version = "0.16.0" @@ -271,7 +305,7 @@ checksum = "91398113b806f4d2a8d5f8d05684704a20ffd5968bf87e3473e1973710b884ad" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus", + "cw-storage-plus 0.16.0", "schemars", "serde", ] @@ -339,11 +373,15 @@ dependencies = [ name = "echo" version = "0.1.0" dependencies = [ + "base64 0.21.0", "cosmwasm-schema", "cosmwasm-std", "cosmwasm-storage", "cw-multi-test", - "cw-storage-plus", + "cw-storage-plus 1.0.1", + "osmosis-std", + "osmosis-std-derive", + "prost 0.11.9", "schemars", "serde", "thiserror", @@ -495,6 +533,25 @@ version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.16.0" @@ -507,6 +564,34 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "osmosis-std" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a10a056b61279b3529aeaa623b0a4b37b39b4b7061e833ff963fb4bc614d5e13" +dependencies = [ + "chrono", + "cosmwasm-std", + "osmosis-std-derive", + "prost 0.11.9", + "prost-types", + "schemars", + "serde", + "serde-cw-value", +] + +[[package]] +name = "osmosis-std-derive" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd08ecb36b79fd53fb6c94b3444566deb185ad6b47bbeeb6cfff6b54e3d37d4" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pkcs8" version = "0.9.0" @@ -533,7 +618,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.9.0", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive 0.11.9", ] [[package]] @@ -549,6 +644,28 @@ dependencies = [ "syn", ] +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost 0.11.9", +] + [[package]] name = "quote" version = "1.0.21" @@ -643,11 +760,20 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-cw-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75d32da6b8ed758b7d850b6c3c08f1d7df51a4df3cb201296e63e34a78e99d4" +dependencies = [ + "serde", +] + [[package]] name = "serde-json-wasm" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" +checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" dependencies = [ "serde", ] diff --git a/tests/ibc-hooks/testutils/contracts/counter/src/contract.rs b/tests/ibc-hooks/testutils/contracts/counter/src/contract.rs index 259d3634924..e54e1506298 100644 --- a/tests/ibc-hooks/testutils/contracts/counter/src/contract.rs +++ b/tests/ibc-hooks/testutils/contracts/counter/src/contract.rs @@ -366,12 +366,12 @@ mod tests { // No acks query(deps.as_ref(), env.clone(), get_msg.clone()).unwrap_err(); - let msg = SudoMsg::ReceiveAck { + let msg = SudoMsg::IBCLifecycleComplete(IBCLifecycleComplete::IBCAck { channel: format!("channel-0"), sequence: 1, ack: String::new(), success: true, - }; + }); let _res = sudo(deps.as_mut(), env.clone(), msg).unwrap(); // should increase counter by 1 @@ -379,12 +379,12 @@ mod tests { let value: GetCountResponse = from_binary(&res).unwrap(); assert_eq!(1, value.count); - let msg = SudoMsg::ReceiveAck { + let msg = SudoMsg::IBCLifecycleComplete(IBCLifecycleComplete::IBCAck { channel: format!("channel-0"), sequence: 1, ack: String::new(), success: true, - }; + }); let _res = sudo(deps.as_mut(), env.clone(), msg).unwrap(); // should increase counter by 1 diff --git a/tests/ibc-hooks/testutils/contracts/echo/Cargo.toml b/tests/ibc-hooks/testutils/contracts/echo/Cargo.toml index 6980ce8cb24..62553b0fd40 100644 --- a/tests/ibc-hooks/testutils/contracts/echo/Cargo.toml +++ b/tests/ibc-hooks/testutils/contracts/echo/Cargo.toml @@ -30,10 +30,14 @@ optimize = """docker run --rm -v "$(pwd)":/code \ """ [dependencies] +base64 = "0.21.0" cosmwasm-schema = "1.1.3" -cosmwasm-std = "1.1.3" +cosmwasm-std = {version = "1.2", features = ["stargate"]} cosmwasm-storage = "1.1.3" -cw-storage-plus = "0.16.0" +cw-storage-plus = "1.0.1" +osmosis-std = "0.15.2" +osmosis-std-derive = "0.15.2" +prost = {version = "0.11.2", default-features = false, features = ["prost-derive"]} schemars = "0.8.10" serde = { version = "1.0.145", default-features = false, features = ["derive"] } thiserror = { version = "1.0.31" } diff --git a/tests/ibc-hooks/testutils/contracts/echo/src/ibc.rs b/tests/ibc-hooks/testutils/contracts/echo/src/ibc.rs new file mode 100644 index 00000000000..8bda147ed80 --- /dev/null +++ b/tests/ibc-hooks/testutils/contracts/echo/src/ibc.rs @@ -0,0 +1,65 @@ +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub struct Height { + /// Previously known as "epoch" + #[serde(skip_serializing_if = "Option::is_none")] + revision_number: Option, + + /// The height of a block + #[serde(skip_serializing_if = "Option::is_none")] + revision_height: Option, +} + +// An IBC packet +#[cw_serde] +pub struct Packet { + pub sequence: u64, + pub source_port: String, + pub source_channel: String, + pub destination_port: String, + pub destination_channel: String, + pub data: String, // FungibleTokenData + pub timeout_height: Height, + #[serde(skip_serializing_if = "Option::is_none")] + pub timeout_timestamp: Option, +} + +// The following are part of the wasm_hooks interface +#[cw_serde] +pub enum IBCAsyncOptions { + #[serde(rename = "request_ack")] + RequestAck { + /// The source channel (osmosis side) of the IBC packet + source_channel: String, + /// The sequence number that the packet was sent with + packet_sequence: u64, + }, +} + +#[cw_serde] +pub struct OnRecvPacketAsyncResponse { + pub is_async_ack: bool, +} + +#[cw_serde] +pub struct ContractAck { + pub contract_result: String, + pub ibc_ack: String, +} + + +#[cw_serde] +#[serde(tag = "type", content = "content")] +pub enum IBCAck { + AckResponse{ + packet: Packet, + contract_ack: ContractAck, + }, + AckError { + packet: Packet, + error_description: String, + error_response: String, + } +} + diff --git a/tests/ibc-hooks/testutils/contracts/echo/src/lib.rs b/tests/ibc-hooks/testutils/contracts/echo/src/lib.rs index ab1d16a3aef..b34c18752f9 100644 --- a/tests/ibc-hooks/testutils/contracts/echo/src/lib.rs +++ b/tests/ibc-hooks/testutils/contracts/echo/src/lib.rs @@ -1,7 +1,12 @@ +pub mod ibc; +pub mod state; +use cosmwasm_schema::cw_serde; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, StdError}; -use cosmwasm_schema::{cw_serde}; +use cosmwasm_std::{to_binary, DepsMut, Env, MessageInfo, Response, StdError}; +use ibc::{ContractAck, IBCAck, IBCAsyncOptions, OnRecvPacketAsyncResponse, Packet}; +use osmosis_std_derive::CosmwasmExt; +use state::INFLIGHT_PACKETS; // Messages #[cw_serde] @@ -9,7 +14,25 @@ pub struct InstantiateMsg {} #[cw_serde] pub enum ExecuteMsg { - Echo { msg: String }, + Echo { + msg: String, + }, + Async { + use_async: bool, + }, + #[serde(rename = "force_emit_ibc_ack")] + ForceEmitIBCAck { + packet: Packet, + channel: String, + success: bool, + }, +} + +/// Message type for `sudo` entry_point +#[cw_serde] +pub enum SudoMsg { + #[serde(rename = "ibc_async")] + IBCAsync(IBCAsyncOptions), } // Instantiate @@ -29,14 +52,82 @@ fn simple_response(msg: String) -> Response { .add_attribute("echo", msg) .set_data(b"this should echo") } + +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.ibchooks.MsgEmitIBCAck")] +pub struct MsgEmitIBCAck { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(uint64, tag = "2")] + pub packet_sequence: u64, + #[prost(string, tag = "3")] + pub channel: ::prost::alloc::string::String, +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( - _deps: DepsMut, - _env: Env, + deps: DepsMut, + env: Env, _info: MessageInfo, msg: ExecuteMsg, ) -> Result { match msg { ExecuteMsg::Echo { msg } => Ok(simple_response(msg)), + ExecuteMsg::Async { use_async } => { + if use_async { + let response_data = OnRecvPacketAsyncResponse { is_async_ack: true }; + Ok(Response::new().set_data(to_binary(&response_data)?)) + } else { + Ok(Response::default()) + } + } + ExecuteMsg::ForceEmitIBCAck { packet, channel, success } => { + INFLIGHT_PACKETS.save( + deps.storage, + (&packet.destination_channel, packet.sequence), + &(packet.clone(), success), + )?; + let msg = MsgEmitIBCAck { + sender: env.contract.address.to_string(), + packet_sequence: packet.sequence, + channel, + }; + Ok(Response::new().add_message(msg)) + } + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn sudo(deps: DepsMut, _env: Env, msg: SudoMsg) -> Result { + match msg { + SudoMsg::IBCAsync(IBCAsyncOptions::RequestAck { + source_channel, + packet_sequence, + }) => { + let (packet, success) = INFLIGHT_PACKETS.load(deps.storage, (&source_channel, packet_sequence))?; + let data = match success { + true => to_binary(&IBCAck::AckResponse { + packet, + contract_ack: ContractAck { + contract_result: base64::encode("success"), + ibc_ack: base64::encode("ack"), + }, + })?, + false => to_binary(&IBCAck::AckError { + packet, + error_description: "forced error".to_string(), + error_response: r#"{"error": "forced error"}"#.to_string(), + })? + }; + Ok(Response::new().set_data(data))}, } } diff --git a/tests/ibc-hooks/testutils/contracts/echo/src/state.rs b/tests/ibc-hooks/testutils/contracts/echo/src/state.rs new file mode 100644 index 00000000000..5bdb309a683 --- /dev/null +++ b/tests/ibc-hooks/testutils/contracts/echo/src/state.rs @@ -0,0 +1,6 @@ +use cw_storage_plus::Map; + +use crate::ibc; + +// (channel, sequence) -> packet +pub const INFLIGHT_PACKETS: Map<(&str, u64), (ibc::Packet, bool)> = Map::new("inflight"); diff --git a/tests/ibc-hooks/xcs_cw20_test.go b/tests/ibc-hooks/xcs_cw20_test.go index 38d85c66dcc..62da7cdd1d5 100644 --- a/tests/ibc-hooks/xcs_cw20_test.go +++ b/tests/ibc-hooks/xcs_cw20_test.go @@ -104,7 +104,7 @@ func (suite *HooksTestSuite) TestCW20ICS20() { cw20IbcDenom := "ibc/134A49086C1164C78313D57E69E5A8656D8AE8CF6BB45B52F2DBFEFAE6EE30B8" cw20Addr, cw20ics20Addr := suite.SetupCW20(ChainB) - swaprouterAddr, crosschainAddr := suite.SetupCrosschainSwaps(ChainA) + swaprouterAddr, crosschainAddr := suite.SetupCrosschainSwaps(ChainA, true) chainA := suite.GetChain(ChainA) chainB := suite.GetChain(ChainB) diff --git a/tests/simulator/state.go b/tests/simulator/state.go index 1f50601cb83..b6ff9fecf6c 100644 --- a/tests/simulator/state.go +++ b/tests/simulator/state.go @@ -137,7 +137,7 @@ func AppStateRandomizedFn( // number of bonded accounts initialStake := r.Int63n(1e12) // Don't allow 0 validators to start off with - numInitiallyBonded := int64(rand.Intn(299)) + 1 + numInitiallyBonded := int64(r.Intn(299)) + 1 if numInitiallyBonded > numAccs { numInitiallyBonded = numAccs diff --git a/wasmbinding/stargate_whitelist.go b/wasmbinding/stargate_whitelist.go index 6117b092530..ea1e7f44017 100644 --- a/wasmbinding/stargate_whitelist.go +++ b/wasmbinding/stargate_whitelist.go @@ -105,6 +105,8 @@ func init() { // lockup setWhitelistedQuery("/osmosis.lockup.Query/ModuleBalance", &lockuptypes.ModuleBalanceResponse{}) setWhitelistedQuery("/osmosis.lockup.Query/ModuleLockedAmount", &lockuptypes.ModuleLockedAmountResponse{}) + // Warning: it iterates over every single lock account has, which means this query can have unbounded gas + setWhitelistedQuery("/osmosis.lockup.Query/AccountLockedCoins", &lockuptypes.AccountLockedCoinsResponse{}) setWhitelistedQuery("/osmosis.lockup.Query/AccountUnlockableCoins", &lockuptypes.AccountUnlockableCoinsResponse{}) setWhitelistedQuery("/osmosis.lockup.Query/AccountUnlockingCoins", &lockuptypes.AccountUnlockingCoinsResponse{}) setWhitelistedQuery("/osmosis.lockup.Query/LockedDenom", &lockuptypes.LockedDenomResponse{}) diff --git a/x/concentrated-liquidity/bench_test.go b/x/concentrated-liquidity/bench_test.go index 97de450456a..efe1a7c4f04 100644 --- a/x/concentrated-liquidity/bench_test.go +++ b/x/concentrated-liquidity/bench_test.go @@ -26,7 +26,7 @@ type BenchTestSuite struct { func (s *BenchTestSuite) createPosition(accountIndex int, poolId uint64, coin0, coin1 sdk.Coin, lowerTick, upperTick int64) { tokensDesired := sdk.NewCoins(coin0, coin1) - _, _, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, poolId, s.TestAccs[accountIndex], tokensDesired, sdk.ZeroInt(), sdk.ZeroInt(), lowerTick, upperTick) + _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, poolId, s.TestAccs[accountIndex], tokensDesired, sdk.ZeroInt(), sdk.ZeroInt(), lowerTick, upperTick) if err != nil { // This can happen for ticks that map to the very small prices // e.g 2 * 10^(-18) ends up mapping to the same sqrt price @@ -116,7 +116,7 @@ func runBenchmark(b *testing.B, testFunc func(b *testing.B, s *BenchTestSuite, p tokenDesired0 := sdk.NewCoin(denom0, sdk.NewInt(100)) tokenDesired1 := sdk.NewCoin(denom1, sdk.NewInt(100)) tokensDesired := sdk.NewCoins(tokenDesired0, tokenDesired1) - _, _, _, _, _, _, err = clKeeper.CreatePosition(s.Ctx, clPoolId, s.TestAccs[0], tokensDesired, sdk.ZeroInt(), sdk.ZeroInt(), types.MinInitializedTick, types.MaxTick) + _, err = clKeeper.CreatePosition(s.Ctx, clPoolId, s.TestAccs[0], tokensDesired, sdk.ZeroInt(), sdk.ZeroInt(), types.MinInitializedTick, types.MaxTick) noError(b, err) pool, err := clKeeper.GetPoolById(s.Ctx, clPoolId) @@ -152,7 +152,10 @@ func runBenchmark(b *testing.B, testFunc func(b *testing.B, s *BenchTestSuite, p // Normalize upperTick to be a multiple of tickSpacing upperTick = upperTick - upperTick%tickSpacing - priceLowerTick, priceUpperTick, _, _, err := clmath.TicksToSqrtPrice(lowerTick, upperTick) + priceLowerTick, err := clmath.TickToPrice(lowerTick) + noError(b, err) + + priceUpperTick, err := clmath.TickToPrice(upperTick) noError(b, err) lowerTick, upperTick, err = cl.RoundTickToCanonicalPriceTick( diff --git a/x/concentrated-liquidity/export_test.go b/x/concentrated-liquidity/export_test.go index fcb5dc56917..7a9dbeade9d 100644 --- a/x/concentrated-liquidity/export_test.go +++ b/x/concentrated-liquidity/export_test.go @@ -5,6 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/osmosis-labs/osmosis/osmomath" "github.com/osmosis-labs/osmosis/osmoutils/accum" "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/model" "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/swapstrategy" @@ -131,23 +132,15 @@ func AsConcentrated(poolI poolmanagertypes.PoolI) (types.ConcentratedPoolExtensi } func (k Keeper) ValidateSpreadFactor(ctx sdk.Context, params types.Params, spreadFactor sdk.Dec) bool { - return k.validateSpreadFactor(ctx, params, spreadFactor) + return k.validateSpreadFactor(params, spreadFactor) } func (k Keeper) ValidateTickSpacing(ctx sdk.Context, params types.Params, tickSpacing uint64) bool { - return k.validateTickSpacing(ctx, params, tickSpacing) + return k.validateTickSpacing(params, tickSpacing) } func (k Keeper) ValidateTickSpacingUpdate(ctx sdk.Context, pool types.ConcentratedPoolExtension, params types.Params, newTickSpacing uint64) bool { - return k.validateTickSpacingUpdate(ctx, pool, params, newTickSpacing) -} - -func (k Keeper) FungifyChargedPosition(ctx sdk.Context, owner sdk.AccAddress, positionIds []uint64) (uint64, error) { - return k.fungifyChargedPosition(ctx, owner, positionIds) -} - -func (k Keeper) ValidatePositionsAndGetTotalLiquidity(ctx sdk.Context, owner sdk.AccAddress, positionIds []uint64, fullyChargedDuration time.Duration) (uint64, int64, int64, sdk.Dec, error) { - return k.validatePositionsAndGetTotalLiquidity(ctx, owner, positionIds, fullyChargedDuration) + return k.validateTickSpacingUpdate(pool, params, newTickSpacing) } func (k Keeper) IsLockMature(ctx sdk.Context, underlyingLockId uint64) (bool, error) { @@ -170,7 +163,7 @@ func (k Keeper) SetPositionIdToLock(ctx sdk.Context, positionId, underlyingLockI k.setPositionIdToLock(ctx, positionId, underlyingLockId) } -func RoundTickToCanonicalPriceTick(lowerTick, upperTick int64, priceTickLower, priceTickUpper sdk.Dec, tickSpacing uint64) (int64, int64, error) { +func RoundTickToCanonicalPriceTick(lowerTick, upperTick int64, priceTickLower, priceTickUpper osmomath.BigDec, tickSpacing uint64) (int64, int64, error) { return roundTickToCanonicalPriceTick(lowerTick, upperTick, priceTickLower, priceTickUpper, tickSpacing) } @@ -319,7 +312,7 @@ func (k Keeper) GetListenersUnsafe() types.ConcentratedLiquidityListeners { } func ValidateAuthorizedQuoteDenoms(ctx sdk.Context, denom1 string, authorizedQuoteDenoms []string) bool { - return validateAuthorizedQuoteDenoms(ctx, denom1, authorizedQuoteDenoms) + return validateAuthorizedQuoteDenoms(denom1, authorizedQuoteDenoms) } func (k Keeper) ValidatePositionUpdateById(ctx sdk.Context, positionId uint64, updateInitiator sdk.AccAddress, lowerTickGiven int64, upperTickGiven int64, liquidityDeltaGiven sdk.Dec, joinTimeGiven time.Time, poolIdGiven uint64) error { @@ -331,12 +324,12 @@ func (k Keeper) GetLargestAuthorizedUptimeDuration(ctx sdk.Context) time.Duratio } func (k Keeper) GetLargestSupportedUptimeDuration(ctx sdk.Context) time.Duration { - return k.getLargestSupportedUptimeDuration(ctx) + return k.getLargestSupportedUptimeDuration() } func (k Keeper) SetupSwapStrategy(ctx sdk.Context, p types.ConcentratedPoolExtension, spreadFactor sdk.Dec, tokenInDenom string, - priceLimit sdk.Dec) (strategy swapstrategy.SwapStrategy, sqrtPriceLimit sdk.Dec, err error) { + priceLimit sdk.Dec) (strategy swapstrategy.SwapStrategy, sqrtPriceLimit osmomath.BigDec, err error) { return k.setupSwapStrategy(p, spreadFactor, tokenInDenom, priceLimit) } diff --git a/x/concentrated-liquidity/fuzz_test.go b/x/concentrated-liquidity/fuzz_test.go index e83ebed4477..b2fa7c4ec90 100644 --- a/x/concentrated-liquidity/fuzz_test.go +++ b/x/concentrated-liquidity/fuzz_test.go @@ -200,7 +200,7 @@ func (s *KeeperTestSuite) swapNearNextTickBoundary(r *rand.Rand, pool types.Conc func (s *KeeperTestSuite) swapNearInitializedTickBoundary(r *rand.Rand, pool types.ConcentratedPoolExtension, zfo bool) (didSwap bool, fatalErr bool) { fmt.Println("swap type: near initialized tick boundary") - ss := swapstrategy.New(zfo, sdk.ZeroDec(), s.App.GetKey(types.ModuleName), sdk.ZeroDec()) + ss := swapstrategy.New(zfo, osmomath.ZeroDec(), s.App.GetKey(types.ModuleName), sdk.ZeroDec()) iter := ss.InitializeNextTickIterator(s.Ctx, pool.GetId(), pool.GetCurrentTick()) defer iter.Close() @@ -483,13 +483,13 @@ func (s *KeeperTestSuite) addRandomPositon(r *rand.Rand, poolId uint64, minTick, fmt.Println("creating position: ", "accountName", "lowerTick", lowerTick, "upperTick", upperTick, "token0Desired", tokenDesired0, "tokenDesired1", tokenDesired1) - positionId, amt0, amt1, liq, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, poolId, s.TestAccs[accountIndex], tokensDesired, sdk.ZeroInt(), sdk.ZeroInt(), types.MinInitializedTick, types.MaxTick) + positionData, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, poolId, s.TestAccs[accountIndex], tokensDesired, sdk.ZeroInt(), sdk.ZeroInt(), types.MinInitializedTick, types.MaxTick) s.Require().NoError(err) - fmt.Printf("actually created: %s%s %s%s \n", amt0, ETH, amt1, USDC) + fmt.Printf("actually created: %s%s %s%s \n", positionData.Amount0, ETH, positionData.Amount1, USDC) s.positionData = append(s.positionData, positionAndLiquidity{ - positionId: positionId, - liquidity: liq, + positionId: positionData.ID, + liquidity: positionData.Liquidity, accountIndex: accountIndex, }) } diff --git a/x/concentrated-liquidity/incentives.go b/x/concentrated-liquidity/incentives.go index 4fb66652250..73bdc111e4b 100644 --- a/x/concentrated-liquidity/incentives.go +++ b/x/concentrated-liquidity/incentives.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/query" "golang.org/x/exp/slices" + "github.com/osmosis-labs/osmosis/osmomath" "github.com/osmosis-labs/osmosis/osmoutils" "github.com/osmosis-labs/osmosis/osmoutils/accum" "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/math" @@ -200,7 +201,7 @@ func (k Keeper) prepareBalancerPoolAsFullRange(ctx sdk.Context, clPoolId uint64, // Calculate the amount of liquidity the Balancer amounts qualify in the CL pool. Note that since we use the CL spot price, this is // safe against prices drifting apart between the two pools (we take the lower bound on the qualifying liquidity in this case). // The `sqrtPriceLowerTick` and `sqrtPriceUpperTick` fields are set to the appropriate values for a full range position. - qualifyingFullRangeSharesPreDiscount := math.GetLiquidityFromAmounts(clPool.GetCurrentSqrtPrice(), types.MinSqrtPrice, types.MaxSqrtPrice, asset0Amount, asset1Amount) + qualifyingFullRangeSharesPreDiscount := math.GetLiquidityFromAmounts(clPool.GetCurrentSqrtPrice(), osmomath.BigDecFromSDKDec(types.MinSqrtPrice), osmomath.BigDecFromSDKDec(types.MaxSqrtPrice), asset0Amount, asset1Amount) // Get discount ratio from governance-set discount rate. // Note that discount rate is the amount that is being discounted by (e.g. 0.05 for a 5% discount), while discount ratio is what @@ -1139,6 +1140,6 @@ func (k Keeper) getLargestAuthorizedUptimeDuration(ctx sdk.Context) time.Duratio // nolint: unused // getLargestSupportedUptimeDuration retrieves the largest supported uptime duration from the preset constant slice. -func (k Keeper) getLargestSupportedUptimeDuration(ctx sdk.Context) time.Duration { +func (k Keeper) getLargestSupportedUptimeDuration() time.Duration { return getLargestDuration(types.SupportedUptimes) } diff --git a/x/concentrated-liquidity/incentives_test.go b/x/concentrated-liquidity/incentives_test.go index c1ce6aaf2b4..e0bf2dd6150 100644 --- a/x/concentrated-liquidity/incentives_test.go +++ b/x/concentrated-liquidity/incentives_test.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/query" distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/osmosis-labs/osmosis/osmomath" "github.com/osmosis-labs/osmosis/osmoutils/accum" cl "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity" "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/math" @@ -1045,14 +1046,15 @@ func (s *KeeperTestSuite) TestUpdateUptimeAccumulatorsToNow() { if !tc.isInvalidBalancerPool { depositedCoins := sdk.NewCoins(sdk.NewCoin(clPool.GetToken0(), testQualifyingDepositsOne), sdk.NewCoin(clPool.GetToken1(), testQualifyingDepositsOne)) s.FundAcc(testAddressOne, depositedCoins) - _, _, _, qualifyingLiquidity, _, _, err = clKeeper.CreatePosition(s.Ctx, clPool.GetId(), testAddressOne, depositedCoins, sdk.ZeroInt(), sdk.ZeroInt(), clPool.GetCurrentTick()-100, clPool.GetCurrentTick()+100) + positionData, err := clKeeper.CreatePosition(s.Ctx, clPool.GetId(), testAddressOne, depositedCoins, sdk.ZeroInt(), sdk.ZeroInt(), clPool.GetCurrentTick()-100, clPool.GetCurrentTick()+100) s.Require().NoError(err) + qualifyingLiquidity = positionData.Liquidity // If a canonical balancer pool exists, we add its respective shares to the qualifying amount as well. clPool, err = clKeeper.GetPoolById(s.Ctx, clPool.GetId()) s.Require().NoError(err) if tc.canonicalBalancerPoolAssets != nil { - qualifyingBalancerLiquidityPreDiscount := math.GetLiquidityFromAmounts(clPool.GetCurrentSqrtPrice(), types.MinSqrtPrice, types.MaxSqrtPrice, tc.canonicalBalancerPoolAssets[0].Token.Amount, tc.canonicalBalancerPoolAssets[1].Token.Amount) + qualifyingBalancerLiquidityPreDiscount := math.GetLiquidityFromAmounts(clPool.GetCurrentSqrtPrice(), osmomath.BigDecFromSDKDec(types.MinSqrtPrice), osmomath.BigDecFromSDKDec(types.MaxSqrtPrice), tc.canonicalBalancerPoolAssets[0].Token.Amount, tc.canonicalBalancerPoolAssets[1].Token.Amount) qualifyingBalancerLiquidity = (sdk.OneDec().Sub(types.DefaultBalancerSharesDiscount)).Mul(qualifyingBalancerLiquidityPreDiscount) qualifyingLiquidity = qualifyingLiquidity.Add(qualifyingBalancerLiquidity) @@ -3124,7 +3126,7 @@ func (s *KeeperTestSuite) TestPrepareClaimAllIncentivesForPosition() { pool := s.PrepareConcentratedPool() // Set up position - positionIdOne, _, _, _, _, _, err := s.clk.CreatePosition(s.Ctx, pool.GetId(), s.TestAccs[0], requiredBalances, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) + positionOneData, err := s.clk.CreatePosition(s.Ctx, pool.GetId(), s.TestAccs[0], requiredBalances, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) s.Require().NoError(err) // Set incentives for pool to ensure accumulators work correctly @@ -3157,7 +3159,7 @@ func (s *KeeperTestSuite) TestPrepareClaimAllIncentivesForPosition() { // Determine what the expected forfeited incentives per share is, including the dust since we reinvest the dust when we forfeit. if tc.blockTimeElapsed < tc.minUptimeIncentiveRecord { for _, uptimeAccum := range uptimeAccumulatorsPreClaim { - newPositionName := string(types.KeyPositionId(positionIdOne)) + newPositionName := string(types.KeyPositionId(positionOneData.ID)) // Check if the accumulator contains the position. hasPosition := uptimeAccum.HasPosition(newPositionName) if hasPosition { @@ -3175,7 +3177,7 @@ func (s *KeeperTestSuite) TestPrepareClaimAllIncentivesForPosition() { } // System under test - collectedInc, forfeitedIncentives, err := s.clk.PrepareClaimAllIncentivesForPosition(s.Ctx, positionIdOne) + collectedInc, forfeitedIncentives, err := s.clk.PrepareClaimAllIncentivesForPosition(s.Ctx, positionOneData.ID) s.Require().NoError(err) s.Require().Equal(tc.expectedCoins.String(), collectedInc.String()) s.Require().Equal(expectedForfeitedIncentives.String(), forfeitedIncentives.String()) @@ -3258,14 +3260,14 @@ func (s *KeeperTestSuite) TestFunctional_ClaimIncentives_LiquidityChange_Varying s.Require().NoError(err) // Set up position - positionIdOne, _, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, defaultPoolId, defaultAddress, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) + positionOneData, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, defaultPoolId, defaultAddress, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) s.Require().NoError(err) // Increase block time by the fully charged duration (first time) s.Ctx = s.Ctx.WithBlockTime(s.Ctx.BlockTime().Add(testFullChargeDuration)) // Claim incentives. - collected, _, err := s.App.ConcentratedLiquidityKeeper.CollectIncentives(s.Ctx, defaultAddress, positionIdOne) + collected, _, err := s.App.ConcentratedLiquidityKeeper.CollectIncentives(s.Ctx, defaultAddress, positionOneData.ID) s.Require().NoError(err) s.Require().Equal(expectedCoinsPerFullCharge.String(), collected.String()) @@ -3273,20 +3275,20 @@ func (s *KeeperTestSuite) TestFunctional_ClaimIncentives_LiquidityChange_Varying s.Ctx = s.Ctx.WithBlockTime(s.Ctx.BlockTime().Add(testFullChargeDuration)) // Create another position - positionIdTwo, _, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, defaultPoolId, defaultAddress, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) + positionTwoData, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, defaultPoolId, defaultAddress, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) s.Require().NoError(err) // Increase block time by the fully charged duration (third time) s.Ctx = s.Ctx.WithBlockTime(s.Ctx.BlockTime().Add(testFullChargeDuration)) // Claim for second position. Must only claim half of the original expected amount since now there are 2 positions. - collected, _, err = s.App.ConcentratedLiquidityKeeper.CollectIncentives(s.Ctx, defaultAddress, positionIdTwo) + collected, _, err = s.App.ConcentratedLiquidityKeeper.CollectIncentives(s.Ctx, defaultAddress, positionTwoData.ID) s.Require().NoError(err) s.Require().Equal(expectedHalfOfExpectedCoinsPerFullCharge.String(), collected.String()) // Claim for first position and observe that claims full expected charge for the period between 1st claim and 2nd position creation // and half of the full charge amount since the 2nd position was created. - collected, _, err = s.App.ConcentratedLiquidityKeeper.CollectIncentives(s.Ctx, defaultAddress, positionIdOne) + collected, _, err = s.App.ConcentratedLiquidityKeeper.CollectIncentives(s.Ctx, defaultAddress, positionOneData.ID) s.Require().NoError(err) // Note, adding one since both expected amounts already subtract one (-2 in total) s.Require().Equal(expectedCoinsPerFullCharge.Add(expectedHalfOfExpectedCoinsPerFullCharge.Add(oneUUSDCCoin)...).String(), collected.String()) @@ -3586,7 +3588,7 @@ func (s *KeeperTestSuite) TestPrepareBalancerPoolAsFullRange() { s.Require().NoError(err) asset0BalancerAmount := tc.balancerPoolAssets[0].Token.Amount.ToDec().Mul(tc.portionOfSharesBonded).TruncateInt() asset1BalancerAmount := tc.balancerPoolAssets[1].Token.Amount.ToDec().Mul(tc.portionOfSharesBonded).TruncateInt() - qualifyingSharesPreDiscount := math.GetLiquidityFromAmounts(updatedClPool.GetCurrentSqrtPrice(), types.MinSqrtPrice, types.MaxSqrtPrice, asset1BalancerAmount, asset0BalancerAmount) + qualifyingSharesPreDiscount := math.GetLiquidityFromAmounts(updatedClPool.GetCurrentSqrtPrice(), osmomath.BigDecFromSDKDec(types.MinSqrtPrice), osmomath.BigDecFromSDKDec(types.MaxSqrtPrice), asset1BalancerAmount, asset0BalancerAmount) qualifyingShares := (sdk.OneDec().Sub(types.DefaultBalancerSharesDiscount)).Mul(qualifyingSharesPreDiscount) // TODO: clean this check up (will likely require refactoring the whole test) diff --git a/x/concentrated-liquidity/keeper_test.go b/x/concentrated-liquidity/keeper_test.go index fe62e142a80..dca9f82aa1c 100644 --- a/x/concentrated-liquidity/keeper_test.go +++ b/x/concentrated-liquidity/keeper_test.go @@ -64,10 +64,10 @@ var ( sqrt4999 = sdk.MustNewDecFromStr("70.703606697254136613") sqrt5500 = sdk.MustNewDecFromStr("74.161984870956629488") sqrt6250 = sdk.MustNewDecFromStr("79.056941504209483300") - DefaultExponentConsecutivePositionLowerTick, _ = math.SqrtPriceToTickRoundDownSpacing(sqrt5500, DefaultTickSpacing) - DefaultExponentConsecutivePositionUpperTick, _ = math.SqrtPriceToTickRoundDownSpacing(sqrt6250, DefaultTickSpacing) - DefaultExponentOverlappingPositionLowerTick, _ = math.SqrtPriceToTickRoundDownSpacing(sqrt4000, DefaultTickSpacing) - DefaultExponentOverlappingPositionUpperTick, _ = math.SqrtPriceToTickRoundDownSpacing(sqrt4999, DefaultTickSpacing) + DefaultExponentConsecutivePositionLowerTick, _ = math.SqrtPriceToTickRoundDownSpacing(osmomath.BigDecFromSDKDec(sqrt5500), DefaultTickSpacing) + DefaultExponentConsecutivePositionUpperTick, _ = math.SqrtPriceToTickRoundDownSpacing(osmomath.BigDecFromSDKDec(sqrt6250), DefaultTickSpacing) + DefaultExponentOverlappingPositionLowerTick, _ = math.SqrtPriceToTickRoundDownSpacing(osmomath.BigDecFromSDKDec(sqrt4000), DefaultTickSpacing) + DefaultExponentOverlappingPositionUpperTick, _ = math.SqrtPriceToTickRoundDownSpacing(osmomath.BigDecFromSDKDec(sqrt4999), DefaultTickSpacing) BAR = "bar" FOO = "foo" ErrInsufficientFunds = fmt.Errorf("insufficient funds") @@ -81,7 +81,7 @@ func TestConstants(t *testing.T) { lowerSqrtPrice, _ := osmomath.MonotonicSqrt(DefaultLowerPrice) upperSqrtPrice, _ := osmomath.MonotonicSqrt(DefaultUpperPrice) liq := math.GetLiquidityFromAmounts(DefaultCurrSqrtPrice, - lowerSqrtPrice, upperSqrtPrice, DefaultAmt0, DefaultAmt1) + osmomath.BigDecFromSDKDec(lowerSqrtPrice), osmomath.BigDecFromSDKDec(upperSqrtPrice), DefaultAmt0, DefaultAmt1) require.Equal(t, DefaultLiquidityAmt, liq) } @@ -129,11 +129,11 @@ func (s *KeeperTestSuite) SetupPosition(poolId uint64, owner sdk.AccAddress, pro } s.FundAcc(owner, providedCoins.Add(roundingErrorCoins...)) - positionId, _, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, poolId, owner, providedCoins, sdk.ZeroInt(), sdk.ZeroInt(), lowerTick, upperTick) + positionData, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, poolId, owner, providedCoins, sdk.ZeroInt(), sdk.ZeroInt(), lowerTick, upperTick) s.Require().NoError(err) - liquidity, err := s.App.ConcentratedLiquidityKeeper.GetPositionLiquidity(s.Ctx, positionId) + liquidity, err := s.App.ConcentratedLiquidityKeeper.GetPositionLiquidity(s.Ctx, positionData.ID) s.Require().NoError(err) - return liquidity, positionId + return liquidity, positionData.ID } // SetupDefaultPositions sets up four different positions to the given pool with different accounts for each position./ @@ -466,9 +466,9 @@ func (s *KeeperTestSuite) runFungifySetup(address sdk.AccAddress, numPositions i // Set up fully charged positions totalLiquidity := sdk.ZeroDec() for i := 0; i < numPositions; i++ { - _, _, _, liquidityCreated, _, _, err := s.clk.CreatePosition(s.Ctx, defaultPoolId, address, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) + positionData, err := s.clk.CreatePosition(s.Ctx, defaultPoolId, address, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) s.Require().NoError(err) - totalLiquidity = totalLiquidity.Add(liquidityCreated) + totalLiquidity = totalLiquidity.Add(positionData.Liquidity) } return pool, expectedPositionIds, totalLiquidity diff --git a/x/concentrated-liquidity/lp.go b/x/concentrated-liquidity/lp.go index c28fe796cbe..5fb17458741 100644 --- a/x/concentrated-liquidity/lp.go +++ b/x/concentrated-liquidity/lp.go @@ -15,6 +15,16 @@ import ( const noUnderlyingLockId = uint64(0) +// CreatePositionData represents the return data from CreatePosition. +type CreatePositionData struct { + ID uint64 + Amount0 sdk.Int + Amount1 sdk.Int + Liquidity sdk.Dec + LowerTick int64 + UpperTick int64 +} + // createPosition creates a concentrated liquidity position in range between lowerTick and upperTick // in a given poolId with the desired amount of each token. Since LPs are only allowed to provide // liquidity proportional to the existing reserves, the actual amount of tokens used might differ from requested. @@ -32,92 +42,92 @@ const noUnderlyingLockId = uint64(0) // - the liquidity delta is zero // - the amount0 or amount1 returned from the position update is less than the given minimums // - the pool or user does not have enough tokens to satisfy the requested amount -func (k Keeper) CreatePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, tokensProvided sdk.Coins, amount0Min, amount1Min sdk.Int, lowerTick, upperTick int64) (positionId uint64, actualAmount0 sdk.Int, actualAmount1 sdk.Int, liquidityDelta sdk.Dec, lowerTickResult int64, upperTickResult int64, err error) { +func (k Keeper) CreatePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, tokensProvided sdk.Coins, amount0Min, amount1Min sdk.Int, lowerTick, upperTick int64) (positionDate CreatePositionData, err error) { // Use the current blockTime as the position's join time. joinTime := ctx.BlockTime() // Retrieve the pool associated with the given pool ID. pool, err := k.getPoolById(ctx, poolId) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, err + return CreatePositionData{}, err } for _, token := range tokensProvided { if token.Denom != pool.GetToken0() && token.Denom != pool.GetToken1() { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, errors.New("token provided is not one of the pool tokens") + return CreatePositionData{}, errors.New("token provided is not one of the pool tokens") } } // Check if the provided tick range is valid according to the pool's tick spacing and module parameters. if err := validateTickRangeIsValid(pool.GetTickSpacing(), lowerTick, upperTick); err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, err + return CreatePositionData{}, err } amount0Desired := tokensProvided.AmountOf(pool.GetToken0()) amount1Desired := tokensProvided.AmountOf(pool.GetToken1()) if amount0Desired.IsZero() && amount1Desired.IsZero() { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, errors.New("cannot create a position with zero amounts of both pool tokens") + return CreatePositionData{}, errors.New("cannot create a position with zero amounts of both pool tokens") } // sanity check that both given minimum accounts are not negative amounts. if amount0Min.IsNegative() { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, types.NotPositiveRequireAmountError{Amount: amount0Min.String()} + return CreatePositionData{}, types.NotPositiveRequireAmountError{Amount: amount0Min.String()} } if amount1Min.IsNegative() { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, types.NotPositiveRequireAmountError{Amount: amount1Min.String()} + return CreatePositionData{}, types.NotPositiveRequireAmountError{Amount: amount1Min.String()} } // Transform the provided ticks into their corresponding sqrtPrices. - _, _, sqrtPriceLowerTick, sqrtPriceUpperTick, err := math.TicksToSqrtPrice(lowerTick, upperTick) + sqrtPriceLowerTick, sqrtPriceUpperTick, err := math.TicksToSqrtPrice(lowerTick, upperTick) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, err + return CreatePositionData{}, err } // If multiple ticks can represent the same spot price, ensure we are using the largest of those ticks. lowerTick, upperTick, err = roundTickToCanonicalPriceTick(lowerTick, upperTick, sqrtPriceLowerTick, sqrtPriceUpperTick, pool.GetTickSpacing()) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, err + return CreatePositionData{}, err } - positionId = k.getNextPositionIdAndIncrement(ctx) + positionId := k.getNextPositionIdAndIncrement(ctx) // If this is the first position created in this pool, ensure that the position includes both asset0 and asset1 // in order to assign an initial spot price. hasPositions, err := k.HasAnyPositionForPool(ctx, poolId) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, err + return CreatePositionData{}, err } if !hasPositions { err := k.initializeInitialPositionForPool(ctx, pool, amount0Desired, amount1Desired) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, err + return CreatePositionData{}, err } } // Calculate the amount of liquidity that will be added to the pool when this position is created. - liquidityDelta = math.GetLiquidityFromAmounts(pool.GetCurrentSqrtPrice(), sqrtPriceLowerTick, sqrtPriceUpperTick, amount0Desired, amount1Desired) + liquidityDelta := math.GetLiquidityFromAmounts(pool.GetCurrentSqrtPrice(), sqrtPriceLowerTick, sqrtPriceUpperTick, amount0Desired, amount1Desired) if liquidityDelta.IsZero() { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, errors.New("liquidityDelta calculated equals zero") + return CreatePositionData{}, errors.New("liquidityDelta calculated equals zero") } // Initialize / update the position in the pool based on the provided tick range and liquidity delta. - actualAmount0, actualAmount1, _, _, err = k.UpdatePosition(ctx, poolId, owner, lowerTick, upperTick, liquidityDelta, joinTime, positionId) + updateData, err := k.UpdatePosition(ctx, poolId, owner, lowerTick, upperTick, liquidityDelta, joinTime, positionId) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, err + return CreatePositionData{}, err } // Check if the actual amounts of tokens 0 and 1 are greater than or equal to the given minimum amounts. - if actualAmount0.LT(amount0Min) { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, types.InsufficientLiquidityCreatedError{Actual: actualAmount0, Minimum: amount0Min, IsTokenZero: true} + if updateData.Amount0.LT(amount0Min) { + return CreatePositionData{}, types.InsufficientLiquidityCreatedError{Actual: updateData.Amount0, Minimum: amount0Min, IsTokenZero: true} } - if actualAmount1.LT(amount1Min) { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, types.InsufficientLiquidityCreatedError{Actual: actualAmount1, Minimum: amount1Min} + if updateData.Amount1.LT(amount1Min) { + return CreatePositionData{}, types.InsufficientLiquidityCreatedError{Actual: updateData.Amount1, Minimum: amount1Min} } // Transfer the actual amounts of tokens 0 and 1 from the position owner to the pool. - err = k.sendCoinsBetweenPoolAndUser(ctx, pool.GetToken0(), pool.GetToken1(), actualAmount0, actualAmount1, owner, pool.GetAddress()) + err = k.sendCoinsBetweenPoolAndUser(ctx, pool.GetToken0(), pool.GetToken1(), updateData.Amount0, updateData.Amount1, owner, pool.GetAddress()) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, err + return CreatePositionData{}, err } event := &liquidityChangeEvent{ @@ -129,8 +139,8 @@ func (k Keeper) CreatePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddr upperTick: upperTick, joinTime: joinTime, liquidityDelta: liquidityDelta, - actualAmount0: actualAmount0, - actualAmount1: actualAmount1, + actualAmount0: updateData.Amount0, + actualAmount1: updateData.Amount1, } event.emit(ctx) @@ -143,15 +153,22 @@ func (k Keeper) CreatePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddr } tokensAdded := sdk.Coins{} - if actualAmount0.IsPositive() { - tokensAdded = tokensAdded.Add(sdk.NewCoin(pool.GetToken0(), actualAmount0)) + if updateData.Amount0.IsPositive() { + tokensAdded = tokensAdded.Add(sdk.NewCoin(pool.GetToken0(), updateData.Amount0)) } - if actualAmount1.IsPositive() { - tokensAdded = tokensAdded.Add(sdk.NewCoin(pool.GetToken1(), actualAmount1)) + if updateData.Amount1.IsPositive() { + tokensAdded = tokensAdded.Add(sdk.NewCoin(pool.GetToken1(), updateData.Amount1)) } k.RecordTotalLiquidityIncrease(ctx, tokensAdded) - return positionId, actualAmount0, actualAmount1, liquidityDelta, lowerTick, upperTick, nil + return CreatePositionData{ + ID: positionId, + Amount0: updateData.Amount0, + Amount1: updateData.Amount1, + Liquidity: liquidityDelta, + LowerTick: lowerTick, + UpperTick: upperTick, + }, nil } // WithdrawPosition attempts to withdraw liquidityAmount from a position with the given pool id in the given tick range. @@ -219,13 +236,13 @@ func (k Keeper) WithdrawPosition(ctx sdk.Context, owner sdk.AccAddress, position liquidityDelta := requestedLiquidityAmountToWithdraw.Neg() // Update the position in the pool based on the provided tick range and liquidity delta. - actualAmount0, actualAmount1, lowerTickIsEmpty, upperTickIsEmpty, err := k.UpdatePosition(ctx, position.PoolId, owner, position.LowerTick, position.UpperTick, liquidityDelta, position.JoinTime, positionId) + updateData, err := k.UpdatePosition(ctx, position.PoolId, owner, position.LowerTick, position.UpperTick, liquidityDelta, position.JoinTime, positionId) if err != nil { return sdk.Int{}, sdk.Int{}, err } // Transfer the actual amounts of tokens 0 and 1 from the pool to the position owner. - err = k.sendCoinsBetweenPoolAndUser(ctx, pool.GetToken0(), pool.GetToken1(), actualAmount0.Abs(), actualAmount1.Abs(), pool.GetAddress(), owner) + err = k.sendCoinsBetweenPoolAndUser(ctx, pool.GetToken0(), pool.GetToken1(), updateData.Amount0.Abs(), updateData.Amount1.Abs(), pool.GetAddress(), owner) if err != nil { return sdk.Int{}, sdk.Int{}, err } @@ -267,19 +284,19 @@ func (k Keeper) WithdrawPosition(ctx sdk.Context, owner sdk.AccAddress, position } // If lowertick/uppertick has no liquidity in it, delete it from state. - if lowerTickIsEmpty { + if updateData.LowerTickIsEmpty { k.RemoveTickInfo(ctx, position.PoolId, position.LowerTick) } - if upperTickIsEmpty { + if updateData.UpperTickIsEmpty { k.RemoveTickInfo(ctx, position.PoolId, position.UpperTick) } tokensRemoved := sdk.Coins{} - if actualAmount0.IsPositive() { - tokensRemoved = tokensRemoved.Add(sdk.NewCoin(pool.GetToken0(), actualAmount0)) + if updateData.Amount0.IsPositive() { + tokensRemoved = tokensRemoved.Add(sdk.NewCoin(pool.GetToken0(), updateData.Amount0)) } - if actualAmount1.IsPositive() { - tokensRemoved = tokensRemoved.Add(sdk.NewCoin(pool.GetToken1(), actualAmount1)) + if updateData.Amount1.IsPositive() { + tokensRemoved = tokensRemoved.Add(sdk.NewCoin(pool.GetToken1(), updateData.Amount1)) } k.RecordTotalLiquidityDecrease(ctx, tokensRemoved) @@ -292,12 +309,12 @@ func (k Keeper) WithdrawPosition(ctx sdk.Context, owner sdk.AccAddress, position upperTick: position.UpperTick, joinTime: position.JoinTime, liquidityDelta: liquidityDelta, - actualAmount0: actualAmount0, - actualAmount1: actualAmount1, + actualAmount0: updateData.Amount0, + actualAmount1: updateData.Amount1, } event.emit(ctx) - return actualAmount0.Neg(), actualAmount1.Neg(), nil + return updateData.Amount0.Neg(), updateData.Amount1.Neg(), nil } // addToPosition attempts to add amount0Added and amount1Added to a position with the given position id. @@ -375,7 +392,7 @@ func (k Keeper) addToPosition(ctx sdk.Context, owner sdk.AccAddress, positionId if !amount1MinGiven.IsZero() { minimumAmount1 = amount1Withdrawn.Add(amount1MinGiven) } - newPositionId, actualAmount0, actualAmount1, _, _, _, err := k.CreatePosition(ctx, position.PoolId, owner, tokensProvided, minimumAmount0, minimumAmount1, position.LowerTick, position.UpperTick) + newPositionData, err := k.CreatePosition(ctx, position.PoolId, owner, tokensProvided, minimumAmount0, minimumAmount1, position.LowerTick, position.UpperTick) if err != nil { return 0, sdk.Int{}, sdk.Int{}, err } @@ -387,13 +404,13 @@ func (k Keeper) addToPosition(ctx sdk.Context, owner sdk.AccAddress, positionId sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), sdk.NewAttribute(sdk.AttributeKeySender, owner.String()), sdk.NewAttribute(types.AttributeKeyPositionId, strconv.FormatUint(positionId, 10)), - sdk.NewAttribute(types.AttributeKeyNewPositionId, strconv.FormatUint(newPositionId, 10)), - sdk.NewAttribute(types.AttributeAmount0, actualAmount0.String()), - sdk.NewAttribute(types.AttributeAmount1, actualAmount1.String()), + sdk.NewAttribute(types.AttributeKeyNewPositionId, strconv.FormatUint(newPositionData.ID, 10)), + sdk.NewAttribute(types.AttributeAmount0, newPositionData.Amount0.String()), + sdk.NewAttribute(types.AttributeAmount1, newPositionData.Amount1.String()), ), }) - return newPositionId, actualAmount0, actualAmount1, nil + return newPositionData.ID, newPositionData.Amount0, newPositionData.Amount1, nil } // UpdatePosition updates the position in the given pool id and in the given tick range and liquidityAmount. @@ -404,14 +421,14 @@ func (k Keeper) addToPosition(ctx sdk.Context, owner sdk.AccAddress, positionId // Positive returned amounts imply that tokens are added to the pool. // If the lower and/or upper ticks are being updated to have zero liquidity, a boolean is returned to flag the tick as empty to be deleted at the end of the withdrawPosition method. // WARNING: this method may mutate the pool, make sure to refetch the pool after calling this method. -func (k Keeper) UpdatePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, lowerTick, upperTick int64, liquidityDelta sdk.Dec, joinTime time.Time, positionId uint64) (sdk.Int, sdk.Int, bool, bool, error) { +func (k Keeper) UpdatePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, lowerTick, upperTick int64, liquidityDelta sdk.Dec, joinTime time.Time, positionId uint64) (types.UpdatePositionData, error) { if err := k.validatePositionUpdateById(ctx, positionId, owner, lowerTick, upperTick, liquidityDelta, joinTime, poolId); err != nil { - return sdk.Int{}, sdk.Int{}, false, false, err + return types.UpdatePositionData{}, err } pool, err := k.getPoolById(ctx, poolId) if err != nil { - return sdk.Int{}, sdk.Int{}, false, false, err + return types.UpdatePositionData{}, err } currentTick := pool.GetCurrentTick() @@ -419,47 +436,52 @@ func (k Keeper) UpdatePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddr // update lower tickInfo state lowerTickIsEmpty, err := k.initOrUpdateTick(ctx, poolId, currentTick, lowerTick, liquidityDelta, false) if err != nil { - return sdk.Int{}, sdk.Int{}, false, false, err + return types.UpdatePositionData{}, err } // update upper tickInfo state upperTickIsEmpty, err := k.initOrUpdateTick(ctx, poolId, currentTick, upperTick, liquidityDelta, true) if err != nil { - return sdk.Int{}, sdk.Int{}, false, false, err + return types.UpdatePositionData{}, err } // update position state err = k.initOrUpdatePosition(ctx, poolId, owner, lowerTick, upperTick, liquidityDelta, joinTime, positionId) if err != nil { - return sdk.Int{}, sdk.Int{}, false, false, err + return types.UpdatePositionData{}, err } // Refetch pool to get the updated pool. // Note that updateUptimeAccumulatorsToNow may modify the pool state and rewrite it to the store. pool, err = k.getPoolById(ctx, poolId) if err != nil { - return sdk.Int{}, sdk.Int{}, false, false, err + return types.UpdatePositionData{}, err } // calculate the actual amounts of tokens 0 and 1 that were added or removed from the pool. actualAmount0, actualAmount1, err := pool.CalcActualAmounts(ctx, lowerTick, upperTick, liquidityDelta) if err != nil { - return sdk.Int{}, sdk.Int{}, false, false, err + return types.UpdatePositionData{}, err } // the pool's liquidity value is only updated if this position is active pool.UpdateLiquidityIfActivePosition(ctx, lowerTick, upperTick, liquidityDelta) if err := k.setPool(ctx, pool); err != nil { - return sdk.Int{}, sdk.Int{}, false, false, err + return types.UpdatePositionData{}, err } if err := k.initOrUpdatePositionSpreadRewardAccumulator(ctx, poolId, lowerTick, upperTick, positionId, liquidityDelta); err != nil { - return sdk.Int{}, sdk.Int{}, false, false, err + return types.UpdatePositionData{}, err } // The returned amounts are rounded down to avoid returning more to clients than they actually deposited. - return actualAmount0.TruncateInt(), actualAmount1.TruncateInt(), lowerTickIsEmpty, upperTickIsEmpty, nil + return types.UpdatePositionData{ + Amount0: actualAmount0.TruncateInt(), + Amount1: actualAmount1.TruncateInt(), + LowerTickIsEmpty: lowerTickIsEmpty, + UpperTickIsEmpty: upperTickIsEmpty, + }, nil } // sendCoinsBetweenPoolAndUser takes the amounts calculated from a join/exit position and executes the send between pool and user @@ -499,7 +521,7 @@ func (k Keeper) initializeInitialPositionForPool(ctx sdk.Context, pool types.Con // Calculate the initial tick from the initial spot price // We round down here so that the tick is rounded to // the nearest possible value given the tick spacing. - initialTick, err := math.SqrtPriceToTickRoundDownSpacing(initialCurSqrtPrice, pool.GetTickSpacing()) + initialTick, err := math.SqrtPriceToTickRoundDownSpacing(osmomath.BigDecFromSDKDec(initialCurSqrtPrice), pool.GetTickSpacing()) if err != nil { return err } diff --git a/x/concentrated-liquidity/lp_test.go b/x/concentrated-liquidity/lp_test.go index 1fda80c3b14..219706f2361 100644 --- a/x/concentrated-liquidity/lp_test.go +++ b/x/concentrated-liquidity/lp_test.go @@ -11,6 +11,7 @@ import ( "github.com/osmosis-labs/osmosis/osmoutils" cl "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity" "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/model" + cltypes "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" types "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" ) @@ -293,7 +294,16 @@ func (s *KeeperTestSuite) TestCreatePosition() { poolBalancePrePositionCreation := s.App.BankKeeper.GetAllBalances(s.Ctx, pool.GetAddress()) // System under test. - positionId, asset0, asset1, liquidityCreated, newLowerTick, newUpperTick, err := clKeeper.CreatePosition(s.Ctx, tc.poolId, s.TestAccs[0], tc.tokensProvided, tc.amount0Minimum, tc.amount1Minimum, tc.lowerTick, tc.upperTick) + positionData, err := clKeeper.CreatePosition(s.Ctx, tc.poolId, s.TestAccs[0], tc.tokensProvided, tc.amount0Minimum, tc.amount1Minimum, tc.lowerTick, tc.upperTick) + + var ( + positionId = positionData.ID + newLowerTick = positionData.LowerTick + newUpperTick = positionData.UpperTick + liquidityCreated = positionData.Liquidity + asset0 = positionData.Amount0 + asset1 = positionData.Amount1 + ) // Note user and pool account balances to compare after create position is called userBalancePostPositionCreation := s.App.BankKeeper.GetAllBalances(s.Ctx, s.TestAccs[0]) @@ -380,20 +390,26 @@ const ( ) func (s *KeeperTestSuite) createPositionWithLockState(ls lockState, poolId uint64, owner sdk.AccAddress, providedCoins sdk.Coins, dur time.Duration) (uint64, sdk.Dec) { - var liquidityCreated sdk.Dec - var positionId uint64 - var err error + var ( + positionData cl.CreatePositionData + fullRangePositionData cltypes.CreateFullRangePositionData + err error + ) + if ls == locked { - positionId, _, _, liquidityCreated, _, err = s.clk.CreateFullRangePositionLocked(s.Ctx, poolId, owner, providedCoins, dur) + fullRangePositionData, _, err = s.clk.CreateFullRangePositionLocked(s.Ctx, poolId, owner, providedCoins, dur) } else if ls == unlocking { - positionId, _, _, liquidityCreated, _, err = s.clk.CreateFullRangePositionUnlocking(s.Ctx, poolId, owner, providedCoins, dur+time.Hour) + fullRangePositionData, _, err = s.clk.CreateFullRangePositionUnlocking(s.Ctx, poolId, owner, providedCoins, dur+time.Hour) } else if ls == unlocked { - positionId, _, _, liquidityCreated, _, err = s.clk.CreateFullRangePositionUnlocking(s.Ctx, poolId, owner, providedCoins, dur-time.Hour) + fullRangePositionData, _, err = s.clk.CreateFullRangePositionUnlocking(s.Ctx, poolId, owner, providedCoins, dur-time.Hour) } else { - positionId, _, _, liquidityCreated, _, _, err = s.clk.CreatePosition(s.Ctx, poolId, owner, providedCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) + positionData, err = s.clk.CreatePosition(s.Ctx, poolId, owner, providedCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) + s.Require().NoError(err) + return positionData.ID, positionData.Liquidity } + // full range case s.Require().NoError(err) - return positionId, liquidityCreated + return fullRangePositionData.ID, fullRangePositionData.Liquidity } func (s *KeeperTestSuite) TestWithdrawPosition() { @@ -734,7 +750,7 @@ func (s *KeeperTestSuite) TestWithdrawPosition() { if expectedRemainingLiquidity.IsZero() { // Add one USDC because we withdraw one less than originally funded due to truncation in favor of the pool. s.FundAcc(owner, sdk.NewCoins(sdk.NewCoin(USDC, sdk.OneInt()))) - _, _, _, _, _, _, err = concentratedLiquidityKeeper.CreatePosition(s.Ctx, pool.GetId(), owner, config.tokensProvided, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) + _, err = concentratedLiquidityKeeper.CreatePosition(s.Ctx, pool.GetId(), owner, config.tokensProvided, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) s.Require().NoError(err) } }) @@ -1062,7 +1078,7 @@ func (s *KeeperTestSuite) TestAddToPosition() { if !tc.lastPositionInPool { s.FundAcc(s.TestAccs[1], fundCoins) - _, _, _, _, _, _, err = concentratedLiquidityKeeper.CreatePosition(s.Ctx, pool.GetId(), s.TestAccs[1], config.tokensProvided, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) + _, err = concentratedLiquidityKeeper.CreatePosition(s.Ctx, pool.GetId(), s.TestAccs[1], config.tokensProvided, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) s.Require().NoError(err) } @@ -1209,7 +1225,7 @@ func (s *KeeperTestSuite) TestSingleSidedAddToPosition() { // Create a position from the parameters in the test case. testCoins := sdk.NewCoins(sdk.NewCoin(ETH, tc.amount0ToAdd), sdk.NewCoin(USDC, tc.amount1ToAdd)) - positionId, _, _, _, _, _, err := s.clk.CreatePosition(s.Ctx, pool.GetId(), owner, testCoins, sdk.ZeroInt(), sdk.ZeroInt(), config.lowerTick, config.upperTick) + positionData, err := s.clk.CreatePosition(s.Ctx, pool.GetId(), owner, testCoins, sdk.ZeroInt(), sdk.ZeroInt(), config.lowerTick, config.upperTick) s.Require().NoError(err) // Move the block time forward @@ -1221,7 +1237,7 @@ func (s *KeeperTestSuite) TestSingleSidedAddToPosition() { if !tc.lastPositionInPool { s.FundAcc(s.TestAccs[0], fundCoins) - _, _, _, _, _, _, err = concentratedLiquidityKeeper.CreatePosition(s.Ctx, pool.GetId(), s.TestAccs[0], config.tokensProvided, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) + _, err = concentratedLiquidityKeeper.CreatePosition(s.Ctx, pool.GetId(), s.TestAccs[0], config.tokensProvided, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) s.Require().NoError(err) } @@ -1235,7 +1251,7 @@ func (s *KeeperTestSuite) TestSingleSidedAddToPosition() { } // --- System under test --- - newPosId, newAmt0, newAmt1, err := concentratedLiquidityKeeper.AddToPosition(s.Ctx, owner, positionId, tc.amount0ToAdd, tc.amount1ToAdd, config.amount0Minimum, config.amount1Minimum) + newPosId, newAmt0, newAmt1, err := concentratedLiquidityKeeper.AddToPosition(s.Ctx, owner, positionData.ID, tc.amount0ToAdd, tc.amount1ToAdd, config.amount0Minimum, config.amount1Minimum) if config.expectedError != nil { s.Require().Error(err) s.Require().Equal(sdk.Int{}, newAmt0) @@ -1260,7 +1276,7 @@ func (s *KeeperTestSuite) TestSingleSidedAddToPosition() { s.Require().Equal(0, errToleranceOneRoundDown.Compare(postBalanceToken1.Amount.Sub(preBalanceToken1.Amount), sdk.ZeroInt())) // now check that old position id has been successfully deleted - _, err = s.App.ConcentratedLiquidityKeeper.GetPosition(s.Ctx, positionId) + _, err = s.App.ConcentratedLiquidityKeeper.GetPosition(s.Ctx, positionData.ID) s.Require().Error(err) }) } @@ -1604,7 +1620,7 @@ func (s *KeeperTestSuite) TestUpdatePosition() { // create position // Fund test account and create the desired position s.FundAcc(s.TestAccs[0], DefaultCoins) - _, _, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition( + _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition( s.Ctx, 1, s.TestAccs[0], @@ -1619,7 +1635,7 @@ func (s *KeeperTestSuite) TestUpdatePosition() { s.Ctx = s.Ctx.WithBlockTime(expectedUpdateTime) // system under test - actualAmount0, actualAmount1, lowerTickIsEmpty, upperTickIsEmpty, err := s.App.ConcentratedLiquidityKeeper.UpdatePosition( + updateData, err := s.App.ConcentratedLiquidityKeeper.UpdatePosition( s.Ctx, tc.poolId, s.TestAccs[tc.ownerIndex], @@ -1632,17 +1648,17 @@ func (s *KeeperTestSuite) TestUpdatePosition() { if tc.expectedError { s.Require().Error(err) - s.Require().Equal(sdk.Int{}, actualAmount0) - s.Require().Equal(sdk.Int{}, actualAmount1) + s.Require().Equal(sdk.Int{}, updateData.Amount0) + s.Require().Equal(sdk.Int{}, updateData.Amount1) } else { s.Require().NoError(err) if tc.liquidityDelta.Equal(DefaultLiquidityAmt.Neg()) { - s.Require().True(lowerTickIsEmpty) - s.Require().True(upperTickIsEmpty) + s.Require().True(updateData.LowerTickIsEmpty) + s.Require().True(updateData.UpperTickIsEmpty) } else { - s.Require().False(lowerTickIsEmpty) - s.Require().False(upperTickIsEmpty) + s.Require().False(updateData.LowerTickIsEmpty) + s.Require().False(updateData.UpperTickIsEmpty) } var ( @@ -1663,8 +1679,8 @@ func (s *KeeperTestSuite) TestUpdatePosition() { expectedAmount1 = tc.amount1Expected.ToDec() } - s.Require().Equal(expectedAmount0.TruncateInt().String(), actualAmount0.String()) - s.Require().Equal(expectedAmount1.TruncateInt().String(), actualAmount1.String()) + s.Require().Equal(expectedAmount0.TruncateInt().String(), updateData.Amount0.String()) + s.Require().Equal(expectedAmount1.TruncateInt().String(), updateData.Amount1.String()) // validate if position has been properly updated s.validatePositionUpdate(s.Ctx, tc.positionId, tc.expectedPositionLiquidity) @@ -1829,9 +1845,18 @@ func (s *KeeperTestSuite) TestInverseRelation_CreatePosition_WithdrawPosition() poolBalancePrePositionCreation := s.App.BankKeeper.GetAllBalances(s.Ctx, poolBefore.GetAddress()) // System under test. - positionId, amtDenom0CreatePosition, amtDenom1CreatePosition, liquidityCreated, newLowerTick, newUpperTick, err := clKeeper.CreatePosition(s.Ctx, tc.poolId, s.TestAccs[0], tc.tokensProvided, tc.amount0Minimum, tc.amount1Minimum, tc.lowerTick, tc.upperTick) + positionData, err := clKeeper.CreatePosition(s.Ctx, tc.poolId, s.TestAccs[0], tc.tokensProvided, tc.amount0Minimum, tc.amount1Minimum, tc.lowerTick, tc.upperTick) s.Require().NoError(err) + var ( + positionId = positionData.ID + newLowerTick = positionData.LowerTick + newUpperTick = positionData.UpperTick + liquidityCreated = positionData.Liquidity + amtDenom0CreatePosition = positionData.Amount0 + amtDenom1CreatePosition = positionData.Amount1 + ) + if tc.expectedLowerTick != 0 { s.Require().Equal(tc.expectedLowerTick, newLowerTick) tc.lowerTick = newLowerTick @@ -1971,8 +1996,8 @@ func (s *KeeperTestSuite) TestIsLockMature() { s.Run(name, func() { tc := tc var ( - positionId uint64 concentratedLockId uint64 + positionData types.CreateFullRangePositionData err error ) s.SetupTest() @@ -1984,17 +2009,15 @@ func (s *KeeperTestSuite) TestIsLockMature() { s.FundAcc(s.TestAccs[0], coinsToFund) if tc.unlockingPosition { - positionId, _, _, _, concentratedLockId, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionUnlocking(s.Ctx, pool.GetId(), s.TestAccs[0], coinsToFund, tc.remainingLockDuration) - s.Require().NoError(err) + positionData, concentratedLockId, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionUnlocking(s.Ctx, pool.GetId(), s.TestAccs[0], coinsToFund, tc.remainingLockDuration) } else if tc.lockedPosition { - positionId, _, _, _, concentratedLockId, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, pool.GetId(), s.TestAccs[0], coinsToFund, tc.remainingLockDuration) - s.Require().NoError(err) + positionData, concentratedLockId, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, pool.GetId(), s.TestAccs[0], coinsToFund, tc.remainingLockDuration) } else { - positionId, _, _, _, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, pool.GetId(), s.TestAccs[0], coinsToFund) - s.Require().NoError(err) + positionData, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, pool.GetId(), s.TestAccs[0], coinsToFund) } + s.Require().NoError(err) - _, err = s.App.ConcentratedLiquidityKeeper.GetPosition(s.Ctx, positionId) + _, err = s.App.ConcentratedLiquidityKeeper.GetPosition(s.Ctx, positionData.ID) s.Require().NoError(err) // Increment block time by a second to ensure test cases with zero lock duration are in the past @@ -2003,11 +2026,7 @@ func (s *KeeperTestSuite) TestIsLockMature() { // System under test lockIsMature, _ := s.App.ConcentratedLiquidityKeeper.IsLockMature(s.Ctx, concentratedLockId) - if tc.expectedLockIsMature { - s.Require().True(lockIsMature) - } else { - s.Require().False(lockIsMature) - } + s.Require().Equal(tc.expectedLockIsMature, lockIsMature) }) } } diff --git a/x/concentrated-liquidity/math/math.go b/x/concentrated-liquidity/math/math.go index d4f210afe0f..3bf2aca1fff 100644 --- a/x/concentrated-liquidity/math/math.go +++ b/x/concentrated-liquidity/math/math.go @@ -173,28 +173,25 @@ func GetNextSqrtPriceFromAmount1OutRoundingDown(sqrtPriceCurrent, liquidity, amo // GetLiquidityFromAmounts takes the current sqrtPrice and the sqrtPrice for the upper and lower ticks as well as the amounts of asset0 and asset1 // and returns the resulting liquidity from these inputs. -func GetLiquidityFromAmounts(sqrtPrice osmomath.BigDec, sqrtPriceA, sqrtPriceB sdk.Dec, amount0, amount1 sdk.Int) (liquidity sdk.Dec) { - sqrtPriceABigDec := osmomath.BigDecFromSDKDec(sqrtPriceA) - sqrtPriceBBigDec := osmomath.BigDecFromSDKDec(sqrtPriceB) - +func GetLiquidityFromAmounts(sqrtPrice osmomath.BigDec, sqrtPriceA, sqrtPriceB osmomath.BigDec, amount0, amount1 sdk.Int) (liquidity sdk.Dec) { // Reorder the prices so that sqrtPriceA is the smaller of the two. // todo: Remove this. - if sqrtPriceABigDec.GT(sqrtPriceBBigDec) { - sqrtPriceABigDec, sqrtPriceBBigDec = sqrtPriceBBigDec, sqrtPriceABigDec + if sqrtPriceA.GT(sqrtPriceB) { + sqrtPriceA, sqrtPriceB = sqrtPriceB, sqrtPriceA } - if sqrtPrice.LTE(sqrtPriceABigDec) { + if sqrtPrice.LTE(sqrtPriceA) { // If the current price is less than or equal to the lower tick, then we use the liquidity0 formula. - liquidity = Liquidity0(amount0, sqrtPriceABigDec, sqrtPriceBBigDec) - } else if sqrtPrice.LT(sqrtPriceBBigDec) { + liquidity = Liquidity0(amount0, sqrtPriceA, sqrtPriceB) + } else if sqrtPrice.LT(sqrtPriceB) { // If the current price is between the lower and upper ticks (exclusive of both the lower and upper ticks, // as both would trigger a division by zero), then we use the minimum of the liquidity0 and liquidity1 formulas. - liquidity0 := Liquidity0(amount0, sqrtPrice, sqrtPriceBBigDec) - liquidity1 := Liquidity1(amount1, sqrtPrice, sqrtPriceABigDec) + liquidity0 := Liquidity0(amount0, sqrtPrice, sqrtPriceB) + liquidity1 := Liquidity1(amount1, sqrtPrice, sqrtPriceA) liquidity = sdk.MinDec(liquidity0, liquidity1) } else { // If the current price is greater than the upper tick, then we use the liquidity1 formula. - liquidity = Liquidity1(amount1, sqrtPriceBBigDec, sqrtPriceABigDec) + liquidity = Liquidity1(amount1, sqrtPriceB, sqrtPriceA) } return liquidity diff --git a/x/concentrated-liquidity/math/math_test.go b/x/concentrated-liquidity/math/math_test.go index a262e68dd3f..d56e2a4c3f8 100644 --- a/x/concentrated-liquidity/math/math_test.go +++ b/x/concentrated-liquidity/math/math_test.go @@ -268,8 +268,8 @@ func TestGetLiquidityFromAmounts(t *testing.T) { testCases := map[string]struct { currentSqrtP osmomath.BigDec - sqrtPHigh sdk.Dec - sqrtPLow sdk.Dec + sqrtPHigh osmomath.BigDec + sqrtPLow osmomath.BigDec // the amount of token0 that will need to be sold to move the price from P_cur to P_low amount0Desired sdk.Int // the amount of token 1 that will need to be sold to move the price from P_cur to P_high. @@ -283,40 +283,40 @@ func TestGetLiquidityFromAmounts(t *testing.T) { }{ "happy path (case A)": { currentSqrtP: osmomath.MustNewDecFromStr("67"), // 4489 - sqrtPHigh: sqrt5500, // 5500 - sqrtPLow: sqrt4545, // 4545 + sqrtPHigh: sqrt5500BigDec, // 5500 + sqrtPLow: sqrt4545BigDec, // 4545 amount0Desired: sdk.NewInt(1000000), amount1Desired: sdk.ZeroInt(), expectedLiquidity: "741212151.448720111852782017", }, "happy path (case A, but with sqrtPriceA greater than sqrtPriceB)": { currentSqrtP: osmomath.MustNewDecFromStr("67"), // 4489 - sqrtPHigh: sqrt4545, // 4545 - sqrtPLow: sqrt5500, // 5500 + sqrtPHigh: sqrt4545BigDec, // 4545 + sqrtPLow: sqrt5500BigDec, // 5500 amount0Desired: sdk.NewInt(1000000), amount1Desired: sdk.ZeroInt(), expectedLiquidity: "741212151.448720111852782017", }, "happy path (case B)": { currentSqrtP: sqrt5000BigDec, // 5000 - sqrtPHigh: sqrt5500, // 5500 - sqrtPLow: sqrt4545, // 4545 + sqrtPHigh: sqrt5500BigDec, // 5500 + sqrtPLow: sqrt4545BigDec, // 4545 amount0Desired: sdk.NewInt(1000000), amount1Desired: sdk.NewInt(5000000000), expectedLiquidity: "1517882343.751510418088349649", }, "happy path (case C)": { currentSqrtP: osmomath.MustNewDecFromStr("75"), // 5625 - sqrtPHigh: sqrt5500, // 5500 - sqrtPLow: sqrt4545, // 4545 + sqrtPHigh: sqrt5500BigDec, // 5500 + sqrtPLow: sqrt4545BigDec, // 4545 amount0Desired: sdk.ZeroInt(), amount1Desired: sdk.NewInt(5000000000), expectedLiquidity: "741249214.836069764856625637", }, "full range, price proportional to amounts, equal liquidities (some rounding error) price of 4": { currentSqrtP: sqrt(sdk.NewDec(4)), - sqrtPHigh: cltypes.MaxSqrtPrice, - sqrtPLow: cltypes.MinSqrtPrice, + sqrtPHigh: osmomath.BigDecFromSDKDec(cltypes.MaxSqrtPrice), + sqrtPLow: osmomath.BigDecFromSDKDec(cltypes.MinSqrtPrice), amount0Desired: sdk.NewInt(4), amount1Desired: sdk.NewInt(16), @@ -326,8 +326,8 @@ func TestGetLiquidityFromAmounts(t *testing.T) { }, "full range, price proportional to amounts, equal liquidities (some rounding error) price of 2": { currentSqrtP: sqrt(sdk.NewDec(2)), - sqrtPHigh: cltypes.MaxSqrtPrice, - sqrtPLow: cltypes.MinSqrtPrice, + sqrtPHigh: osmomath.BigDecFromSDKDec(cltypes.MaxSqrtPrice), + sqrtPLow: osmomath.BigDecFromSDKDec(cltypes.MinSqrtPrice), amount0Desired: sdk.NewInt(1), amount1Desired: sdk.NewInt(2), @@ -337,8 +337,8 @@ func TestGetLiquidityFromAmounts(t *testing.T) { }, "not full range, price proportional to amounts, non equal liquidities": { currentSqrtP: sqrt(sdk.NewDec(2)), - sqrtPHigh: sqrt(sdk.NewDec(3)).SDKDec(), - sqrtPLow: sqrt(sdk.NewDec(1)).SDKDec(), + sqrtPHigh: sqrt(sdk.NewDec(3)), + sqrtPLow: sqrt(sdk.NewDec(1)), amount0Desired: sdk.NewInt(1), amount1Desired: sdk.NewInt(2), @@ -348,8 +348,8 @@ func TestGetLiquidityFromAmounts(t *testing.T) { }, "current sqrt price on upper bound": { currentSqrtP: sqrt5500BigDec, - sqrtPHigh: sqrt5500, - sqrtPLow: sqrt4545, + sqrtPHigh: sqrt5500BigDec, + sqrtPLow: sqrt4545BigDec, amount0Desired: sdk.ZeroInt(), amount1Desired: sdk.NewInt(1000000), // Liquidity1 = amount1 / (sqrtPriceB - sqrtPriceA) diff --git a/x/concentrated-liquidity/math/tick.go b/x/concentrated-liquidity/math/tick.go index 5888ce1febf..eeddc4c595e 100644 --- a/x/concentrated-liquidity/math/tick.go +++ b/x/concentrated-liquidity/math/tick.go @@ -13,79 +13,62 @@ import ( // TicksToSqrtPrice returns the sqrtPrice for the lower and upper ticks by // individually calling `TickToSqrtPrice` method. // Returns error if fails to calculate price. -func TicksToSqrtPrice(lowerTick, upperTick int64) (sdk.Dec, sdk.Dec, sdk.Dec, sdk.Dec, error) { +func TicksToSqrtPrice(lowerTick, upperTick int64) (osmomath.BigDec, osmomath.BigDec, error) { if lowerTick >= upperTick { - return sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, types.InvalidLowerUpperTickError{LowerTick: lowerTick, UpperTick: upperTick} + return osmomath.BigDec{}, osmomath.BigDec{}, types.InvalidLowerUpperTickError{LowerTick: lowerTick, UpperTick: upperTick} } - priceUpperTick, sqrtPriceUpperTick, err := TickToSqrtPrice(upperTick) + _, sqrtPriceUpperTick, err := TickToSqrtPrice(upperTick) if err != nil { - return sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, err + return osmomath.BigDec{}, osmomath.BigDec{}, err } - priceLowerTick, sqrtPriceLowerTick, err := TickToSqrtPrice(lowerTick) + _, sqrtPriceLowerTick, err := TickToSqrtPrice(lowerTick) if err != nil { - return sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, err + return osmomath.BigDec{}, osmomath.BigDec{}, err } - return priceLowerTick, priceUpperTick, sqrtPriceLowerTick, sqrtPriceUpperTick, nil + return sqrtPriceLowerTick, sqrtPriceUpperTick, nil } // TickToSqrtPrice returns the sqrtPrice given a tickIndex // If tickIndex is zero, the function returns sdk.OneDec(). // It is the combination of calling TickToPrice followed by Sqrt. -func TickToSqrtPrice(tickIndex int64) (sdk.Dec, sdk.Dec, error) { - price, err := TickToPrice(tickIndex) +func TickToSqrtPrice(tickIndex int64) (osmomath.BigDec, osmomath.BigDec, error) { + priceBigDec, err := TickToPrice(tickIndex) if err != nil { - return sdk.Dec{}, sdk.Dec{}, err + return osmomath.BigDec{}, osmomath.BigDec{}, err } - // Determine the sqrtPrice from the price - sqrtPrice, err := osmomath.MonotonicSqrt(price) - if err != nil { - return sdk.Dec{}, sdk.Dec{}, err - } - return price, sqrtPrice, nil -} - -// TickToSqrtPriceBigDec returns the sqrtPrice given a tickIndex -// as a BigDec. -func TickToSqrtPriceBigDec(tickIndex int64) (osmomath.BigDec, error) { - price, err := TickToPrice(tickIndex) - if err != nil { - return osmomath.BigDec{}, err - } + // It is acceptable to truncate here as TickToPrice() function converts + // from sdk.Dec to osmomath.BigDec before returning. + price := priceBigDec.SDKDec() // Determine the sqrtPrice from the price sqrtPrice, err := osmomath.MonotonicSqrt(price) if err != nil { - return osmomath.BigDec{}, err + return osmomath.BigDec{}, osmomath.BigDec{}, err } - return osmomath.BigDecFromSDKDec(sqrtPrice), nil + return osmomath.BigDecFromSDKDec(price), osmomath.BigDecFromSDKDec(sqrtPrice), nil } // TickToPrice returns the price given a tickIndex // If tickIndex is zero, the function returns sdk.OneDec(). -func TickToPrice(tickIndex int64) (price sdk.Dec, err error) { +func TickToPrice(tickIndex int64) (osmomath.BigDec, error) { if tickIndex == 0 { - return sdk.OneDec(), nil + return osmomath.OneDec(), nil } - // The formula is as follows: geometricExponentIncrementDistanceInTicks = 9 * 10**(-exponentAtPriceOne) - // Due to sdk.Power restrictions, if the resulting power is negative, we take 9 * (1/10**exponentAtPriceOne) - exponentAtPriceOne := types.ExponentAtPriceOne - geometricExponentIncrementDistanceInTicks := sdkNineDec.Mul(PowTenInternal(-exponentAtPriceOne)).TruncateInt64() - // Check that the tick index is between min and max value if tickIndex < types.MinCurrentTick { - return sdk.Dec{}, types.TickIndexMinimumError{MinTick: types.MinCurrentTick} + return osmomath.BigDec{}, types.TickIndexMinimumError{MinTick: types.MinCurrentTick} } if tickIndex > types.MaxTick { - return sdk.Dec{}, types.TickIndexMaximumError{MaxTick: types.MaxTick} + return osmomath.BigDec{}, types.TickIndexMaximumError{MaxTick: types.MaxTick} } - // Use floor division to determine what the geometricExponent is now (the delta) + // Use floor division to determine what the geometricExponent is now (the delta from the starting exponentAtPriceOne) geometricExponentDelta := tickIndex / geometricExponentIncrementDistanceInTicks // Calculate the exponentAtCurrentTick from the starting exponentAtPriceOne and the geometricExponentDelta - exponentAtCurrentTick := exponentAtPriceOne + geometricExponentDelta + exponentAtCurrentTick := types.ExponentAtPriceOne + geometricExponentDelta if tickIndex < 0 { // We must decrement the exponentAtCurrentTick when entering the negative tick range in order to constantly step up in precision when going further down in ticks // Otherwise, from tick 0 to tick -(geometricExponentIncrementDistanceInTicks), we would use the same exponent as the exponentAtPriceOne @@ -100,14 +83,14 @@ func TickToPrice(tickIndex int64) (price sdk.Dec, err error) { numAdditiveTicks := tickIndex - (geometricExponentDelta * geometricExponentIncrementDistanceInTicks) // Finally, we can calculate the price - price = PowTenInternal(geometricExponentDelta).Add(osmomath.NewBigDec(numAdditiveTicks).Mul(currentAdditiveIncrementInTicks).SDKDec()) + price := PowTenInternal(geometricExponentDelta).Add(osmomath.NewBigDec(numAdditiveTicks).Mul(currentAdditiveIncrementInTicks).SDKDec()) // defense in depth, this logic would not be reached due to use having checked if given tick is in between // min tick and max tick. if price.GT(types.MaxSpotPrice) || price.LT(types.MinSpotPrice) { - return sdk.Dec{}, types.PriceBoundError{ProvidedPrice: price, MinSpotPrice: types.MinSpotPrice, MaxSpotPrice: types.MaxSpotPrice} + return osmomath.BigDec{}, types.PriceBoundError{ProvidedPrice: price, MinSpotPrice: types.MinSpotPrice, MaxSpotPrice: types.MaxSpotPrice} } - return price, nil + return osmomath.BigDecFromSDKDec(price), nil } // RoundDownTickToSpacing rounds the tick index down to the nearest tick spacing if the tickIndex is in between authorized tick values @@ -139,8 +122,8 @@ func RoundDownTickToSpacing(tickIndex int64, tickSpacing int64) (int64, error) { // SqrtPriceToTickRoundDown converts the given sqrt price to its corresponding tick rounded down // to the nearest tick spacing. -func SqrtPriceToTickRoundDownSpacing(sqrtPrice sdk.Dec, tickSpacing uint64) (int64, error) { - tickIndex, err := CalculateSqrtPriceToTick(osmomath.BigDecFromSDKDec(sqrtPrice)) +func SqrtPriceToTickRoundDownSpacing(sqrtPrice osmomath.BigDec, tickSpacing uint64) (int64, error) { + tickIndex, err := CalculateSqrtPriceToTick(sqrtPrice) if err != nil { return 0, err } @@ -169,7 +152,11 @@ func powTenBigDec(exponent int64) osmomath.BigDec { return bigNegPowersOfTen[-exponent] } -func CalculatePriceToTickDec(price sdk.Dec) (tickIndex sdk.Dec, err error) { +func CalculatePriceToTickDec(priceBigDec osmomath.BigDec) (tickIndex sdk.Dec, err error) { + // It is acceptable to truncate price as the minimum we support is + // 10**-12 which is above the smallest value of sdk.Dec. + price := priceBigDec.SDKDec() + if price.IsNegative() { return sdk.ZeroDec(), fmt.Errorf("price must be greater than zero") } @@ -225,11 +212,7 @@ func CalculateSqrtPriceToTick(sqrtPrice osmomath.BigDec) (tickIndex int64, err e // and move it in a +/- 1 tick range based on the sqrt price those ticks would imply. price := sqrtPrice.Mul(sqrtPrice) - // It is acceptable to truncate price as the minimum we support is - // 10**-12 which is above the smallest value of sdk.Dec. - priceDec := price.SDKDec() - - tick, err := CalculatePriceToTickDec(priceDec) + tick, err := CalculatePriceToTickDec(price) if err != nil { return 0, err } @@ -257,10 +240,10 @@ func CalculateSqrtPriceToTick(sqrtPrice osmomath.BigDec) (tickIndex int64, err e outOfBounds = true } - sqrtPriceTmin1, errM1 := TickToSqrtPriceBigDec(truncatedTick - 1) - sqrtPriceT, errT := TickToSqrtPriceBigDec(truncatedTick) - sqrtPriceTplus1, errP1 := TickToSqrtPriceBigDec(truncatedTick + 1) - sqrtPriceTplus2, errP2 := TickToSqrtPriceBigDec(truncatedTick + 2) + _, sqrtPriceTmin1, errM1 := TickToSqrtPrice(truncatedTick - 1) + _, sqrtPriceT, errT := TickToSqrtPrice(truncatedTick) + _, sqrtPriceTplus1, errP1 := TickToSqrtPrice(truncatedTick + 1) + _, sqrtPriceTplus2, errP2 := TickToSqrtPrice(truncatedTick + 2) if errM1 != nil || errT != nil || errP1 != nil || errP2 != nil { return 0, errors.New("internal error in computing square roots within CalculateSqrtPriceToTick") } diff --git a/x/concentrated-liquidity/math/tick_test.go b/x/concentrated-liquidity/math/tick_test.go index 954315a6bc5..3bfc63391ef 100644 --- a/x/concentrated-liquidity/math/tick_test.go +++ b/x/concentrated-liquidity/math/tick_test.go @@ -23,7 +23,7 @@ var ( ) // testing helper for price to tick, state machine only implements sqrt price to tick. -func PriceToTick(price sdk.Dec) (int64, error) { +func PriceToTick(price osmomath.BigDec) (int64, error) { tickDec, err := math.CalculatePriceToTickDec(price) tickIndex := tickDec.TruncateInt64() return tickIndex, err @@ -31,7 +31,7 @@ func PriceToTick(price sdk.Dec) (int64, error) { // testing helper for price to tick round down spacing, // state machine only implements sqrt price to tick round dow spacing. -func PriceToTickRoundDownSpacing(price sdk.Dec, tickSpacing uint64) (int64, error) { +func PriceToTickRoundDownSpacing(price osmomath.BigDec, tickSpacing uint64) (int64, error) { tickIndex, err := PriceToTick(price) if err != nil { return 0, err @@ -217,8 +217,8 @@ func TestTickToSqrtPrice(t *testing.T) { expectedSqrtPrice, err := osmomath.MonotonicSqrt(tc.expectedPrice) require.NoError(t, err) - require.Equal(t, tc.expectedPrice.String(), price.String()) - require.Equal(t, expectedSqrtPrice.String(), sqrtPrice.String()) + require.Equal(t, osmomath.BigDecFromSDKDec(tc.expectedPrice).String(), price.String()) + require.Equal(t, osmomath.BigDecFromSDKDec(expectedSqrtPrice).String(), sqrtPrice.String()) }) } } @@ -281,7 +281,7 @@ func TestTicksToSqrtPrice(t *testing.T) { for name, tc := range testCases { tc := tc t.Run(name, func(t *testing.T) { - priceLower, priceUpper, lowerSqrtPrice, upperSqrtPrice, err := math.TicksToSqrtPrice(tc.lowerTickIndex.Int64(), tc.upperTickIndex.Int64()) + lowerSqrtPrice, upperSqrtPrice, err := math.TicksToSqrtPrice(tc.lowerTickIndex.Int64(), tc.upperTickIndex.Int64()) if tc.expectedError != nil { require.Error(t, err) require.Equal(t, tc.expectedError.Error(), err.Error()) @@ -295,10 +295,8 @@ func TestTicksToSqrtPrice(t *testing.T) { expectedUpperSqrtPrice, err := osmomath.MonotonicSqrt(tc.expectedUpperPrice) require.NoError(t, err) - require.Equal(t, tc.expectedLowerPrice.String(), priceLower.String()) - require.Equal(t, tc.expectedUpperPrice.String(), priceUpper.String()) - require.Equal(t, expectedLowerSqrtPrice.String(), lowerSqrtPrice.String()) - require.Equal(t, expectedUpperSqrtPrice.String(), upperSqrtPrice.String()) + require.Equal(t, osmomath.BigDecFromSDKDec(expectedLowerSqrtPrice).String(), lowerSqrtPrice.String()) + require.Equal(t, osmomath.BigDecFromSDKDec(expectedUpperSqrtPrice).String(), upperSqrtPrice.String()) }) } } @@ -309,88 +307,88 @@ func TestPriceToTick(t *testing.T) { ) testCases := map[string]struct { - price sdk.Dec + price osmomath.BigDec tickExpected int64 expectedError error }{ "BTC <> USD, tick 38035200 -> price 30352": { - price: sdk.MustNewDecFromStr("30352"), + price: osmomath.MustNewDecFromStr("30352"), tickExpected: 38035200, }, "BTC <> USD, tick 38035300 + 100 -> price 30353": { - price: sdk.MustNewDecFromStr("30353"), + price: osmomath.MustNewDecFromStr("30353"), tickExpected: 38035300, }, "SHIB <> USD, tick -44821000 -> price 0.000011790": { - price: sdk.MustNewDecFromStr("0.000011790"), + price: osmomath.MustNewDecFromStr("0.000011790"), tickExpected: -44821000, }, "SHIB <> USD, tick -44820900 -> price 0.000011791": { - price: sdk.MustNewDecFromStr("0.000011791"), + price: osmomath.MustNewDecFromStr("0.000011791"), tickExpected: -44820900, }, "ETH <> BTC, tick -12104000 -> price 0.068960": { - price: sdk.MustNewDecFromStr("0.068960"), + price: osmomath.MustNewDecFromStr("0.068960"), tickExpected: -12104000, }, "ETH <> BTC, tick -12104000 + 100 -> price 0.068961": { - price: sdk.MustNewDecFromStr("0.068961"), + price: osmomath.MustNewDecFromStr("0.068961"), tickExpected: -12103900, }, "max sqrt price -1, max neg tick six - 100 -> max tick neg six - 100": { - price: sdk.MustNewDecFromStr("99999000000000000000000000000000000000"), + price: osmomath.MustNewDecFromStr("99999000000000000000000000000000000000"), tickExpected: types.MaxTick - 100, }, "max sqrt price, max tick neg six -> max spot price": { - price: types.MaxSqrtPrice.Power(2), + price: osmomath.BigDecFromSDKDec(types.MaxSqrtPrice.Power(2)), tickExpected: types.MaxTick, }, "Gyen <> USD, tick -20594000 -> price 0.0074060": { - price: sdk.MustNewDecFromStr("0.007406"), + price: osmomath.MustNewDecFromStr("0.007406"), tickExpected: -20594000, }, "Gyen <> USD, tick -20594000 + 100 -> price 0.0074061": { - price: sdk.MustNewDecFromStr("0.0074061"), + price: osmomath.MustNewDecFromStr("0.0074061"), tickExpected: -20593900, }, "Spell <> USD, tick -29204000 -> price 0.00077960": { - price: sdk.MustNewDecFromStr("0.0007796"), + price: osmomath.MustNewDecFromStr("0.0007796"), tickExpected: -29204000, }, "Spell <> USD, tick -29204000 + 100 -> price 0.00077961": { - price: sdk.MustNewDecFromStr("0.00077961"), + price: osmomath.MustNewDecFromStr("0.00077961"), tickExpected: -29203900, }, "Atom <> Osmo, tick -12150000 -> price 0.068500": { - price: sdk.MustNewDecFromStr("0.0685"), + price: osmomath.MustNewDecFromStr("0.0685"), tickExpected: -12150000, }, "Atom <> Osmo, tick -12150000 + 100 -> price 0.068501": { - price: sdk.MustNewDecFromStr("0.068501"), + price: osmomath.MustNewDecFromStr("0.068501"), tickExpected: -12149900, }, "Boot <> Osmo, tick 64576000 -> price 25760000": { - price: sdk.MustNewDecFromStr("25760000"), + price: osmomath.MustNewDecFromStr("25760000"), tickExpected: 64576000, }, "Boot <> Osmo, tick 64576000 + 100 -> price 25761000": { - price: sdk.MustNewDecFromStr("25761000"), + price: osmomath.MustNewDecFromStr("25761000"), tickExpected: 64576100, }, "price is one Dec": { - price: sdk.OneDec(), + price: osmomath.OneDec(), tickExpected: 0, }, "price is negative decimal": { - price: sdk.OneDec().Neg(), + price: osmomath.OneDec().Neg(), expectedError: fmt.Errorf("price must be greater than zero"), }, "price is greater than max spot price": { - price: types.MaxSpotPrice.Add(sdk.OneDec()), + price: osmomath.BigDecFromSDKDec(types.MaxSpotPrice.Add(sdk.OneDec())), expectedError: types.PriceBoundError{ProvidedPrice: types.MaxSpotPrice.Add(sdk.OneDec()), MinSpotPrice: types.MinSpotPrice, MaxSpotPrice: types.MaxSpotPrice}, }, "price is smaller than min spot price": { - price: types.MinSpotPrice.Quo(sdk.NewDec(10)), + price: osmomath.BigDecFromSDKDec(types.MinSpotPrice.Quo(sdk.NewDec(10))), expectedError: types.PriceBoundError{ProvidedPrice: types.MinSpotPrice.Quo(sdk.NewDec(10)), MinSpotPrice: types.MinSpotPrice, MaxSpotPrice: types.MaxSpotPrice}, }, } @@ -417,64 +415,64 @@ func TestPriceToTick(t *testing.T) { func TestPriceToTickRoundDown(t *testing.T) { testCases := map[string]struct { - price sdk.Dec + price osmomath.BigDec tickSpacing uint64 tickExpected int64 }{ "tick spacing 100, price of 1": { - price: sdk.OneDec(), + price: osmomath.OneDec(), tickSpacing: defaultTickSpacing, tickExpected: 0, }, "tick spacing 100, price of 1.000030, tick 30 -> 0": { - price: sdk.MustNewDecFromStr("1.000030"), + price: osmomath.MustNewDecFromStr("1.000030"), tickSpacing: defaultTickSpacing, tickExpected: 0, }, "tick spacing 100, price of 0.9999970, tick -30 -> -100": { - price: sdk.MustNewDecFromStr("0.9999970"), + price: osmomath.MustNewDecFromStr("0.9999970"), tickSpacing: defaultTickSpacing, tickExpected: -100, }, "tick spacing 50, price of 0.9999730, tick -270 -> -300": { - price: sdk.MustNewDecFromStr("0.9999730"), + price: osmomath.MustNewDecFromStr("0.9999730"), tickSpacing: 50, tickExpected: -300, }, "tick spacing 100, MinSpotPrice, MinTick": { - price: types.MinSpotPrice, + price: osmomath.BigDecFromSDKDec(types.MinSpotPrice), tickSpacing: defaultTickSpacing, tickExpected: types.MinInitializedTick, }, "tick spacing 100, Spot price one tick above min, one tick above min -> MinTick": { - price: types.MinSpotPrice.Add(sdk.SmallestDec()), + price: osmomath.BigDecFromSDKDec(types.MinSpotPrice.Add(sdk.SmallestDec())), tickSpacing: defaultTickSpacing, // Since the tick should always be the closest tick below (and `smallestDec` isn't sufficient // to push us into the next tick), we expect MinTick to be returned here. tickExpected: types.MinInitializedTick, }, "tick spacing 100, Spot price one tick below max, one tick below max -> MaxTick - 1": { - price: closestPriceBelowMaxPriceDefaultTickSpacing, + price: osmomath.BigDecFromSDKDec(closestPriceBelowMaxPriceDefaultTickSpacing), tickSpacing: defaultTickSpacing, tickExpected: types.MaxTick - 100, }, "tick spacing 100, Spot price 100_000_050 -> 72000000": { - price: sdk.NewDec(100_000_050), + price: osmomath.NewBigDec(100_000_050), tickSpacing: defaultTickSpacing, tickExpected: 72000000, }, "tick spacing 100, Spot price 100_000_051 -> 72000100 (rounded up to tick spacing)": { - price: sdk.NewDec(100_000_051), + price: osmomath.NewBigDec(100_000_051), tickSpacing: defaultTickSpacing, tickExpected: 72000000, }, "tick spacing 1, Spot price 100_000_051 -> 72000000 no tick spacing rounding": { - price: sdk.NewDec(100_000_051), + price: osmomath.NewBigDec(100_000_051), tickSpacing: 1, tickExpected: 72000000, }, "tick spacing 1, Spot price 100_000_101 -> 72000001 no tick spacing rounding": { - price: sdk.NewDec(100_000_101), + price: osmomath.NewBigDec(100_000_101), tickSpacing: 1, tickExpected: 72000001, }, @@ -496,90 +494,90 @@ func TestPriceToTickRoundDown(t *testing.T) { // TODO: Revisit this test, under the lens of bucket index. func TestTickToSqrtPricePriceToTick_InverseRelationship(t *testing.T) { type testcase struct { - price sdk.Dec - truncatedPrice sdk.Dec + price osmomath.BigDec + truncatedPrice osmomath.BigDec tickExpected int64 } testCases := map[string]testcase{ "50000 to tick": { - price: sdk.MustNewDecFromStr("50000"), + price: osmomath.MustNewDecFromStr("50000"), tickExpected: 40000000, }, "5.01 to tick": { - price: sdk.MustNewDecFromStr("5.01"), + price: osmomath.MustNewDecFromStr("5.01"), tickExpected: 4010000, }, "50000.01 to tick": { - price: sdk.MustNewDecFromStr("50000.01"), + price: osmomath.MustNewDecFromStr("50000.01"), tickExpected: 40000001, }, "0.090001 to tick": { - price: sdk.MustNewDecFromStr("0.090001"), + price: osmomath.MustNewDecFromStr("0.090001"), tickExpected: -9999900, }, "0.9998 to tick": { - price: sdk.MustNewDecFromStr("0.9998"), + price: osmomath.MustNewDecFromStr("0.9998"), tickExpected: -2000, }, "53030 to tick": { - price: sdk.MustNewDecFromStr("53030"), + price: osmomath.MustNewDecFromStr("53030"), tickExpected: 40303000, }, "max spot price": { - price: types.MaxSpotPrice, + price: osmomath.BigDecFromSDKDec(types.MaxSpotPrice), tickExpected: types.MaxTick, }, "max spot price - smallest price delta given exponent at price one of -6": { // 37 - 6 is calculated by counting the exponent of max spot price and subtracting exponent at price one - price: types.MaxSpotPrice.Sub(sdk.NewDec(10).PowerMut(37 - 6)), + price: osmomath.BigDecFromSDKDec(types.MaxSpotPrice.Sub(sdk.NewDec(10).PowerMut(37 - 6))), tickExpected: types.MaxTick - 1, // still max }, "min spot price": { - price: types.MinSpotPrice, + price: osmomath.BigDecFromSDKDec(types.MinSpotPrice), tickExpected: types.MinInitializedTick, }, "smallest + min price + tick": { - price: sdk.MustNewDecFromStr("0.000000000001000001"), + price: osmomath.MustNewDecFromStr("0.000000000001000001"), tickExpected: types.MinInitializedTick + 1, }, "at price level of 0.01 - odd": { - price: sdk.MustNewDecFromStr("0.012345670000000000"), + price: osmomath.MustNewDecFromStr("0.012345670000000000"), tickExpected: -17765433, }, "at price level of 0.01 - even": { - price: sdk.MustNewDecFromStr("0.01234568000000000"), + price: osmomath.MustNewDecFromStr("0.01234568000000000"), tickExpected: -17765432, }, "at min price level of 0.01 - odd": { - price: sdk.MustNewDecFromStr("0.000000000001234567"), + price: osmomath.MustNewDecFromStr("0.000000000001234567"), tickExpected: -107765433, }, "at min price level of 0.01 - even": { - price: sdk.MustNewDecFromStr("0.000000000001234568"), + price: osmomath.MustNewDecFromStr("0.000000000001234568"), tickExpected: -107765432, }, "at price level of 1_000_000_000 - odd end": { - price: sdk.MustNewDecFromStr("1234567000"), + price: osmomath.MustNewDecFromStr("1234567000"), tickExpected: 81234567, }, "at price level of 1_000_000_000 - in-between supported": { - price: sdk.MustNewDecFromStr("1234567500"), + price: osmomath.MustNewDecFromStr("1234567500"), tickExpected: 81234567, - truncatedPrice: sdk.MustNewDecFromStr("1234567000"), + truncatedPrice: osmomath.MustNewDecFromStr("1234567000"), }, "at price level of 1_000_000_000 - even end": { - price: sdk.MustNewDecFromStr("1234568000"), + price: osmomath.MustNewDecFromStr("1234568000"), tickExpected: 81234568, }, "inverse testing with 1": { - price: sdk.OneDec(), + price: osmomath.OneDec(), tickExpected: 0, }, } var powTen int64 = 10 for i := 1; i < 13; i++ { testCases[fmt.Sprintf("min spot price * 10^%d", i)] = testcase{ - price: types.MinSpotPrice.MulInt64(powTen), + price: osmomath.BigDecFromSDKDec(types.MinSpotPrice.MulInt64(powTen)), tickExpected: types.MinInitializedTick + (int64(i) * 9e6), } powTen *= 10 @@ -618,7 +616,7 @@ func TestTickToSqrtPricePriceToTick_InverseRelationship(t *testing.T) { // require.Equal(t, expectedPrice.String(), priceFromSqrtPrice.String()) // 5. Compute tick from sqrt price from the original tick. - inverseTickFromSqrtPrice, err := math.CalculateSqrtPriceToTick(osmomath.BigDecFromSDKDec(sqrtPrice)) + inverseTickFromSqrtPrice, err := math.CalculateSqrtPriceToTick(sqrtPrice) require.NoError(t, err) require.Equal(t, tickFromPrice, inverseTickFromSqrtPrice, "expected: %s, actual: %s", tickFromPrice, inverseTickFromSqrtPrice) @@ -628,16 +626,16 @@ func TestTickToSqrtPricePriceToTick_InverseRelationship(t *testing.T) { func TestPriceToTick_ErrorCases(t *testing.T) { testCases := map[string]struct { - price sdk.Dec + price osmomath.BigDec }{ "use negative price": { - price: sdk.OneDec().Neg(), + price: osmomath.OneDec().Neg(), }, "price is greater than max spot price": { - price: types.MaxSpotPrice.Add(sdk.OneDec()), + price: osmomath.BigDecFromSDKDec(types.MaxSpotPrice.Add(sdk.OneDec())), }, "price is less than min spot price": { - price: types.MinSpotPrice.Sub(sdk.OneDec()), + price: osmomath.BigDecFromSDKDec(types.MinSpotPrice.Sub(sdk.OneDec())), }, } for name, tc := range testCases { @@ -673,31 +671,31 @@ func TestTickToPrice_ErrorCases(t *testing.T) { func TestCalculatePriceToTick(t *testing.T) { testCases := map[string]struct { - price sdk.Dec + price osmomath.BigDec expectedTickIndex int64 }{ "Price greater than 1": { - price: sdk.MustNewDecFromStr("9.78"), + price: osmomath.MustNewDecFromStr("9.78"), expectedTickIndex: 8780000, }, "Price less than 1": { - price: sdk.MustNewDecFromStr("0.71"), + price: osmomath.MustNewDecFromStr("0.71"), expectedTickIndex: -2900000, }, "100_000_000 -> 72000000": { - price: sdk.NewDec(100_000_000), + price: osmomath.NewBigDec(100_000_000), expectedTickIndex: 72000000, }, "100_000_050 -> 72000000": { - price: sdk.NewDec(100_000_050), + price: osmomath.NewBigDec(100_000_050), expectedTickIndex: 72000000, }, "100_000_051 -> 72000000": { - price: sdk.NewDec(100_000_051), + price: osmomath.NewBigDec(100_000_051), expectedTickIndex: 72000000, }, "100_000_100 -> 72000001": { - price: sdk.NewDec(100_000_100), + price: osmomath.NewBigDec(100_000_100), expectedTickIndex: 72000001, }, } @@ -736,6 +734,8 @@ func TestPowTenInternal(t *testing.T) { } func TestSqrtPriceToTickRoundDownSpacing(t *testing.T) { + sdkULP := osmomath.BigDecFromSDKDec(sdk.SmallestDec()) + // Compute reference values that need to be satisfied _, sqp1, err := math.TickToSqrtPrice(1) require.NoError(t, err) @@ -755,12 +755,12 @@ func TestSqrtPriceToTickRoundDownSpacing(t *testing.T) { require.NoError(t, err) testCases := map[string]struct { - sqrtPrice sdk.Dec + sqrtPrice osmomath.BigDec tickSpacing uint64 tickExpected int64 }{ "sqrt price of 1 (tick spacing 1)": { - sqrtPrice: sdk.OneDec(), + sqrtPrice: osmomath.OneDec(), tickSpacing: 1, tickExpected: 0, }, @@ -770,7 +770,7 @@ func TestSqrtPriceToTickRoundDownSpacing(t *testing.T) { tickExpected: 1, }, "sqrt price one ULP below boundary of next tick (tick spacing 1)": { - sqrtPrice: sqp1.Sub(sdk.SmallestDec()), + sqrtPrice: sqp1.Sub(sdkULP), tickSpacing: 1, tickExpected: 0, }, @@ -785,7 +785,7 @@ func TestSqrtPriceToTickRoundDownSpacing(t *testing.T) { tickExpected: 100, }, "sqrt price one ULP below bucket 100 (tick spacing 100)": { - sqrtPrice: sqp100.Sub(sdk.SmallestDec()), + sqrtPrice: sqp100.Sub(sdkULP), tickSpacing: defaultTickSpacing, tickExpected: 0, }, @@ -795,7 +795,7 @@ func TestSqrtPriceToTickRoundDownSpacing(t *testing.T) { tickExpected: -100, }, "sqrt price one ULP below bucket -100 (tick spacing 100)": { - sqrtPrice: sqpn100.Sub(sdk.SmallestDec()), + sqrtPrice: sqpn100.Sub(sdkULP), tickSpacing: defaultTickSpacing, tickExpected: -200, }, @@ -805,17 +805,17 @@ func TestSqrtPriceToTickRoundDownSpacing(t *testing.T) { tickExpected: -200, }, "sqrt price exactly equal to max sqrt price": { - sqrtPrice: types.MaxSqrtPrice, + sqrtPrice: osmomath.BigDecFromSDKDec(types.MaxSqrtPrice), tickSpacing: defaultTickSpacing, tickExpected: types.MaxTick, }, "sqrt price exactly equal to min sqrt price": { - sqrtPrice: types.MinSqrtPrice, + sqrtPrice: osmomath.BigDecFromSDKDec(types.MinSqrtPrice), tickSpacing: defaultTickSpacing, tickExpected: types.MinInitializedTick, }, "sqrt price equal to max sqrt price minus one ULP": { - sqrtPrice: types.MaxSqrtPrice.Sub(sdk.SmallestDec()), + sqrtPrice: osmomath.BigDecFromSDKDec(types.MaxSqrtPrice).Sub(sdkULP), tickSpacing: defaultTickSpacing, tickExpected: types.MaxTick - defaultTickSpacing, }, @@ -825,12 +825,12 @@ func TestSqrtPriceToTickRoundDownSpacing(t *testing.T) { tickExpected: types.MaxTick - 1, }, "sqrt price one ULP below max tick - 1 (tick spacing 1)": { - sqrtPrice: sqpMaxTickSubOne.Sub(sdk.SmallestDec()), + sqrtPrice: sqpMaxTickSubOne.Sub(sdkULP), tickSpacing: 1, tickExpected: types.MaxTick - 2, }, "sqrt price one ULP below max tick - 1 (tick spacing 100)": { - sqrtPrice: sqpMaxTickSubOne.Sub(sdk.SmallestDec()), + sqrtPrice: sqpMaxTickSubOne.Sub(sdkULP), tickSpacing: defaultTickSpacing, tickExpected: types.MaxTick - defaultTickSpacing, }, @@ -841,13 +841,13 @@ func TestSqrtPriceToTickRoundDownSpacing(t *testing.T) { }, "sqrt price corresponds exactly to min tick + 1 minus 1 ULP (tick spacing 1)": { // Calculated using TickToSqrtPrice(types.MinInitializedTick + 1) - 1 ULP - sqrtPrice: sqpMinTickPlusOne.Sub(sdk.SmallestDec()), + sqrtPrice: sqpMinTickPlusOne.Sub(sdkULP), tickSpacing: 1, tickExpected: types.MinInitializedTick, }, "sqrt price corresponds exactly to min tick + 1 plus 1 ULP (tick spacing 1)": { // Calculated using TickToSqrtPrice(types.MinInitializedTick + 1) + 1 ULP - sqrtPrice: sqpMinTickPlusOne.Add(sdk.SmallestDec()), + sqrtPrice: sqpMinTickPlusOne.Add(sdkULP), tickSpacing: 1, tickExpected: types.MinInitializedTick + 1, }, @@ -858,13 +858,13 @@ func TestSqrtPriceToTickRoundDownSpacing(t *testing.T) { }, "sqrt price corresponds exactly to min tick + 2 plus 1 ULP (tick spacing 1)": { // Calculated using TickToSqrtPrice(types.MinInitializedTick + 2) + 1 ULP - sqrtPrice: sqpMinTickPlusTwo.Add(sdk.SmallestDec()), + sqrtPrice: sqpMinTickPlusTwo.Add(sdkULP), tickSpacing: 1, tickExpected: types.MinInitializedTick + 2, }, "sqrt price corresponds exactly to min tick + 2 minus 1 ULP (tick spacing 1)": { // Calculated using TickToSqrtPrice(types.MinInitializedTick + 2) - 1 ULP - sqrtPrice: sqpMinTickPlusTwo.Sub(sdk.SmallestDec()), + sqrtPrice: sqpMinTickPlusTwo.Sub(sdkULP), tickSpacing: 1, tickExpected: types.MinInitializedTick + 1, }, diff --git a/x/concentrated-liquidity/model/pool.go b/x/concentrated-liquidity/model/pool.go index 8d0d825b26e..93a488112ee 100644 --- a/x/concentrated-liquidity/model/pool.go +++ b/x/concentrated-liquidity/model/pool.go @@ -228,7 +228,7 @@ func (p Pool) CalcActualAmounts(ctx sdk.Context, lowerTick, upperTick int64, liq } // Transform the provided ticks into their corresponding sqrtPrices. - _, _, sqrtPriceLowerTick, sqrtPriceUpperTick, err := math.TicksToSqrtPrice(lowerTick, upperTick) + sqrtPriceLowerTick, sqrtPriceUpperTick, err := math.TicksToSqrtPrice(lowerTick, upperTick) if err != nil { return sdk.Dec{}, sdk.Dec{}, err } @@ -242,9 +242,7 @@ func (p Pool) CalcActualAmounts(ctx sdk.Context, lowerTick, upperTick int64, liq roundUp := liquidityDelta.IsPositive() var ( - liquidityDeltaBigDec = osmomath.BigDecFromSDKDec(liquidityDelta) - sqrtPriceLowerTickBigDec = osmomath.BigDecFromSDKDec(sqrtPriceLowerTick) - sqrtPriceUpperTickBigDec = osmomath.BigDecFromSDKDec(sqrtPriceUpperTick) + liquidityDeltaBigDec = osmomath.BigDecFromSDKDec(liquidityDelta) actualAmountDenom0 osmomath.BigDec actualAmountDenom1 osmomath.BigDec @@ -255,18 +253,18 @@ func (p Pool) CalcActualAmounts(ctx sdk.Context, lowerTick, upperTick int64, liq // if this is the case, we attempt to provide liquidity evenly between asset0 and asset1 // we also update the pool liquidity since the virtual liquidity is modified by this position's creation currentSqrtPrice := p.CurrentSqrtPrice - actualAmountDenom0 = math.CalcAmount0Delta(liquidityDeltaBigDec, currentSqrtPrice, sqrtPriceUpperTickBigDec, roundUp) - actualAmountDenom1 = math.CalcAmount1Delta(liquidityDeltaBigDec, currentSqrtPrice, sqrtPriceLowerTickBigDec, roundUp) + actualAmountDenom0 = math.CalcAmount0Delta(liquidityDeltaBigDec, currentSqrtPrice, sqrtPriceUpperTick, roundUp) + actualAmountDenom1 = math.CalcAmount1Delta(liquidityDeltaBigDec, currentSqrtPrice, sqrtPriceLowerTick, roundUp) } else if p.CurrentTick < lowerTick { // outcome two: position is below current price // this means position is solely made up of asset0 actualAmountDenom1 = osmomath.ZeroDec() - actualAmountDenom0 = math.CalcAmount0Delta(liquidityDeltaBigDec, sqrtPriceLowerTickBigDec, sqrtPriceUpperTickBigDec, roundUp) + actualAmountDenom0 = math.CalcAmount0Delta(liquidityDeltaBigDec, sqrtPriceLowerTick, sqrtPriceUpperTick, roundUp) } else { // outcome three: position is above current price // this means position is solely made up of asset1 actualAmountDenom0 = osmomath.ZeroDec() - actualAmountDenom1 = math.CalcAmount1Delta(liquidityDeltaBigDec, sqrtPriceLowerTickBigDec, sqrtPriceUpperTickBigDec, roundUp) + actualAmountDenom1 = math.CalcAmount1Delta(liquidityDeltaBigDec, sqrtPriceLowerTick, sqrtPriceUpperTick, roundUp) } if roundUp { diff --git a/x/concentrated-liquidity/model/pool_test.go b/x/concentrated-liquidity/model/pool_test.go index 99581fa387b..b234cfb7fc1 100644 --- a/x/concentrated-liquidity/model/pool_test.go +++ b/x/concentrated-liquidity/model/pool_test.go @@ -564,7 +564,7 @@ func (s *ConcentratedPoolTestSuite) TestNewConcentratedLiquidityPool() { func (s *ConcentratedPoolTestSuite) TestCalcActualAmounts() { var ( - tickToSqrtPrice = func(tick int64) sdk.Dec { + tickToSqrtPrice = func(tick int64) osmomath.BigDec { _, sqrtPrice, err := clmath.TickToSqrtPrice(tick) s.Require().NoError(err) return sqrtPrice @@ -574,13 +574,13 @@ func (s *ConcentratedPoolTestSuite) TestCalcActualAmounts() { defaultLiquidityDeltaBigDec = osmomath.NewBigDec(1000) lowerTick = int64(-99) - lowerSqrtPriceBigDec = osmomath.BigDecFromSDKDec(tickToSqrtPrice(lowerTick)) + lowerSqrtPriceBigDec = tickToSqrtPrice(lowerTick) midtick = int64(2) - midSqrtPriceBigDec = osmomath.BigDecFromSDKDec(tickToSqrtPrice(midtick)) + midSqrtPriceBigDec = tickToSqrtPrice(midtick) uppertick = int64(74) - upperSqrtPriceBigDec = osmomath.BigDecFromSDKDec(tickToSqrtPrice(uppertick)) + upperSqrtPriceBigDec = tickToSqrtPrice(uppertick) ) tests := map[string]struct { @@ -694,7 +694,7 @@ func (s *ConcentratedPoolTestSuite) TestCalcActualAmounts() { CurrentTick: tc.currentTick, } _, currenTicktSqrtPrice, _ := clmath.TickToSqrtPrice(pool.CurrentTick) - pool.CurrentSqrtPrice = osmomath.BigDecFromSDKDec(currenTicktSqrtPrice) + pool.CurrentSqrtPrice = currenTicktSqrtPrice actualAmount0, actualAmount1, err := pool.CalcActualAmounts(s.Ctx, tc.lowerTick, tc.upperTick, tc.liquidityDelta) @@ -789,7 +789,7 @@ func (s *ConcentratedPoolTestSuite) TestUpdateLiquidityIfActivePosition() { CurrentTickLiquidity: defaultLiquidityAmt, } _, currenTicktSqrtPrice, _ := clmath.TickToSqrtPrice(pool.CurrentTick) - pool.CurrentSqrtPrice = osmomath.BigDecFromSDKDec(currenTicktSqrtPrice) + pool.CurrentSqrtPrice = currenTicktSqrtPrice wasUpdated := pool.UpdateLiquidityIfActivePosition(s.Ctx, tc.lowerTick, tc.upperTick, tc.liquidityDelta) if tc.lowerTick <= tc.currentTick && tc.currentTick <= tc.upperTick { diff --git a/x/concentrated-liquidity/msg_server.go b/x/concentrated-liquidity/msg_server.go index b00fe0dd5ff..4d56bd56ade 100644 --- a/x/concentrated-liquidity/msg_server.go +++ b/x/concentrated-liquidity/msg_server.go @@ -52,7 +52,7 @@ func (server msgServer) CreatePosition(goCtx context.Context, msg *types.MsgCrea return nil, err } - positionId, actualAmount0, actualAmount1, liquidityCreated, lowerTick, upperTick, err := server.keeper.CreatePosition(ctx, msg.PoolId, sender, msg.TokensProvided, msg.TokenMinAmount0, msg.TokenMinAmount1, msg.LowerTick, msg.UpperTick) + positionData, err := server.keeper.CreatePosition(ctx, msg.PoolId, sender, msg.TokensProvided, msg.TokenMinAmount0, msg.TokenMinAmount1, msg.LowerTick, msg.UpperTick) if err != nil { return nil, err } @@ -67,7 +67,7 @@ func (server msgServer) CreatePosition(goCtx context.Context, msg *types.MsgCrea // Note: create position event is emitted in keeper.createPosition(...) - return &types.MsgCreatePositionResponse{PositionId: positionId, Amount0: actualAmount0, Amount1: actualAmount1, LiquidityCreated: liquidityCreated, LowerTick: lowerTick, UpperTick: upperTick}, nil + return &types.MsgCreatePositionResponse{PositionId: positionData.ID, Amount0: positionData.Amount0, Amount1: positionData.Amount1, LiquidityCreated: positionData.Liquidity, LowerTick: positionData.LowerTick, UpperTick: positionData.UpperTick}, nil } func (server msgServer) AddToPosition(goCtx context.Context, msg *types.MsgAddToPosition) (*types.MsgAddToPositionResponse, error) { diff --git a/x/concentrated-liquidity/pool.go b/x/concentrated-liquidity/pool.go index 9edf0e46455..67eb7b5bb50 100644 --- a/x/concentrated-liquidity/pool.go +++ b/x/concentrated-liquidity/pool.go @@ -42,15 +42,15 @@ func (k Keeper) InitializePool(ctx sdk.Context, poolI poolmanagertypes.PoolI, cr poolId := concentratedPool.GetId() quoteAsset := concentratedPool.GetToken1() - if !k.validateTickSpacing(ctx, params, tickSpacing) { + if !k.validateTickSpacing(params, tickSpacing) { return types.UnauthorizedTickSpacingError{ProvidedTickSpacing: tickSpacing, AuthorizedTickSpacings: params.AuthorizedTickSpacing} } - if !k.validateSpreadFactor(ctx, params, spreadFactor) { + if !k.validateSpreadFactor(params, spreadFactor) { return types.UnauthorizedSpreadFactorError{ProvidedSpreadFactor: spreadFactor, AuthorizedSpreadFactors: params.AuthorizedSpreadFactors} } - if !validateAuthorizedQuoteDenoms(ctx, quoteAsset, params.AuthorizedQuoteDenoms) { + if !validateAuthorizedQuoteDenoms(quoteAsset, params.AuthorizedQuoteDenoms) { return types.UnauthorizedQuoteDenomError{ProvidedQuoteDenom: quoteAsset, AuthorizedQuoteDenoms: params.AuthorizedQuoteDenoms} } @@ -270,7 +270,7 @@ func (k Keeper) DecreaseConcentratedPoolTickSpacing(ctx sdk.Context, poolIdToTic } params := k.GetParams(ctx) - if !k.validateTickSpacingUpdate(ctx, pool, params, poolIdToTickSpacingRecord.NewTickSpacing) { + if !k.validateTickSpacingUpdate(pool, params, poolIdToTickSpacingRecord.NewTickSpacing) { return fmt.Errorf("tick spacing %d is not valid", poolIdToTickSpacingRecord.NewTickSpacing) } @@ -285,7 +285,7 @@ func (k Keeper) DecreaseConcentratedPoolTickSpacing(ctx sdk.Context, poolIdToTic // validateTickSpacing returns true if the given tick spacing is one of the authorized tick spacings set in the // params. False otherwise. -func (k Keeper) validateTickSpacing(ctx sdk.Context, params types.Params, tickSpacing uint64) bool { +func (k Keeper) validateTickSpacing(params types.Params, tickSpacing uint64) bool { for _, authorizedTick := range params.AuthorizedTickSpacing { if tickSpacing == authorizedTick { return true @@ -296,7 +296,7 @@ func (k Keeper) validateTickSpacing(ctx sdk.Context, params types.Params, tickSp // validateTickSpacingUpdate returns true if the given tick spacing is one of the authorized tick spacings set in the // params and is less than the current tick spacing. False otherwise. -func (k Keeper) validateTickSpacingUpdate(ctx sdk.Context, pool types.ConcentratedPoolExtension, params types.Params, newTickSpacing uint64) bool { +func (k Keeper) validateTickSpacingUpdate(pool types.ConcentratedPoolExtension, params types.Params, newTickSpacing uint64) bool { currentTickSpacing := pool.GetTickSpacing() for _, authorizedTick := range params.AuthorizedTickSpacing { // New tick spacing must be one of the authorized tick spacings and must be less than the current tick spacing @@ -309,7 +309,7 @@ func (k Keeper) validateTickSpacingUpdate(ctx sdk.Context, pool types.Concentrat // validateSpreadFactor returns true if the given spread factor is one of the authorized spread factors set in the // params. False otherwise. -func (k Keeper) validateSpreadFactor(ctx sdk.Context, params types.Params, spreadFactor sdk.Dec) bool { +func (k Keeper) validateSpreadFactor(params types.Params, spreadFactor sdk.Dec) bool { for _, authorizedSpreadFactor := range params.AuthorizedSpreadFactors { if spreadFactor.Equal(authorizedSpreadFactor) { return true @@ -328,7 +328,7 @@ func (k Keeper) validateSpreadFactor(ctx sdk.Context, params types.Params, sprea // // Returns: // - bool: A boolean indicating if the denom1 is authorized or not. -func validateAuthorizedQuoteDenoms(ctx sdk.Context, denom1 string, authorizedQuoteDenoms []string) bool { +func validateAuthorizedQuoteDenoms(denom1 string, authorizedQuoteDenoms []string) bool { for _, authorizedQuoteDenom := range authorizedQuoteDenoms { if denom1 == authorizedQuoteDenom { return true diff --git a/x/concentrated-liquidity/pool_test.go b/x/concentrated-liquidity/pool_test.go index 96c0438812a..3aa101f768f 100644 --- a/x/concentrated-liquidity/pool_test.go +++ b/x/concentrated-liquidity/pool_test.go @@ -470,11 +470,11 @@ func (s *KeeperTestSuite) TestDecreaseConcentratedPoolTickSpacing() { concentratedPool := s.PrepareConcentratedPoolWithCoinsAndFullRangePosition(ETH, USDC) // Create a position in the pool that is divisible by the tick spacing - _, _, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, concentratedPool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), -100, 100) + _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, concentratedPool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), -100, 100) s.Require().NoError(err) // Attempt to create a position that is not divisible by the tick spacing - _, _, _, _, _, _, err = s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, concentratedPool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), test.position.lowerTick, test.position.upperTick) + _, err = s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, concentratedPool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), test.position.lowerTick, test.position.upperTick) s.Require().Error(err) // Alter the tick spacing of the pool @@ -487,7 +487,7 @@ func (s *KeeperTestSuite) TestDecreaseConcentratedPoolTickSpacing() { s.Require().NoError(err) // Attempt to create a position that was previously not divisible by the tick spacing but now is - _, _, _, _, _, _, err = s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, concentratedPool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), test.position.lowerTick, test.position.upperTick) + _, err = s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, concentratedPool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), test.position.lowerTick, test.position.upperTick) if test.expectedCreatePositionErr != nil { s.Require().Error(err) s.Require().ErrorContains(err, test.expectedCreatePositionErr.Error()) @@ -683,7 +683,7 @@ func (s *KeeperTestSuite) TestGetUserUnbondingPositions() { // Create 3 locked positions for i := 0; i < 3; i++ { - _, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPool.GetId(), defaultAddress, defaultFunds, time.Hour) + _, _, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPool.GetId(), defaultAddress, defaultFunds, time.Hour) s.Require().NoError(err) } diff --git a/x/concentrated-liquidity/position.go b/x/concentrated-liquidity/position.go index 8519c866d90..9d837fbfa77 100644 --- a/x/concentrated-liquidity/position.go +++ b/x/concentrated-liquidity/position.go @@ -5,7 +5,6 @@ import ( "fmt" "sort" "strconv" - "strings" "time" sdkprefix "github.com/cosmos/cosmos-sdk/store/prefix" @@ -393,78 +392,78 @@ func (k Keeper) deletePosition(ctx sdk.Context, // CreateFullRangePosition creates a full range (min to max tick) concentrated liquidity position for the given pool ID, owner, and coins. // The function returns the amounts of token 0 and token 1, and the liquidity created from the position. -func (k Keeper) CreateFullRangePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, coins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, err error) { +func (k Keeper) CreateFullRangePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, coins sdk.Coins) (types.CreateFullRangePositionData, error) { // Check that exactly two coins are provided. if len(coins) != 2 { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, types.NumCoinsError{NumCoins: len(coins)} + return types.CreateFullRangePositionData{}, types.NumCoinsError{NumCoins: len(coins)} } concentratedPool, err := k.GetConcentratedPoolById(ctx, poolId) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, err + return types.CreateFullRangePositionData{}, err } // Defense in depth, ensure coins provided match the pool's token denominations. if coins.AmountOf(concentratedPool.GetToken0()).LTE(sdk.ZeroInt()) { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, types.Amount0IsNegativeError{Amount0: coins.AmountOf(concentratedPool.GetToken0())} + return types.CreateFullRangePositionData{}, types.Amount0IsNegativeError{Amount0: coins.AmountOf(concentratedPool.GetToken0())} } if coins.AmountOf(concentratedPool.GetToken1()).LTE(sdk.ZeroInt()) { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, types.Amount1IsNegativeError{Amount1: coins.AmountOf(concentratedPool.GetToken1())} + return types.CreateFullRangePositionData{}, types.Amount1IsNegativeError{Amount1: coins.AmountOf(concentratedPool.GetToken1())} } // Create a full range (min to max tick) concentrated liquidity position. - positionId, amount0, amount1, liquidity, _, _, err = k.CreatePosition(ctx, concentratedPool.GetId(), owner, coins, sdk.ZeroInt(), sdk.ZeroInt(), types.MinInitializedTick, types.MaxTick) + positionData, err := k.CreatePosition(ctx, concentratedPool.GetId(), owner, coins, sdk.ZeroInt(), sdk.ZeroInt(), types.MinInitializedTick, types.MaxTick) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, err + return types.CreateFullRangePositionData{}, err } - return positionId, amount0, amount1, liquidity, nil + return types.CreateFullRangePositionData{ID: positionData.ID, Amount0: positionData.Amount0, Amount1: positionData.Amount1, Liquidity: positionData.Liquidity}, nil } // CreateFullRangePositionLocked creates a full range (min to max tick) concentrated liquidity position for the given pool ID, owner, and coins. // CL shares are minted which represent the underlying liquidity and are locked for the given duration. // State entries are also created to map the position ID to the underlying lock ID. -func (k Keeper) CreateFullRangePositionLocked(ctx sdk.Context, clPoolId uint64, owner sdk.AccAddress, coins sdk.Coins, remainingLockDuration time.Duration) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, concentratedLockID uint64, err error) { +func (k Keeper) CreateFullRangePositionLocked(ctx sdk.Context, clPoolId uint64, owner sdk.AccAddress, coins sdk.Coins, remainingLockDuration time.Duration) (positionData types.CreateFullRangePositionData, concentratedLockID uint64, err error) { // Create a full range (min to max tick) concentrated liquidity position. - positionId, amount0, amount1, liquidity, err = k.CreateFullRangePosition(ctx, clPoolId, owner, coins) + positionData, err = k.CreateFullRangePosition(ctx, clPoolId, owner, coins) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err + return types.CreateFullRangePositionData{}, 0, err } // Mint CL shares (similar to GAMM shares) for the position and lock them for the remaining lock duration. // Also sets the position ID to underlying lock ID mapping. - concentratedLockId, _, err := k.mintSharesAndLock(ctx, clPoolId, positionId, owner, remainingLockDuration) + concentratedLockId, _, err := k.mintSharesAndLock(ctx, clPoolId, positionData.ID, owner, remainingLockDuration) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err + return types.CreateFullRangePositionData{}, 0, err } - return positionId, amount0, amount1, liquidity, concentratedLockId, nil + return positionData, concentratedLockId, nil } // CreateFullRangePositionUnlocking creates a full range (min to max tick) concentrated liquidity position for the given pool ID, owner, and coins. // This function is strictly used when migrating a balancer position to CL, where the balancer position is currently unlocking. // We lock the cl position for whatever the remaining time is from the balancer position and immediately begin unlocking from where it left off. -func (k Keeper) CreateFullRangePositionUnlocking(ctx sdk.Context, clPoolId uint64, owner sdk.AccAddress, coins sdk.Coins, remainingLockDuration time.Duration) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, concentratedLockID uint64, err error) { +func (k Keeper) CreateFullRangePositionUnlocking(ctx sdk.Context, clPoolId uint64, owner sdk.AccAddress, coins sdk.Coins, remainingLockDuration time.Duration) (positionData types.CreateFullRangePositionData, concentratedLockID uint64, err error) { // Create a full range (min to max tick) concentrated liquidity position. - positionId, amount0, amount1, liquidity, err = k.CreateFullRangePosition(ctx, clPoolId, owner, coins) + positionData, err = k.CreateFullRangePosition(ctx, clPoolId, owner, coins) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err + return types.CreateFullRangePositionData{}, 0, err } // Mint cl shares for the position and lock them for the remaining lock duration. // Also sets the position ID to underlying lock ID mapping. - concentratedLockId, underlyingLiquidityTokenized, err := k.mintSharesAndLock(ctx, clPoolId, positionId, owner, remainingLockDuration) + concentratedLockId, underlyingLiquidityTokenized, err := k.mintSharesAndLock(ctx, clPoolId, positionData.ID, owner, remainingLockDuration) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err + return types.CreateFullRangePositionData{}, 0, err } // Begin unlocking the newly created concentrated lock. concentratedLockID, err = k.lockupKeeper.BeginForceUnlock(ctx, concentratedLockId, underlyingLiquidityTokenized) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err + return types.CreateFullRangePositionData{}, 0, err } - return positionId, amount0, amount1, liquidity, concentratedLockID, nil + return positionData, concentratedLockID, nil } // mintSharesAndLock mints the shares for the full range concentrated liquidity position and locks them for the given duration. It also updates the position ID to underlying lock ID mapping. @@ -533,193 +532,6 @@ func (k Keeper) getNextPositionIdAndIncrement(ctx sdk.Context) uint64 { return nextPositionId } -// fungifyChargedPosition takes in a list of positionIds and combines them into a single position. -// It validates that all positions belong to the same owner, are in the same tick range, pool, and are fully charged. Fails if not. -// Otherwise, it creates a completely new position P. P's liquidity equals to the sum of all -// liquidities of positions given by positionIds. The uptime of the join time of the new position equals -// to current block time - max authorized uptime duration (to signify that it is fully charged). -// The previous positions are deleted from state. Prior to deleting, the old position's unclaimed rewards are transferred to the new position. -// The new position ID is returned. -// An error is returned if: -// - the caller does not own all the positions -// - positions are not in the same pool -// - positions are all not fully charged -// - positions are not in the same tick range -// - all positions are unlocked -// NOTE: disabled and unused at launch -// nolint: unused -func (k Keeper) fungifyChargedPosition(ctx sdk.Context, owner sdk.AccAddress, positionIds []uint64) (uint64, error) { - // Get the longest authorized uptime, which is the fully charged duration. - fullyChargedDuration := k.getLargestAuthorizedUptimeDuration(ctx) - - // Check that all the positions are in the same pool, tick range, are fully charged, and two or more positions were provided. - // Sum the liquidity of all the positions. - poolId, lowerTick, upperTick, combinedLiquidityOfAllPositions, err := k.validatePositionsAndGetTotalLiquidity(ctx, owner, positionIds, fullyChargedDuration) - if err != nil { - return 0, err - } - - // The new position's timestamp is the current block time minus the fully charged duration. - joinTime := ctx.BlockTime().Add(-fullyChargedDuration) - - // Get the next position ID and increment the global counter. - newPositionId := k.getNextPositionIdAndIncrement(ctx) - - // Update pool uptime accumulators to now. - if err := k.UpdatePoolUptimeAccumulatorsToNow(ctx, poolId); err != nil { - return 0, err - } - - // Create the new position in the pool based on the provided tick range and liquidity delta. - // This also initializes the spread reward accumulator and the uptime accumulators for the new position. - _, _, _, _, err = k.UpdatePosition(ctx, poolId, owner, lowerTick, upperTick, combinedLiquidityOfAllPositions, joinTime, newPositionId) - if err != nil { - return 0, err - } - - // Get the new position's uptime accum name and the pool's uptime accumulators. - newPositionUptimeAccName := string(types.KeyPositionId(newPositionId)) - uptimeAccumulators, err := k.GetUptimeAccumulators(ctx, poolId) - if err != nil { - return 0, err - } - - // Get the new position's spread reward accum name and the pool's spread reward accumulator. - newPositionSpreadRewardAccName := types.KeySpreadRewardPositionAccumulator(newPositionId) - spreadRewardAccumulator, err := k.GetSpreadRewardAccumulator(ctx, poolId) - if err != nil { - return 0, err - } - - // Compute uptime growth outside of the range between lower tick and upper tick - uptimeGrowthOutside, err := k.GetUptimeGrowthOutsideRange(ctx, poolId, lowerTick, upperTick) - if err != nil { - return 0, err - } - - // Compute the spread reward growth outside of the range between lower tick and upper tick - spreadRewardGrowthOutside, err := k.getSpreadRewardGrowthOutside(ctx, poolId, lowerTick, upperTick) - if err != nil { - return 0, err - } - - // Move unclaimed rewards from the old positions to the new position. - // Also, delete the old positions from state. - - // Loop through each of the old position IDs. - for _, oldPositionId := range positionIds { - // Loop through each uptime accumulator for the pool. - for uptimeIndex, uptimeAccum := range uptimeAccumulators { - // Move rewards into the new uptime accumulator and delete the old uptime accumulator. - oldPositionName := string(types.KeyPositionId(oldPositionId)) - if err := moveRewardsToNewPositionAndDeleteOldAcc(uptimeAccum, oldPositionName, newPositionUptimeAccName, uptimeGrowthOutside[uptimeIndex]); err != nil { - return 0, err - } - } - - // Move spread rewards into the new spread reward accumulator and delete the old spread reward accumulator. - oldPositionSpreadRewardName := types.KeySpreadRewardPositionAccumulator(oldPositionId) - if err := moveRewardsToNewPositionAndDeleteOldAcc(spreadRewardAccumulator, oldPositionSpreadRewardName, newPositionSpreadRewardAccName, spreadRewardGrowthOutside); err != nil { - return 0, err - } - - // Remove the old position from state. - err = k.deletePosition(ctx, oldPositionId, owner, poolId) - if err != nil { - return 0, err - } - } - - // Query claimable incentives for events. - claimableIncentives, _, err := k.GetClaimableIncentives(ctx, newPositionId) - if err != nil { - return 0, err - } - - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( - types.TypeEvtFungifyChargedPosition, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, owner.String()), - sdk.NewAttribute(types.AttributeInputPositionIds, strings.Trim(strings.Join(strings.Fields(fmt.Sprint(positionIds)), ","), "[]")), - sdk.NewAttribute(types.AttributeOutputPositionId, strconv.FormatUint(newPositionId, 10)), - sdk.NewAttribute(types.AttributeClaimableIncentives, claimableIncentives.String()), - ), - }) - - return newPositionId, nil -} - -// validatePositionsAndGetTotalLiquidity validates a list of positions owned by the caller and returns their total liquidity. -// It also returns the pool ID, lower tick, and upper tick that all the provided positions are confirmed to share. -// Returns error if: -// - the caller does not own all the positions -// - positions are not in the same pool -// - positions are all not fully charged -// - positions are not in the same tick range -// - all positions are unlocked -// NOTE: It is only used by fungifyChargedPosition which we disabled for launch. -// nolint: unused -func (k Keeper) validatePositionsAndGetTotalLiquidity(ctx sdk.Context, owner sdk.AccAddress, positionIds []uint64, fullyChargedDuration time.Duration) (uint64, int64, int64, sdk.Dec, error) { - totalLiquidity := sdk.ZeroDec() - - // Check we meet the minimum number of positions to combine. - if len(positionIds) < MinNumPositions { - return 0, 0, 0, sdk.Dec{}, types.PositionQuantityTooLowError{MinNumPositions: MinNumPositions, NumPositions: len(positionIds)} - } - - // Note the first position's params to use as the base for comparison. - basePosition, err := k.GetPosition(ctx, positionIds[0]) - if err != nil { - return 0, 0, 0, sdk.Dec{}, err - } - - for i, positionId := range positionIds { - position, err := k.GetPosition(ctx, positionId) - if err != nil { - return 0, 0, 0, sdk.Dec{}, err - } - // Check that the caller owns each of the positions. - if position.Address != owner.String() { - return 0, 0, 0, sdk.Dec{}, types.PositionOwnerMismatchError{PositionOwner: position.Address, Sender: owner.String()} - } - - // Check that each of the positions have no underlying lock. - // If the position has an underlying lock, check that it has matured. - // Note, this is calling a non mutative method, so if the position's lock is now mature, - // it will not return an error but the connection will be persisted until a mutative method is called. - positionHasActiveUnderlyingLock, lockId, err := k.PositionHasActiveUnderlyingLock(ctx, positionId) - if err != nil { - return 0, 0, 0, sdk.Dec{}, err - } - if positionHasActiveUnderlyingLock { - // Lock is not mature, return error. - return 0, 0, 0, sdk.Dec{}, types.LockNotMatureError{PositionId: position.PositionId, LockId: lockId} - } - - // Check that each of the positions are fully charged. - fullyChargedMinTimestamp := position.JoinTime.Add(fullyChargedDuration) - if fullyChargedMinTimestamp.After(ctx.BlockTime()) { - return 0, 0, 0, sdk.Dec{}, types.PositionNotFullyChargedError{PositionId: position.PositionId, PositionJoinTime: position.JoinTime, FullyChargedMinTimestamp: fullyChargedMinTimestamp} - } - - // No need to check the first position against itself. - if i > 0 { - // Check that each of the positions are in the same pool and tick range. - if position.PoolId != basePosition.PoolId { - return 0, 0, 0, sdk.Dec{}, types.PositionsNotInSamePoolError{Position1PoolId: position.PoolId, Position2PoolId: basePosition.PoolId} - } - if position.LowerTick != basePosition.LowerTick || position.UpperTick != basePosition.UpperTick { - return 0, 0, 0, sdk.Dec{}, types.PositionsNotInSameTickRangeError{Position1TickLower: position.LowerTick, Position1TickUpper: position.UpperTick, Position2TickLower: basePosition.LowerTick, Position2TickUpper: basePosition.UpperTick} - } - } - - // Add the liquidity of the position to the total liquidity. - totalLiquidity = totalLiquidity.Add(position.Liquidity) - } - return basePosition.PoolId, basePosition.LowerTick, basePosition.UpperTick, totalLiquidity, nil -} - // GetLockIdFromPositionId returns the lock id associated with the given position id. func (k Keeper) GetLockIdFromPositionId(ctx sdk.Context, positionId uint64) (uint64, error) { store := ctx.KVStore(k.storeKey) diff --git a/x/concentrated-liquidity/position_test.go b/x/concentrated-liquidity/position_test.go index 1667920d1e1..2c2f5a8a99e 100644 --- a/x/concentrated-liquidity/position_test.go +++ b/x/concentrated-liquidity/position_test.go @@ -15,6 +15,7 @@ import ( "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/math" "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/model" "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" + cltypes "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" ) const ( @@ -814,20 +815,20 @@ func (s *KeeperTestSuite) TestCalculateUnderlyingAssetsFromPosition() { // prepare concentrated pool with a default position s.PrepareConcentratedPool() s.FundAcc(s.TestAccs[0], sdk.NewCoins(sdk.NewCoin(ETH, DefaultAmt0), sdk.NewCoin(USDC, DefaultAmt1))) - _, _, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, 1, s.TestAccs[0], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) + _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, 1, s.TestAccs[0], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) s.Require().NoError(err) // create a position from the test case s.FundAcc(s.TestAccs[1], sdk.NewCoins(sdk.NewCoin(ETH, DefaultAmt0), sdk.NewCoin(USDC, DefaultAmt1))) - _, actualAmount0, actualAmount1, liquidity, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, tc.position.PoolId, s.TestAccs[1], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), tc.position.LowerTick, tc.position.UpperTick) + positionData, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, tc.position.PoolId, s.TestAccs[1], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), tc.position.LowerTick, tc.position.UpperTick) s.Require().NoError(err) - tc.position.Liquidity = liquidity + tc.position.Liquidity = positionData.Liquidity if tc.isZeroLiquidity { // set the position liquidity to zero tc.position.Liquidity = sdk.ZeroDec() - actualAmount0 = sdk.ZeroInt() - actualAmount1 = sdk.ZeroInt() + positionData.Amount0 = sdk.ZeroInt() + positionData.Amount1 = sdk.ZeroInt() } // calculate underlying assets from the position @@ -836,304 +837,8 @@ func (s *KeeperTestSuite) TestCalculateUnderlyingAssetsFromPosition() { calculatedCoin0, calculatedCoin1, err := cl.CalculateUnderlyingAssetsFromPosition(s.Ctx, tc.position, clPool) s.Require().NoError(err) - s.Require().Equal(calculatedCoin0.String(), sdk.NewCoin(clPool.GetToken0(), actualAmount0).String()) - s.Require().Equal(calculatedCoin1.String(), sdk.NewCoin(clPool.GetToken1(), actualAmount1).String()) - }) - } -} - -func (s *KeeperTestSuite) TestValidateAndFungifyChargedPositions() { - const ( - locked = true - unlocked = !locked - ) - - var ( - defaultAddress = s.TestAccs[0] - secondAddress = s.TestAccs[1] - defaultBlockTime = time.Unix(1, 1).UTC() - testFullChargeDuration = time.Hour * 24 - ) - - type position struct { - positionId uint64 - poolId uint64 - acc sdk.AccAddress - coins sdk.Coins - lowerTick int64 - upperTick int64 - isLocked bool - } - - tests := []struct { - name string - setupFullyChargedPositions []position - setupUnchargedPositions []position - lockPositionIds []uint64 - positionIdsToMigrate []uint64 - accountCallingMigration sdk.AccAddress - unlockBeforeBlockTimeMs time.Duration //nolint:stylecheck - expectedNewPositionId uint64 - expectedErr error - }{ - { - name: "Happy path: Fungify three fully charged positions", - setupFullyChargedPositions: []position{ - {1, defaultPoolId, defaultAddress, DefaultCoins, DefaultLowerTick, DefaultUpperTick, unlocked}, - {2, defaultPoolId, defaultAddress, DefaultCoins, DefaultLowerTick, DefaultUpperTick, unlocked}, - {3, defaultPoolId, defaultAddress, DefaultCoins, DefaultLowerTick, DefaultUpperTick, unlocked}, - }, - positionIdsToMigrate: []uint64{1, 2, 3}, - accountCallingMigration: defaultAddress, - expectedNewPositionId: 4, - }, - { - name: "Error: Fungify three positions, but one of them is not fully charged", - setupFullyChargedPositions: []position{ - {1, defaultPoolId, defaultAddress, DefaultCoins, DefaultLowerTick, DefaultUpperTick, unlocked}, - {2, defaultPoolId, defaultAddress, DefaultCoins, DefaultLowerTick, DefaultUpperTick, unlocked}, - }, - setupUnchargedPositions: []position{ - {3, defaultPoolId, defaultAddress, DefaultCoins, DefaultLowerTick, DefaultUpperTick, unlocked}, - }, - positionIdsToMigrate: []uint64{1, 2, 3}, - accountCallingMigration: defaultAddress, - expectedNewPositionId: 0, - expectedErr: types.PositionNotFullyChargedError{PositionId: 3, PositionJoinTime: defaultBlockTime.Add(testFullChargeDuration), FullyChargedMinTimestamp: defaultBlockTime.Add(testFullChargeDuration).Add(testFullChargeDuration)}, - }, - { - name: "Error: Fungify three positions, but one of them is not in the same pool", - setupFullyChargedPositions: []position{ - {1, defaultPoolId, defaultAddress, DefaultCoins, DefaultLowerTick, DefaultUpperTick, unlocked}, - {2, 2, defaultAddress, DefaultCoins, DefaultLowerTick, DefaultUpperTick, unlocked}, - {3, defaultPoolId, defaultAddress, DefaultCoins, DefaultLowerTick, DefaultUpperTick, unlocked}, - }, - positionIdsToMigrate: []uint64{1, 2, 3}, - accountCallingMigration: defaultAddress, - expectedNewPositionId: 0, - expectedErr: types.PositionsNotInSamePoolError{Position1PoolId: 2, Position2PoolId: 1}, - }, - { - name: "Error: Fungify three positions, but one of them is not owned by the same owner", - setupFullyChargedPositions: []position{ - {1, defaultPoolId, defaultAddress, DefaultCoins, DefaultLowerTick, DefaultUpperTick, unlocked}, - {2, defaultPoolId, secondAddress, DefaultCoins, DefaultLowerTick, DefaultUpperTick, unlocked}, - {3, defaultPoolId, defaultAddress, DefaultCoins, DefaultLowerTick, DefaultUpperTick, unlocked}, - }, - positionIdsToMigrate: []uint64{1, 2, 3}, - accountCallingMigration: defaultAddress, - expectedNewPositionId: 0, - expectedErr: types.PositionOwnerMismatchError{PositionOwner: secondAddress.String(), Sender: defaultAddress.String()}, - }, - { - name: "Error: Fungify three positions, but one of them is not in the same range", - setupFullyChargedPositions: []position{ - {1, defaultPoolId, defaultAddress, DefaultCoins, DefaultLowerTick, DefaultUpperTick, unlocked}, - {2, defaultPoolId, defaultAddress, DefaultCoins, DefaultLowerTick - 100, DefaultUpperTick, unlocked}, - {3, defaultPoolId, defaultAddress, DefaultCoins, DefaultLowerTick, DefaultUpperTick, unlocked}, - }, - positionIdsToMigrate: []uint64{1, 2, 3}, - accountCallingMigration: defaultAddress, - expectedNewPositionId: 0, - expectedErr: types.PositionsNotInSameTickRangeError{Position1TickLower: DefaultLowerTick - 100, Position1TickUpper: DefaultUpperTick, Position2TickLower: DefaultLowerTick, Position2TickUpper: DefaultUpperTick}, - }, - { - name: "Error: Fungify one position, must have at least two", - setupFullyChargedPositions: []position{ - {1, defaultPoolId, defaultAddress, DefaultCoins, DefaultLowerTick, DefaultUpperTick, unlocked}, - }, - setupUnchargedPositions: []position{}, - positionIdsToMigrate: []uint64{1}, - accountCallingMigration: defaultAddress, - expectedNewPositionId: 0, - expectedErr: types.PositionQuantityTooLowError{MinNumPositions: cl.MinNumPositions, NumPositions: 1}, - }, - { - name: "Error: No position to fungify", - setupFullyChargedPositions: []position{}, - setupUnchargedPositions: []position{}, - positionIdsToMigrate: []uint64{1}, - accountCallingMigration: defaultAddress, - expectedNewPositionId: 0, - expectedErr: types.PositionQuantityTooLowError{MinNumPositions: cl.MinNumPositions, NumPositions: 1}, - }, - { - name: "Error: one of the full range positions is locked", - setupFullyChargedPositions: []position{ - {1, defaultPoolId, defaultAddress, DefaultCoins, types.MinInitializedTick, types.MaxTick, unlocked}, - {2, defaultPoolId, defaultAddress, DefaultCoins, types.MinInitializedTick, types.MaxTick, locked}, - {3, defaultPoolId, defaultAddress, DefaultCoins, types.MinInitializedTick, types.MaxTick, unlocked}, - }, - lockPositionIds: []uint64{2}, - positionIdsToMigrate: []uint64{1, 2, 3}, - accountCallingMigration: defaultAddress, - expectedNewPositionId: 0, - expectedErr: types.LockNotMatureError{PositionId: 2, LockId: 1}, - }, - { - name: "Pass: one of the full range positions was locked but got unlocked 1ms before fungification", - setupFullyChargedPositions: []position{ - {1, defaultPoolId, defaultAddress, DefaultCoins, types.MinInitializedTick, types.MaxTick, unlocked}, - {2, defaultPoolId, defaultAddress, DefaultCoins, types.MinInitializedTick, types.MaxTick, locked}, - {3, defaultPoolId, defaultAddress, DefaultCoins, types.MinInitializedTick, types.MaxTick, unlocked}, - }, - - lockPositionIds: []uint64{2}, - positionIdsToMigrate: []uint64{1, 2, 3}, - accountCallingMigration: defaultAddress, - // Subtracting one millisecond from the block time (when it's supposed to be unlocked - // by default, makes the lock mature) - unlockBeforeBlockTimeMs: time.Millisecond * -1, - expectedNewPositionId: 4, - }, - } - - for _, test := range tests { - s.Run(test.name, func() { - // Init suite for each test. - s.SetupTest() - s.Ctx = s.Ctx.WithBlockTime(defaultBlockTime) - totalPositionsToCreate := sdk.NewInt(int64(len(test.setupFullyChargedPositions) + len(test.setupUnchargedPositions))) - requiredBalances := sdk.NewCoins(sdk.NewCoin(ETH, DefaultAmt0.Mul(totalPositionsToCreate)), sdk.NewCoin(USDC, DefaultAmt1.Mul(totalPositionsToCreate))) - - params := s.App.ConcentratedLiquidityKeeper.GetParams(s.Ctx) - params.AuthorizedUptimes = []time.Duration{time.Nanosecond, testFullChargeDuration} - s.App.ConcentratedLiquidityKeeper.SetParams(s.Ctx, params) - - // Fund accounts - s.FundAcc(defaultAddress, requiredBalances) - s.FundAcc(secondAddress, requiredBalances) - - // Create two default CL pools - s.PrepareConcentratedPool() - s.PrepareConcentratedPool() - - // Set incentives for pool to ensure accumulators work correctly - err := s.clk.SetMultipleIncentiveRecords(s.Ctx, DefaultIncentiveRecords) - s.Require().NoError(err) - - // Set up fully charged positions - totalLiquidity := sdk.ZeroDec() - - // See increases in the test below. - // The reason we double testFullChargeDurationis is because that is by how much we increase block time in total - // to set up the fully charged positions. - lockDuration := testFullChargeDuration + testFullChargeDuration + test.unlockBeforeBlockTimeMs - for _, pos := range test.setupFullyChargedPositions { - var ( - liquidityCreated sdk.Dec - err error - ) - if pos.isLocked { - _, _, _, liquidityCreated, _, err = s.clk.CreateFullRangePositionUnlocking(s.Ctx, pos.poolId, pos.acc, pos.coins, lockDuration) - s.Require().NoError(err) - } else { - _, _, _, liquidityCreated, _, _, err = s.clk.CreatePosition(s.Ctx, pos.poolId, pos.acc, pos.coins, sdk.ZeroInt(), sdk.ZeroInt(), pos.lowerTick, pos.upperTick) - s.Require().NoError(err) - } - - totalLiquidity = totalLiquidity.Add(liquidityCreated) - } - - // Increase block time by the fully charged duration to make sure previously added positions are charged. - s.Ctx = s.Ctx.WithBlockTime(s.Ctx.BlockTime().Add(testFullChargeDuration)) - - // Set up uncharged positions - for _, pos := range test.setupUnchargedPositions { - _, _, _, _, _, _, err := s.clk.CreatePosition(s.Ctx, pos.poolId, pos.acc, pos.coins, sdk.ZeroInt(), sdk.ZeroInt(), pos.lowerTick, pos.upperTick) - s.Require().NoError(err) - } - - // Increase block time by one more day - 1 ns to ensure that the previously added positions are not fully charged. - s.Ctx = s.Ctx.WithBlockTime(s.Ctx.BlockTime().Add(testFullChargeDuration - time.Nanosecond)) - - // Get the longest authorized uptime, which is the fully charged duration. - fullyChargedDuration := s.clk.GetLargestAuthorizedUptimeDuration(s.Ctx) - - // First run non mutative validation and check results - poolId, lowerTick, upperTick, liquidity, err := s.clk.ValidatePositionsAndGetTotalLiquidity(s.Ctx, test.accountCallingMigration, test.positionIdsToMigrate, fullyChargedDuration) - if test.expectedErr != nil { - s.Require().ErrorIs(err, test.expectedErr) - s.Require().Equal(uint64(0), poolId) - s.Require().Equal(int64(0), lowerTick) - s.Require().Equal(int64(0), upperTick) - s.Require().Equal(sdk.Dec{}, liquidity) - } else { - s.Require().NoError(err) - - // Check that the poolId, lowerTick, upperTick, and liquidity are correct - for _, posId := range test.positionIdsToMigrate { - position, err := s.clk.GetPosition(s.Ctx, posId) - s.Require().NoError(err) - s.Require().Equal(poolId, position.PoolId) - s.Require().Equal(lowerTick, position.LowerTick) - s.Require().Equal(upperTick, position.UpperTick) - } - s.Require().Equal(totalLiquidity, liquidity) - } - - // Update the accumulators for defaultPoolId to the current time - err = s.clk.UpdatePoolUptimeAccumulatorsToNow(s.Ctx, defaultPoolId) - s.Require().NoError(err) - - // Get the unclaimed rewards for all the positions that are being migrated - unclaimedRewardsForEachUptimeAcrossAllOldPositions := make([]sdk.DecCoins, len(types.SupportedUptimes)) - for _, positionId := range test.positionIdsToMigrate { - unclaimedRewardsForPosition := s.GetTotalAccruedRewardsByAccumulator(positionId, false) - unclaimedRewardsForEachUptimeAcrossAllOldPositions, err = osmoutils.AddDecCoinArrays(unclaimedRewardsForEachUptimeAcrossAllOldPositions, unclaimedRewardsForPosition) - s.Require().NoError(err) - } - - // Next, run the mutative function and check results - newPositionId, err := s.clk.FungifyChargedPosition(s.Ctx, test.accountCallingMigration, test.positionIdsToMigrate) - if test.expectedErr != nil { - s.Require().ErrorIs(err, test.expectedErr) - s.Require().Equal(uint64(0), newPositionId) - } else { - s.Require().NoError(err) - s.Require().Equal(test.expectedNewPositionId, newPositionId) - - // Since the positionLiquidity of the old position should have been deleted, retrieving it should return an error. - for _, posId := range test.positionIdsToMigrate { - positionLiquidity, err := s.clk.GetPositionLiquidity(s.Ctx, posId) - s.Require().ErrorIs(err, types.PositionIdNotFoundError{PositionId: posId}) - s.Require().Equal(sdk.Dec{}, positionLiquidity) - } - - // --- New position assertions --- - - newPosition, err := s.clk.GetPosition(s.Ctx, newPositionId) - - // Check that the liquidity is equal to the sum of the old positions. - s.Require().NoError(err) - s.Require().Equal(totalLiquidity, newPosition.Liquidity) - - // The new position's join time should be the current block time minus the fully charged duration. - fullCharge := s.clk.GetLargestAuthorizedUptimeDuration(s.Ctx) - s.Require().Equal(s.Ctx.BlockTime().Add(-fullCharge), newPosition.JoinTime) - - // Get the unclaimed rewards for the new position - unclaimedRewardsForEachUptimeNewPosition := s.GetTotalAccruedRewardsByAccumulator(newPositionId, true) - - // Check that the old positions have been deleted. - s.AssertPositionsDoNotExist(test.positionIdsToMigrate) - - // The new position's unclaimed rewards should be the sum of the old positions' unclaimed rewards. - s.Require().Equal(unclaimedRewardsForEachUptimeAcrossAllOldPositions, unclaimedRewardsForEachUptimeNewPosition) - - // Get the final amount expected to be claimed by merging the unclaimed rewards across all uptimes. - // Note that the second value is dust, not an error. - expectedRewardsToClaim, _ := osmoutils.CollapseDecCoinsArray(unclaimedRewardsForEachUptimeAcrossAllOldPositions).TruncateDecimal() - - // Claim all the rewards for the new position and check that the rewards match the unclaimed rewards. - s.ExecuteAndValidateSuccessfulIncentiveClaim(newPositionId, expectedRewardsToClaim, sdk.Coins(nil)) - - // Check that cannot claim rewards for the old positions. - for _, positionId := range test.positionIdsToMigrate { - _, _, err := s.clk.PrepareClaimAllIncentivesForPosition(s.Ctx, positionId) - s.Require().Error(err) - } - } + s.Require().Equal(calculatedCoin0.String(), sdk.NewCoin(clPool.GetToken0(), positionData.Amount0).String()) + s.Require().Equal(calculatedCoin1.String(), sdk.NewCoin(clPool.GetToken1(), positionData.Amount1).String()) }) } } @@ -1235,351 +940,9 @@ func (s *KeeperTestSuite) TestHasAnyPositionForPool() { } } -// This test specifically tests that spread reward collection works as expected -// after fungifying positions. -func (s *KeeperTestSuite) TestFungifyChargedPositions_SwapAndClaimSpreadRewards() { - // Init suite for the test. - s.SetupTest() - - const swapAmount = 1_000_000 - var defaultAddress = s.TestAccs[0] - - // Set up pool, positions, and incentive records - _, expectedPositionIds, totalLiquidity := s.runFungifySetup(defaultAddress, DefaultFungifyNumPositions, DefaultFungifyFullChargeDuration, DefaultSpreadFactor, DefaultIncentiveRecords) - - // Perform a swap to earn spread rewards - swapAmountIn := sdk.NewCoin(ETH, sdk.NewInt(swapAmount)) - expectedSpreadReward := swapAmountIn.Amount.ToDec().Mul(DefaultSpreadFactor) - // We run expected spread rewards through a cycle of division and multiplication by liquidity to capture appropriate rounding behavior. - // Note that we truncate the int at the end since it is not possible to have a decimal spread reward amount collected (the QuoTruncate - // and MulTruncates are much smaller operations that round down for values past the 18th decimal place). - expectedSpreadRewardTruncated := expectedSpreadReward.QuoTruncate(totalLiquidity).MulTruncate(totalLiquidity).TruncateInt() - s.FundAcc(s.TestAccs[0], sdk.NewCoins(swapAmountIn)) - s.swapAndTrackXTimesInARow(defaultPoolId, swapAmountIn, USDC, types.MinSpotPrice, 1) - - // Increase block time by the fully charged duration - s.AddBlockTime(DefaultFungifyFullChargeDuration) - - // First run non mutative validation and check results - newPositionId, err := s.clk.FungifyChargedPosition(s.Ctx, defaultAddress, expectedPositionIds) - s.Require().NoError(err) - - // Claim spread rewards - collected, err := s.clk.CollectSpreadRewards(s.Ctx, defaultAddress, newPositionId) - s.Require().NoError(err) - - // Validate that the correct spread reward amount was collected. - s.Require().Equal(expectedSpreadRewardTruncated, collected.AmountOf(swapAmountIn.Denom)) - - // Check that cannot claim again. - collected, err = s.clk.CollectSpreadRewards(s.Ctx, defaultAddress, newPositionId) - s.Require().NoError(err) - s.Require().Equal(sdk.Coins(nil), collected) - - spreadRewardAccum, err := s.clk.GetSpreadRewardAccumulator(s.Ctx, defaultPoolId) - s.Require().NoError(err) - - // Check that cannot claim old positions - for _, oldPositionId := range expectedPositionIds { - collected, err = s.clk.CollectSpreadRewards(s.Ctx, defaultAddress, oldPositionId) - s.Require().Error(err) - s.Require().Equal(sdk.Coins{}, collected) - - hasPosition := s.clk.HasPosition(s.Ctx, oldPositionId) - s.Require().False(hasPosition) - - hasSpreadRewardPositionTracker := spreadRewardAccum.HasPosition(types.KeySpreadRewardPositionAccumulator(oldPositionId)) - s.Require().False(hasSpreadRewardPositionTracker) - } -} - -func (s *KeeperTestSuite) TestFungifyChargedPositions_ClaimIncentives() { - // Init suite for the test. - s.SetupTest() - var defaultAddress = s.TestAccs[0] - - // Set incentives for pool to ensure accumulators work correctly - testIncentiveRecord := types.IncentiveRecord{ - PoolId: 1, - IncentiveRecordBody: types.IncentiveRecordBody{ - RemainingCoin: sdk.NewDecCoinFromDec(USDC, sdk.NewDec(1000000000000000000)), - EmissionRate: sdk.NewDec(1), // 1 per second - StartTime: defaultBlockTime, - }, - MinUptime: time.Nanosecond, - } - - // Set up pool, positions, and incentive records - pool, expectedPositionIds, _ := s.runFungifySetup(defaultAddress, DefaultFungifyNumPositions, DefaultFungifyFullChargeDuration, DefaultSpreadFactor, []types.IncentiveRecord{testIncentiveRecord}) - - // an error of 1 for each position - roundingError := int64(DefaultFungifyNumPositions) - roundingTolerance := osmomath.ErrTolerance{ - AdditiveTolerance: sdk.NewDec(roundingError), - RoundingDir: osmomath.RoundDown, - } - expectedAmount := sdk.NewInt(60 * 60 * 24) // 1 day in seconds * 1 per second - s.FundAcc(pool.GetIncentivesAddress(), sdk.NewCoins(sdk.NewCoin(USDC, expectedAmount))) - - // Increase block time by the fully charged duration - s.Ctx = s.Ctx.WithBlockTime(s.Ctx.BlockTime().Add(DefaultFungifyFullChargeDuration)) - - // sync accumulators - // We use cache context to update uptime accumulators for estimating claimable incentives - // prior to running fungify. However, we do not want the mutations made in test setup to have - // impact on the system under test because it (fungify) must update the uptime accumulators itself. - cacheCtx, _ := s.Ctx.CacheContext() - err := s.clk.UpdatePoolUptimeAccumulatorsToNow(cacheCtx, pool.GetId()) - s.Require().NoError(err) - - claimableIncentives := sdk.NewCoins() - for i := 0; i < DefaultFungifyNumPositions; i++ { - positionIncentives, forfeitedIncentives, err := s.clk.GetClaimableIncentives(cacheCtx, uint64(i+1)) - s.Require().NoError(err) - s.Require().Equal(sdk.Coins(nil), forfeitedIncentives) - claimableIncentives = claimableIncentives.Add(positionIncentives...) - } - - actualClaimedAmount := claimableIncentives.AmountOf(USDC) - s.Require().Equal(0, roundingTolerance.Compare(expectedAmount, actualClaimedAmount), "expected: %s, got: %s", expectedAmount, actualClaimedAmount) - - // System under test - newPositionId, err := s.clk.FungifyChargedPosition(s.Ctx, defaultAddress, expectedPositionIds) - s.Require().NoError(err) - - // Claim incentives. - collected, _, err := s.clk.CollectIncentives(s.Ctx, defaultAddress, newPositionId) - s.Require().NoError(err) - - // Validate that the correct incentives amount was collected. - actualClaimedAmount = collected.AmountOf(USDC) - s.Require().Equal(1, len(collected)) - s.Require().Equal(0, roundingTolerance.Compare(expectedAmount, actualClaimedAmount), "expected: %s, got: %s", expectedAmount, actualClaimedAmount) - - // Check that cannot claim again. - collected, _, err = s.clk.CollectIncentives(s.Ctx, defaultAddress, newPositionId) - s.Require().NoError(err) - s.Require().Equal(sdk.Coins(nil), collected) - - // Check that cannot claim old positions - for i := 0; i < DefaultFungifyNumPositions; i++ { - collected, _, err = s.clk.CollectIncentives(s.Ctx, defaultAddress, uint64(i+1)) - s.Require().Error(err) - s.Require().Equal(sdk.Coins{}, collected) - } -} - -// TestFunctionalFungifyChargedPositions is a functional test that covers more complex scenarios related to fee/incentive claiming -// in the context of fungified positions. -// -// Testing strategy: -// 1. Create a pool with 6 positions in groups of 2 such that each group of 2 is adjacent to each other -// 2. Swap out all the USDC in the pool to generate spread rewards -// 3. Emit incentives (to left two positions) -// 4. Collect all spread rewards and incentives on cached ctx and make assertions -// 5. Fungify each set of positions -// 6. Collect all spread rewards and incentives on and make assertions -func (s *KeeperTestSuite) TestFunctionalFungifyChargedPositions() { - s.SetupTest() - s.Ctx = s.Ctx.WithBlockTime(defaultBlockTime) - - // Set incentives for pool to ensure accumulators work correctly - testIncentiveRecord := types.IncentiveRecord{ - PoolId: 1, - IncentiveRecordBody: types.IncentiveRecordBody{ - RemainingCoin: sdk.NewDecCoinFromDec(USDC, sdk.NewDec(1000000000000000000)), - EmissionRate: sdk.NewDec(1), // 1 per second - StartTime: defaultBlockTime, - }, - MinUptime: time.Nanosecond, - } - - // --- Set up positions --- - - // Create the relevant positions with these acccounts such that the left, middle, and right positions - // are exactly adjacent to each other in terms of tick ranges. - defaultPositionWidth := DefaultUpperTick - DefaultLowerTick - - // middleAddress refers to the owner of the position in the middle of the three we create in this test - middleAddress := s.TestAccs[0] - - // Set up pool, default incentive records, and a single default position - pool, middlePositionIds, _ := s.runFungifySetup(middleAddress, 2, DefaultFungifyFullChargeDuration, DefaultSpreadFactor, []types.IncentiveRecord{testIncentiveRecord}) - - // Create two new addresses to hold a position to the left and right of the one we created above - testAccs := apptesting.CreateRandomAccounts(2) - leftAddress := testAccs[0] - rightAddress := testAccs[1] - - // Set up left positions - leftPositionLowerTick := DefaultLowerTick - defaultPositionWidth - leftPositionUpperTick := DefaultLowerTick - _, leftOne := s.SetupPosition(pool.GetId(), leftAddress, DefaultCoins, leftPositionLowerTick, leftPositionUpperTick, false) - _, leftTwo := s.SetupPosition(pool.GetId(), leftAddress, DefaultCoins, leftPositionLowerTick, leftPositionUpperTick, false) - - // Set up right positions - rightPositionLowerTick := DefaultUpperTick - rightPositionUpperTick := DefaultUpperTick + defaultPositionWidth - _, rightOne := s.SetupPosition(pool.GetId(), rightAddress, DefaultCoins, rightPositionLowerTick, rightPositionUpperTick, true) - _, rightTwo := s.SetupPosition(pool.GetId(), rightAddress, DefaultCoins, rightPositionLowerTick, rightPositionUpperTick, true) - - // --- Set up large swap --- - - // Calculate input and output amounts for swap based on pool liquidity - pool, err := s.clk.GetPoolById(s.Ctx, pool.GetId()) - s.Require().NoError(err) - poolLiquidity := s.App.BankKeeper.GetAllBalances(s.Ctx, pool.GetAddress()) - usdcSupply := poolLiquidity.FilterDenoms([]string{USDC})[0] - usdcSupply = sdk.NewCoin(USDC, usdcSupply.Amount.Sub(sdk.NewInt(1))) - ethFunded := sdk.NewCoins(sdk.NewCoin(ETH, poolLiquidity.AmountOf(ETH).MulRaw(2))) - - // --- Execute large swap --- - - s.TestAccs = apptesting.CreateRandomAccounts(5) - s.FundAcc(s.TestAccs[4], ethFunded) - coinIn, _, _, err := s.clk.SwapInAmtGivenOut(s.Ctx, s.TestAccs[4], pool, usdcSupply, ETH, DefaultSpreadFactor, types.MinSpotPrice) - s.Require().NoError(err) - - // --- Set up expected spread rewards and incentives --- - - // Set up expected spread rewards - expectedTotalSpreadReward := coinIn.Amount.ToDec().Mul(DefaultSpreadFactor).Ceil().TruncateInt() - expectedTotalSpreadRewardCoins := sdk.NewCoins(sdk.NewCoin(coinIn.Denom, expectedTotalSpreadReward)) - - // Set up expected incentives - expectedIncentivesAmount := sdk.NewInt(int64(DefaultFungifyFullChargeDuration.Seconds())) - expectedIncentivesCoins := sdk.NewCoins(sdk.NewCoin(USDC, expectedIncentivesAmount)) - s.FundAcc(pool.GetIncentivesAddress(), expectedIncentivesCoins) - - // --- Emit incentives --- - - // Increase block time by the fully charged duration - // Note: claiming incentives should already trigger update incentives accumulators - s.AddBlockTime(DefaultFungifyFullChargeDuration) - - // --- Assertions on non-fungified positions --- - - // We operate and claim on cached context so we can compare against behavior with fungified positions - cacheCtx, _ := s.Ctx.CacheContext() - allPositionIds := []uint64{leftOne, leftTwo, middlePositionIds[0], middlePositionIds[1], rightOne, rightTwo} - positionOwners := []sdk.AccAddress{leftAddress, leftAddress, middleAddress, middleAddress, rightAddress, rightAddress} - - // Set up trackers for individual and total collected rewards - collectedSpreadRewardsMap := make(map[uint64]sdk.Coins, len(allPositionIds)) - collectedIncentivesMap := make(map[uint64]sdk.Coins, len(allPositionIds)) - totalCollectedSpread := sdk.NewCoins() - totalCollectedIncentives := sdk.NewCoins() - - for i, id := range allPositionIds { - // Collect spread rewards and incentives on cached context - collectedSpread, err := s.clk.CollectSpreadRewards(cacheCtx, positionOwners[i], id) - s.Require().NoError(err) - collectedIncentives, forfeited, err := s.clk.CollectIncentives(cacheCtx, positionOwners[i], id) - s.Require().NoError(err) - s.Require().True(forfeited.Empty()) - - // Ensure positions that aren't touched don't collect any spread rewards or incentives - if id == rightOne || id == rightTwo { - s.Require().True(collectedSpread.Empty()) - s.Require().True(collectedIncentives.Empty()) - } - - // Middle positions collect no incentives either since we emit after the swap - if id == middlePositionIds[0] || id == middlePositionIds[1] { - s.Require().True(collectedIncentives.Empty()) - } - - // Track total amounts - collectedSpreadRewardsMap[id] = collectedSpread - collectedIncentivesMap[id] = collectedIncentives - totalCollectedSpread = totalCollectedSpread.Add(collectedSpread...) - totalCollectedIncentives = totalCollectedIncentives.Add(collectedIncentives...) - } - - // Ensure that identical positions collected the same amounts - s.Require().Equal(collectedSpreadRewardsMap[leftOne], collectedSpreadRewardsMap[leftTwo]) - s.Require().Equal(collectedSpreadRewardsMap[middlePositionIds[0]], collectedSpreadRewardsMap[middlePositionIds[1]]) - s.Require().Equal(collectedSpreadRewardsMap[rightOne], collectedSpreadRewardsMap[rightTwo]) - - s.Require().Equal(collectedIncentivesMap[leftOne], collectedIncentivesMap[leftTwo]) - s.Require().Equal(collectedIncentivesMap[middlePositionIds[0]], collectedIncentivesMap[middlePositionIds[1]]) - s.Require().Equal(collectedIncentivesMap[rightOne], collectedIncentivesMap[rightTwo]) - - // Sanity check that majority of spread rewards went to the positions that provided majority of liquidity - s.Require().True(collectedSpreadRewardsMap[leftOne].IsAllGT(collectedSpreadRewardsMap[middlePositionIds[0]])) - - // Ensure that the total spread rewards collected is correct - roundingTolerance := osmomath.ErrTolerance{ - AdditiveTolerance: sdk.NewDec(int64(len(allPositionIds))), - RoundingDir: osmomath.RoundDown, - } - for _, spreadRewardCoin := range expectedTotalSpreadRewardCoins { - denom := spreadRewardCoin.Denom - s.Require().Equal(0, roundingTolerance.Compare(expectedTotalSpreadRewardCoins.AmountOf(denom), totalCollectedSpread.AmountOf(denom))) - } - - // Ensure that the total incentives collected is correct - for _, incentiveCoin := range expectedIncentivesCoins { - denom := incentiveCoin.Denom - s.Require().Equal(0, roundingTolerance.Compare(expectedIncentivesCoins.AmountOf(denom), totalCollectedIncentives.AmountOf(denom)), "expected: %s, got: %s", expectedIncentivesCoins.AmountOf(denom), totalCollectedIncentives.AmountOf(denom)) - } - - // --- System under test: Fungify positions --- - - fungifiedLeft, err := s.clk.FungifyChargedPosition(s.Ctx, leftAddress, []uint64{leftOne, leftTwo}) - s.Require().NoError(err) - fungifiedMiddle, err := s.clk.FungifyChargedPosition(s.Ctx, middleAddress, middlePositionIds) - s.Require().NoError(err) - fungifiedRight, err := s.clk.FungifyChargedPosition(s.Ctx, rightAddress, []uint64{rightOne, rightTwo}) - s.Require().NoError(err) - - // --- Spread reward assertions on fungified positions --- - - // Set up variables to represent loss due to truncation since expected values - // are derived from individual position claims (each of which truncate) - truncatedETHCoins := sdk.NewCoins(sdk.NewCoin(ETH, roundingError)) - truncatedUSDCCoins := sdk.NewCoins(sdk.NewCoin(USDC, roundingError)) - - // Left position spread reward assertion - fungifiedLeftSpread, err := s.clk.CollectSpreadRewards(s.Ctx, leftAddress, fungifiedLeft) - s.Require().NoError(err) - s.Require().Equal(collectedSpreadRewardsMap[leftOne].Add(collectedSpreadRewardsMap[leftTwo]...).Add(truncatedETHCoins...), fungifiedLeftSpread) - - // Middle position spread reward assertion - fungifiedMiddleSpread, err := s.clk.CollectSpreadRewards(s.Ctx, middleAddress, fungifiedMiddle) - s.Require().NoError(err) - s.Require().Equal(collectedSpreadRewardsMap[middlePositionIds[0]].Add(collectedSpreadRewardsMap[middlePositionIds[1]]...).Add(truncatedETHCoins...), fungifiedMiddleSpread) - - // Right position spread reward assertion - fungifiedRightSpread, err := s.clk.CollectSpreadRewards(s.Ctx, rightAddress, fungifiedRight) - s.Require().NoError(err) - s.Require().True(fungifiedRightSpread.Empty()) - - // --- Incentive assertions on fungified positions --- - - // Left position incentives assertion - fungifiedLeftIncentives, forfeited, err := s.clk.CollectIncentives(s.Ctx, leftAddress, fungifiedLeft) - s.Require().NoError(err) - s.Require().True(forfeited.Empty()) - s.Require().Equal(collectedIncentivesMap[leftOne].Add(collectedIncentivesMap[leftTwo]...).Add(truncatedUSDCCoins...), fungifiedLeftIncentives) - - // Middle position incentives assertion - fungifiedMiddleIncentives, forfeited, err := s.clk.CollectIncentives(s.Ctx, middleAddress, fungifiedMiddle) - s.Require().NoError(err) - s.Require().True(forfeited.Empty()) - s.Require().True(fungifiedMiddleIncentives.Empty()) - - // Right position incentives assertion - fungifiedRightIncentives, forfeited, err := s.clk.CollectIncentives(s.Ctx, rightAddress, fungifiedRight) - s.Require().NoError(err) - s.Require().True(forfeited.Empty()) - s.Require().True(fungifiedRightIncentives.Empty()) -} - func (s *KeeperTestSuite) TestCreateFullRangePosition() { var ( - positionId uint64 - liquidity sdk.Dec + positionData cltypes.CreateFullRangePositionData concentratedLockId uint64 err error ) @@ -1660,11 +1023,11 @@ func (s *KeeperTestSuite) TestCreateFullRangePosition() { // System under test if test.isLocked { - positionId, _, _, liquidity, concentratedLockId, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPool.GetId(), defaultAddress, test.coinsForPosition, test.remainingLockDuration) + positionData, concentratedLockId, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPool.GetId(), defaultAddress, test.coinsForPosition, test.remainingLockDuration) } else if test.isUnlocking { - positionId, _, _, liquidity, concentratedLockId, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionUnlocking(s.Ctx, clPool.GetId(), defaultAddress, test.coinsForPosition, test.remainingLockDuration) + positionData, concentratedLockId, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionUnlocking(s.Ctx, clPool.GetId(), defaultAddress, test.coinsForPosition, test.remainingLockDuration) } else { - positionId, _, _, liquidity, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, clPool.GetId(), defaultAddress, test.coinsForPosition) + positionData, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, clPool.GetId(), defaultAddress, test.coinsForPosition) } if test.expectedErr != nil { @@ -1676,14 +1039,14 @@ func (s *KeeperTestSuite) TestCreateFullRangePosition() { s.Require().NoError(err) // Check position - _, err = s.App.ConcentratedLiquidityKeeper.GetPosition(s.Ctx, positionId) + _, err = s.App.ConcentratedLiquidityKeeper.GetPosition(s.Ctx, positionData.ID) s.Require().NoError(err) // Check lock if test.isLocked || test.isUnlocking { concentratedLock, err := s.App.LockupKeeper.GetLockByID(s.Ctx, concentratedLockId) s.Require().NoError(err) - s.Require().Equal(liquidity.TruncateInt().String(), concentratedLock.Coins[0].Amount.String()) + s.Require().Equal(positionData.Liquidity.TruncateInt().String(), concentratedLock.Coins[0].Amount.String()) isUnlocking := concentratedLock.IsUnlocking() s.Require().Equal(!test.isLocked, isUnlocking) } @@ -1756,12 +1119,16 @@ func (s *KeeperTestSuite) TestMintSharesAndLock() { liquidity := sdk.ZeroDec() //nolint:staticcheck if test.createFullRangePosition { var err error - positionId, _, _, liquidity, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, clPool.GetId(), test.owner, DefaultCoins) + positionData, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, clPool.GetId(), test.owner, DefaultCoins) s.Require().NoError(err) + positionId = positionData.ID + liquidity = positionData.Liquidity } else { var err error - positionId, _, _, liquidity, _, _, err = s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, clPool.GetId(), test.owner, defaultPositionCoins, sdk.ZeroInt(), sdk.ZeroInt(), test.lowerTick, test.upperTick) + positionData, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, clPool.GetId(), test.owner, defaultPositionCoins, sdk.ZeroInt(), sdk.ZeroInt(), test.lowerTick, test.upperTick) s.Require().NoError(err) + positionId = positionData.ID + liquidity = positionData.Liquidity } lockupModuleAccountBalancePre := s.App.LockupKeeper.GetModuleBalance(s.Ctx) @@ -1824,10 +1191,10 @@ func (s *KeeperTestSuite) TestPositionHasActiveUnderlyingLock() { name: "position with lock locked", createPosition: func(s *KeeperTestSuite) (uint64, uint64) { s.FundAcc(owner, defaultPositionCoins) - positionID, _, _, _, concentratedLockID, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked( + positionData, concentratedLockID, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked( s.Ctx, clPool.GetId(), owner, defaultPositionCoins, defaultLockDuration) s.Require().NoError(err) - return positionID, concentratedLockID + return positionData.ID, concentratedLockID }, expectedHasActiveLock: true, // lock starts as active expectedHasActiveLockAfterTimeUpdate: true, // since lock is locked, it remains active after time update @@ -1839,10 +1206,10 @@ func (s *KeeperTestSuite) TestPositionHasActiveUnderlyingLock() { name: "position with lock unlocking", createPosition: func(s *KeeperTestSuite) (uint64, uint64) { s.FundAcc(owner, defaultPositionCoins) - positionID, _, _, _, concentratedLockID, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionUnlocking( + positionData, concentratedLockID, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionUnlocking( s.Ctx, clPool.GetId(), owner, defaultPositionCoins, defaultLockDuration) s.Require().NoError(err) - return positionID, concentratedLockID + return positionData.ID, concentratedLockID }, expectedHasActiveLock: true, // lock starts as active expectedHasActiveLockAfterTimeUpdate: false, // since lock is unlocking, it should no longer be active after time update @@ -1854,10 +1221,10 @@ func (s *KeeperTestSuite) TestPositionHasActiveUnderlyingLock() { name: "position without lock", createPosition: func(s *KeeperTestSuite) (uint64, uint64) { s.FundAcc(owner, defaultPositionCoins) - positionID, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition( + positionData, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition( s.Ctx, clPool.GetId(), owner, defaultPositionCoins) s.Require().NoError(err) - return positionID, 0 + return positionData.ID, 0 }, expectedHasActiveLock: false, expectedHasActiveLockAfterTimeUpdate: false, @@ -1951,10 +1318,10 @@ func (s *KeeperTestSuite) TestPositionHasActiveUnderlyingLockAndUpdate() { name: "position with lock locked", createPosition: func(s *KeeperTestSuite) (uint64, uint64) { s.FundAcc(owner, defaultPositionCoins) - positionID, _, _, _, concentratedLockID, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked( + positionData, concentratedLockID, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked( s.Ctx, clPool.GetId(), owner, defaultPositionCoins, defaultLockDuration) s.Require().NoError(err) - return positionID, concentratedLockID + return positionData.ID, concentratedLockID }, expectedHasActiveLock: true, // lock starts as active expectedHasActiveLockAfterTimeUpdate: true, // since lock is locked, it remains active after time update @@ -1968,10 +1335,10 @@ func (s *KeeperTestSuite) TestPositionHasActiveUnderlyingLockAndUpdate() { name: "position with lock unlocking", createPosition: func(s *KeeperTestSuite) (uint64, uint64) { s.FundAcc(owner, defaultPositionCoins) - positionID, _, _, _, concentratedLockID, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionUnlocking( + positionData, concentratedLockID, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionUnlocking( s.Ctx, clPool.GetId(), owner, defaultPositionCoins, defaultLockDuration) s.Require().NoError(err) - return positionID, concentratedLockID + return positionData.ID, concentratedLockID }, expectedHasActiveLock: true, // lock starts as active expectedHasActiveLockAfterTimeUpdate: false, // since lock is unlocking, it should no longer be active after time update @@ -1985,10 +1352,10 @@ func (s *KeeperTestSuite) TestPositionHasActiveUnderlyingLockAndUpdate() { name: "position without lock", createPosition: func(s *KeeperTestSuite) (uint64, uint64) { s.FundAcc(owner, defaultPositionCoins) - positionID, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition( + positionData, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition( s.Ctx, clPool.GetId(), owner, defaultPositionCoins) s.Require().NoError(err) - return positionID, 0 + return positionData.ID, 0 }, expectedHasActiveLock: false, expectedHasActiveLockAfterTimeUpdate: false, @@ -2082,46 +1449,46 @@ func (s *KeeperTestSuite) TestPositionToLockCRUD() { s.FundAcc(owner, defaultPositionCoins) // Create a position with a lock - positionId, _, _, _, concentratedLockId, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionUnlocking(s.Ctx, clPool.GetId(), owner, defaultPositionCoins, remainingLockDuration) + positionData, concentratedLockId, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionUnlocking(s.Ctx, clPool.GetId(), owner, defaultPositionCoins, remainingLockDuration) s.Require().NoError(err) // We should be able to retrieve the lockId from the positionId now - retrievedLockId, err := s.App.ConcentratedLiquidityKeeper.GetLockIdFromPositionId(s.Ctx, positionId) + retrievedLockId, err := s.App.ConcentratedLiquidityKeeper.GetLockIdFromPositionId(s.Ctx, positionData.ID) s.Require().NoError(err) s.Require().Equal(concentratedLockId, retrievedLockId) // Check if lock has position in state retrievedPositionId, err := s.App.ConcentratedLiquidityKeeper.GetPositionIdToLockId(s.Ctx, retrievedLockId) s.Require().NoError(err) - s.Require().Equal(positionId, retrievedPositionId) + s.Require().Equal(positionData.ID, retrievedPositionId) // Create a position without a lock - positionId, _, _, _, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, clPool.GetId(), owner, defaultPositionCoins) + positionData, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, clPool.GetId(), owner, defaultPositionCoins) s.Require().Error(err) // Check if position has lock in state, should not - retrievedLockId, err = s.App.ConcentratedLiquidityKeeper.GetLockIdFromPositionId(s.Ctx, positionId) + retrievedLockId, err = s.App.ConcentratedLiquidityKeeper.GetLockIdFromPositionId(s.Ctx, positionData.ID) s.Require().Error(err) s.Require().Equal(uint64(0), retrievedLockId) // Set the position to have a lockId (despite it not actually having a lock) - s.App.ConcentratedLiquidityKeeper.SetPositionIdToLock(s.Ctx, positionId, concentratedLockId) + s.App.ConcentratedLiquidityKeeper.SetPositionIdToLock(s.Ctx, positionData.ID, concentratedLockId) // Check if position has lock in state, it should now - retrievedLockId, err = s.App.ConcentratedLiquidityKeeper.GetLockIdFromPositionId(s.Ctx, positionId) + retrievedLockId, err = s.App.ConcentratedLiquidityKeeper.GetLockIdFromPositionId(s.Ctx, positionData.ID) s.Require().NoError(err) s.Require().Equal(concentratedLockId, retrievedLockId) // Check if lock has position in state retrievedPositionId, err = s.App.ConcentratedLiquidityKeeper.GetPositionIdToLockId(s.Ctx, retrievedLockId) s.Require().NoError(err) - s.Require().Equal(positionId, retrievedPositionId) + s.Require().Equal(positionData.ID, retrievedPositionId) // Remove the lockId from the position - s.App.ConcentratedLiquidityKeeper.RemovePositionIdForLockId(s.Ctx, positionId, retrievedLockId) + s.App.ConcentratedLiquidityKeeper.RemovePositionIdForLockId(s.Ctx, positionData.ID, retrievedLockId) // Check if position has lock in state, should not - retrievedLockId, err = s.App.ConcentratedLiquidityKeeper.GetLockIdFromPositionId(s.Ctx, positionId) + retrievedLockId, err = s.App.ConcentratedLiquidityKeeper.GetLockIdFromPositionId(s.Ctx, positionData.ID) s.Require().Error(err) s.Require().Equal(uint64(0), retrievedLockId) } @@ -2254,9 +1621,9 @@ func (s *KeeperTestSuite) TestGetAndUpdateFullRangeLiquidity() { actualFullRangeLiquidity := sdk.ZeroDec() // Create a full range position. - _, _, _, liquidity, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, clPool.GetId(), owner, tc.positionCoins) + positionData, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, clPool.GetId(), owner, tc.positionCoins) s.Require().NoError(err) - actualFullRangeLiquidity = actualFullRangeLiquidity.Add(liquidity) + actualFullRangeLiquidity = actualFullRangeLiquidity.Add(positionData.Liquidity) _, err = s.App.ConcentratedLiquidityKeeper.GetPoolById(s.Ctx, clPoolId) s.Require().NoError(err) @@ -2268,7 +1635,7 @@ func (s *KeeperTestSuite) TestGetAndUpdateFullRangeLiquidity() { // Create a new position that overlaps with the min tick, but is not full range and therefore should not count towards the full range liquidity. s.FundAcc(owner, tc.positionCoins) - _, _, _, _, _, _, err = s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, clPoolId, owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), tc.lowerTick, tc.upperTick) + _, err = s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, clPoolId, owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), tc.lowerTick, tc.upperTick) s.Require().NoError(err) _, err = s.App.ConcentratedLiquidityKeeper.GetPoolById(s.Ctx, clPoolId) @@ -2373,7 +1740,7 @@ func (s *KeeperTestSuite) TestCreateFullRangePositionLocked() { s.FundAcc(defaultAddress, test.coinsForPosition) // System under test - positionId, _, _, liquidity, concentratedLockId, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPool.GetId(), defaultAddress, test.coinsForPosition, test.remainingLockDuration) + positionData, concentratedLockId, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPool.GetId(), defaultAddress, test.coinsForPosition, test.remainingLockDuration) if test.expectedErr != nil { s.Require().ErrorContains(err, test.expectedErr.Error()) @@ -2383,18 +1750,18 @@ func (s *KeeperTestSuite) TestCreateFullRangePositionLocked() { s.Require().NoError(err) // Check position - position, err := s.App.ConcentratedLiquidityKeeper.GetPosition(s.Ctx, positionId) + position, err := s.App.ConcentratedLiquidityKeeper.GetPosition(s.Ctx, positionData.ID) s.Require().NoError(err) s.Require().Equal(s.Ctx.BlockTime(), position.JoinTime) s.Require().Equal(types.MaxTick, position.UpperTick) s.Require().Equal(types.MinInitializedTick, position.LowerTick) - s.Require().Equal(liquidity, position.Liquidity) + s.Require().Equal(positionData.Liquidity, position.Liquidity) // Check locked concentratedLock, err := s.App.LockupKeeper.GetLockByID(s.Ctx, concentratedLockId) s.Require().NoError(err) - s.Require().Equal(concentratedLock.Coins[0].Amount.String(), liquidity.TruncateInt().String()) + s.Require().Equal(concentratedLock.Coins[0].Amount.String(), positionData.Liquidity.TruncateInt().String()) s.Require().False(concentratedLock.IsUnlocking()) }) } @@ -2656,7 +2023,7 @@ func (s *KeeperTestSuite) TestNegativeTickRange_SpreadFactor() { s.Require().True(toTick < pool.GetCurrentTick()) - amountZeroIn := math.CalcAmount0Delta(osmomath.BigDecFromSDKDec(pool.GetLiquidity()), pool.GetCurrentSqrtPrice(), osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(toTick)), true) + amountZeroIn := math.CalcAmount0Delta(osmomath.BigDecFromSDKDec(pool.GetLiquidity()), pool.GetCurrentSqrtPrice(), s.tickToSqrtPrice(toTick), true) coinZeroIn := sdk.NewCoin(denom0, amountZeroIn.SDKDec().TruncateInt()) return coinZeroIn @@ -2672,7 +2039,7 @@ func (s *KeeperTestSuite) TestNegativeTickRange_SpreadFactor() { s.Require().True(toTick > pool.GetCurrentTick()) - amountOneIn := math.CalcAmount1Delta(osmomath.BigDecFromSDKDec(pool.GetLiquidity()), pool.GetCurrentSqrtPrice(), osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(toTick)), true) + amountOneIn := math.CalcAmount1Delta(osmomath.BigDecFromSDKDec(pool.GetLiquidity()), pool.GetCurrentSqrtPrice(), s.tickToSqrtPrice(toTick), true) coinOneIn := sdk.NewCoin(denom1, amountOneIn.SDKDec().TruncateInt()) return coinOneIn @@ -2687,7 +2054,7 @@ func (s *KeeperTestSuite) TestNegativeTickRange_SpreadFactor() { // Initialize position at a higher range s.FundAcc(s.TestAccs[0], DefaultCoins) - _, _, _, _, _, _, err = s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, poolId, s.TestAccs[0], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultCurrTick+50, DefaultCurrTick+100) + _, err = s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, poolId, s.TestAccs[0], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultCurrTick+50, DefaultCurrTick+100) s.Require().NoError(err) // Estimate how much to swap in to approximately DefaultCurrTick - 50 @@ -2730,7 +2097,7 @@ func (s *KeeperTestSuite) TestNegativeTickRange_SpreadFactor() { // We initialized the lower tick's accumulator (DefaultCurrTick - 25) to be greater than the upper tick's accumulator (DefaultCurrTick + 50) // Whenever the current tick is above the position's range, we compute in range accumulator as upper tick accumulator - lower tick accumulator // In this case, it ends up being negative, which is now supported. - negativeIntervalAccumPositionId, _, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, poolId, s.TestAccs[0], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultCurrTick-25, DefaultCurrTick+50) + negativeIntervalAccumPositionData, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, poolId, s.TestAccs[0], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultCurrTick-25, DefaultCurrTick+50) s.Require().NoError(err) // Increase block time @@ -2857,7 +2224,7 @@ func (s *KeeperTestSuite) TestNegativeTickRange_SpreadFactor() { spreadRewardAccumulator, err := s.clk.GetSpreadRewardAccumulator(s.Ctx, poolId) s.Require().NoError(err) - accum, err := spreadRewardAccumulator.GetPosition(types.KeySpreadRewardPositionAccumulator(negativeIntervalAccumPositionId)) + accum, err := spreadRewardAccumulator.GetPosition(types.KeySpreadRewardPositionAccumulator(negativeIntervalAccumPositionData.ID)) s.Require().NoError(err) // Validate that at least one accumulator is negative for the test to be valid. diff --git a/x/concentrated-liquidity/query.go b/x/concentrated-liquidity/query.go index 5db18358839..cf547dabc46 100644 --- a/x/concentrated-liquidity/query.go +++ b/x/concentrated-liquidity/query.go @@ -23,7 +23,7 @@ import ( func (k Keeper) GetTickLiquidityForFullRange(ctx sdk.Context, poolId uint64) ([]queryproto.LiquidityDepthWithRange, error) { // use false for zeroForOne since we're going from lower tick -> upper tick zeroForOne := false - swapStrategy := swapstrategy.New(zeroForOne, sdk.ZeroDec(), k.storeKey, sdk.ZeroDec()) + swapStrategy := swapstrategy.New(zeroForOne, osmomath.ZeroDec(), k.storeKey, sdk.ZeroDec()) // set current tick to min tick, and find the first initialized tick starting from min tick -1. // we do -1 to make min tick inclusive. @@ -149,7 +149,7 @@ func (k Keeper) GetTickLiquidityNetInDirection(ctx sdk.Context, poolId uint64, t } liquidityDepths := []queryproto.TickLiquidityNet{} - swapStrategy := swapstrategy.New(zeroForOne, sdk.ZeroDec(), k.storeKey, sdk.ZeroDec()) + swapStrategy := swapstrategy.New(zeroForOne, osmomath.ZeroDec(), k.storeKey, sdk.ZeroDec()) currentTick := p.GetCurrentTick() _, currentTickSqrtPrice, err := math.TickToSqrtPrice(currentTick) @@ -168,7 +168,7 @@ func (k Keeper) GetTickLiquidityNetInDirection(ctx sdk.Context, poolId uint64, t } ctx.Logger().Debug(fmt.Sprintf("validateTick %s; validate sqrtPrice %s\n", validateTick.String(), validateSqrtPrice.String())) - if err := swapStrategy.ValidateSqrtPrice(validateSqrtPrice, osmomath.BigDecFromSDKDec(currentTickSqrtPrice)); err != nil { + if err := swapStrategy.ValidateSqrtPrice(validateSqrtPrice, currentTickSqrtPrice); err != nil { return err } diff --git a/x/concentrated-liquidity/query_test.go b/x/concentrated-liquidity/query_test.go index e18ef993722..ee40ac026fd 100644 --- a/x/concentrated-liquidity/query_test.go +++ b/x/concentrated-liquidity/query_test.go @@ -572,7 +572,7 @@ func (s *KeeperTestSuite) TestGetTickLiquidityNetInDirection() { s.Require().NoError(err) curTick = test.currentPoolTick - curSqrtPrice = osmomath.BigDecFromSDKDec(sqrtPrice) + curSqrtPrice = sqrtPrice } pool.SetCurrentSqrtPrice(curSqrtPrice) pool.SetCurrentTick(curTick) diff --git a/x/concentrated-liquidity/range_test.go b/x/concentrated-liquidity/range_test.go index a567ad376a6..2ec7acf92f8 100644 --- a/x/concentrated-liquidity/range_test.go +++ b/x/concentrated-liquidity/range_test.go @@ -210,12 +210,12 @@ func (s *KeeperTestSuite) setupRangesAndAssertInvariants(pool types.Concentrated cumulativeEmittedIncentives, lastIncentiveTrackerUpdate = s.trackEmittedIncentives(cumulativeEmittedIncentives, lastIncentiveTrackerUpdate) // Set up position - curPositionId, actualAmt0, actualAmt1, curLiquidity, actualLowerTick, actualUpperTick, err := s.clk.CreatePosition(s.Ctx, pool.GetId(), curAddr, curAssets, sdk.ZeroInt(), sdk.ZeroInt(), ranges[curRange][0], ranges[curRange][1]) + positionData, err := s.clk.CreatePosition(s.Ctx, pool.GetId(), curAddr, curAssets, sdk.ZeroInt(), sdk.ZeroInt(), ranges[curRange][0], ranges[curRange][1]) s.Require().NoError(err) // Ensure position was set up correctly and didn't break global invariants - s.Require().Equal(ranges[curRange][0], actualLowerTick) - s.Require().Equal(ranges[curRange][1], actualUpperTick) + s.Require().Equal(ranges[curRange][0], positionData.LowerTick) + s.Require().Equal(ranges[curRange][1], positionData.UpperTick) s.assertGlobalInvariants(ExpectedGlobalRewardValues{}) // Let time elapse after join if applicable @@ -226,14 +226,14 @@ func (s *KeeperTestSuite) setupRangesAndAssertInvariants(pool types.Concentrated s.assertGlobalInvariants(ExpectedGlobalRewardValues{}) // Track changes to state - actualAddedCoins := sdk.NewCoins(sdk.NewCoin(pool.GetToken0(), actualAmt0), sdk.NewCoin(pool.GetToken1(), actualAmt1)) + actualAddedCoins := sdk.NewCoins(sdk.NewCoin(pool.GetToken0(), positionData.Amount0), sdk.NewCoin(pool.GetToken1(), positionData.Amount1)) totalAssets = totalAssets.Add(actualAddedCoins...) if testParams.baseSwapAmount != (sdk.Int{}) { totalAssets = totalAssets.Add(swappedIn).Sub(sdk.NewCoins(swappedOut)) } - totalLiquidity = totalLiquidity.Add(curLiquidity) + totalLiquidity = totalLiquidity.Add(positionData.Liquidity) totalTimeElapsed = totalTimeElapsed + timeElapsed - allPositionIds = append(allPositionIds, curPositionId) + allPositionIds = append(allPositionIds, positionData.ID) curBlock++ } endNumPositions := len(allPositionIds) @@ -433,7 +433,7 @@ func (s *KeeperTestSuite) getInitialPositionAssets(pool types.ConcentratedPoolEx // Calculate asset amounts that would be required to get the required spot price (rounding up on asset1 to ensure we stay in the intended tick) asset0Amount := sdk.NewInt(100000000000000) - asset1Amount := sdk.NewDecFromInt(asset0Amount).Mul(requiredPrice).Ceil().TruncateInt() + asset1Amount := sdk.NewDecFromInt(asset0Amount).Mul(requiredPrice.SDKDec()).Ceil().TruncateInt() assetCoins := sdk.NewCoins( sdk.NewCoin(pool.GetToken0(), asset0Amount), diff --git a/x/concentrated-liquidity/simulation/sim_msgs.go b/x/concentrated-liquidity/simulation/sim_msgs.go index c2c679db57c..e7f647eb22f 100644 --- a/x/concentrated-liquidity/simulation/sim_msgs.go +++ b/x/concentrated-liquidity/simulation/sim_msgs.go @@ -233,7 +233,7 @@ func RandMsgCollectIncentives(k clkeeper.Keeper, sim *osmosimtypes.SimCtx, ctx s } // createPoolRestriction creates specific restriction for the creation of a pool. -func createPoolRestriction(k clkeeper.Keeper, sim *osmosimtypes.SimCtx, ctx sdk.Context) osmosimtypes.SimAccountConstraint { +func createPoolRestriction(sim *osmosimtypes.SimCtx, ctx sdk.Context) osmosimtypes.SimAccountConstraint { return func(acc legacysimulationtype.Account) bool { accCoins := sim.BankKeeper().SpendableCoins(ctx, acc.Address) hasTwoCoins := len(accCoins) >= 3 @@ -329,7 +329,7 @@ func RandomPreparePoolFunc(sim *osmosimtypes.SimCtx, ctx sdk.Context, k clkeeper authorizedSpreadFactor := cltypes.AuthorizedSpreadFactors // find an address with two or more distinct denoms in their wallet - sender, senderExists := sim.RandomSimAccountWithConstraint(createPoolRestriction(k, sim, ctx)) + sender, senderExists := sim.RandomSimAccountWithConstraint(createPoolRestriction(sim, ctx)) if !senderExists { return nil, sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, fmt.Errorf("no sender with two different denoms & pool creation fee exists") } diff --git a/x/concentrated-liquidity/spread_rewards_test.go b/x/concentrated-liquidity/spread_rewards_test.go index e97c34d6320..2d57fbd10d3 100644 --- a/x/concentrated-liquidity/spread_rewards_test.go +++ b/x/concentrated-liquidity/spread_rewards_test.go @@ -505,7 +505,7 @@ func (s *KeeperTestSuite) TestGetInitialSpreadRewardGrowthOppositeDirectionOfLas func (s *KeeperTestSuite) TestGetInitialSpreadRewardGrowthOppositeDirectionOfLastTraversalForTick() { sqrtPrice := osmomath.MustMonotonicSqrt(DefaultAmt1.ToDec().Quo(DefaultAmt0.ToDec())) - initialPoolTick, err := clmath.SqrtPriceToTickRoundDownSpacing(sqrtPrice, DefaultTickSpacing) + initialPoolTick, err := clmath.SqrtPriceToTickRoundDownSpacing(osmomath.BigDecFromSDKDec(sqrtPrice), DefaultTickSpacing) s.Require().NoError(err) initialGlobalSpreadRewardGrowth := oneEth @@ -1417,39 +1417,40 @@ func (s *KeeperTestSuite) TestFunctional_SpreadRewards_LP() { s.Require().Error(err) // Create position in the default range 1. - positionIdOne, _, _, liquidity, _, _, err := concentratedLiquidityKeeper.CreatePosition(ctx, pool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) + positionDataOne, err := concentratedLiquidityKeeper.CreatePosition(ctx, pool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) s.Require().NoError(err) // Swap once. ticksActivatedAfterEachSwap, totalSpreadRewardsExpected, _, _ := s.swapAndTrackXTimesInARow(pool.GetId(), DefaultCoin1, ETH, types.MaxSpotPrice, 1) // Withdraw half. - halfLiquidity := liquidity.Mul(sdk.NewDecWithPrec(5, 1)) - _, _, err = concentratedLiquidityKeeper.WithdrawPosition(ctx, owner, positionIdOne, halfLiquidity) + halfLiquidity := positionDataOne.Liquidity.Mul(sdk.NewDecWithPrec(5, 1)) + _, _, err = concentratedLiquidityKeeper.WithdrawPosition(ctx, owner, positionDataOne.ID, halfLiquidity) s.Require().NoError(err) // Collect spread rewards. - spreadRewardsCollected := s.collectSpreadRewardsAndCheckInvariance(ctx, 0, DefaultMinTick, DefaultMaxTick, positionIdOne, sdk.NewCoins(), []string{USDC}, [][]int64{ticksActivatedAfterEachSwap}) + spreadRewardsCollected := s.collectSpreadRewardsAndCheckInvariance(ctx, 0, DefaultMinTick, DefaultMaxTick, positionDataOne.ID, sdk.NewCoins(), []string{USDC}, [][]int64{ticksActivatedAfterEachSwap}) expectedSpreadRewardsTruncated := totalSpreadRewardsExpected for i, spreadRewardToken := range totalSpreadRewardsExpected { // We run expected spread rewards through a cycle of division and multiplication by liquidity to capture appropriate rounding behavior - expectedSpreadRewardsTruncated[i] = sdk.NewCoin(spreadRewardToken.Denom, spreadRewardToken.Amount.ToDec().QuoTruncate(liquidity).MulTruncate(liquidity).TruncateInt()) + expectedSpreadRewardsTruncated[i] = sdk.NewCoin(spreadRewardToken.Denom, spreadRewardToken.Amount.ToDec().QuoTruncate(positionDataOne.Liquidity).MulTruncate(positionDataOne.Liquidity).TruncateInt()) } s.Require().Equal(expectedSpreadRewardsTruncated, spreadRewardsCollected) // Unclaimed rewards should be emptied since spread rewards were collected. - s.validatePositionSpreadRewardGrowth(pool.GetId(), positionIdOne, cl.EmptyCoins) + s.validatePositionSpreadRewardGrowth(pool.GetId(), positionDataOne.ID, cl.EmptyCoins) // Create position in the default range 2. - positionIdTwo, _, _, fullLiquidity, _, _, err := concentratedLiquidityKeeper.CreatePosition(ctx, pool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) + positionDataTwo, err := concentratedLiquidityKeeper.CreatePosition(ctx, pool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) s.Require().NoError(err) + fullLiquidity := positionDataTwo.Liquidity // Swap once in the other direction. ticksActivatedAfterEachSwap, totalSpreadRewardsExpected, _, _ = s.swapAndTrackXTimesInARow(pool.GetId(), DefaultCoin0, USDC, types.MinSpotPrice, 1) // This should claim under the hood for position 2 since full liquidity is removed. balanceBeforeWithdraw := s.App.BankKeeper.GetBalance(ctx, owner, ETH) - amtDenom0, _, err := concentratedLiquidityKeeper.WithdrawPosition(ctx, owner, positionIdTwo, fullLiquidity) + amtDenom0, _, err := concentratedLiquidityKeeper.WithdrawPosition(ctx, owner, positionDataTwo.ID, positionDataTwo.Liquidity) s.Require().NoError(err) balanceAfterWithdraw := s.App.BankKeeper.GetBalance(ctx, owner, ETH) @@ -1459,20 +1460,20 @@ func (s *KeeperTestSuite) TestFunctional_SpreadRewards_LP() { s.Require().Equal(expectedPositionToWithdraw.String(), balanceAfterWithdraw.Sub(balanceBeforeWithdraw).Amount.Sub(amtDenom0).String()) // Validate cannot claim for withdrawn position. - _, err = s.App.ConcentratedLiquidityKeeper.CollectSpreadRewards(ctx, owner, positionIdTwo) + _, err = s.App.ConcentratedLiquidityKeeper.CollectSpreadRewards(ctx, owner, positionDataTwo.ID) s.Require().Error(err) - spreadRewardsCollected = s.collectSpreadRewardsAndCheckInvariance(ctx, 0, DefaultMinTick, DefaultMaxTick, positionIdOne, sdk.NewCoins(), []string{ETH}, [][]int64{ticksActivatedAfterEachSwap}) + spreadRewardsCollected = s.collectSpreadRewardsAndCheckInvariance(ctx, 0, DefaultMinTick, DefaultMaxTick, positionDataOne.ID, sdk.NewCoins(), []string{ETH}, [][]int64{ticksActivatedAfterEachSwap}) // total spread rewards * half liquidity / (full liquidity + half liquidity) expectesSpreadRewardsCollected := totalSpreadRewardsExpected.AmountOf(ETH).ToDec().Mul(halfLiquidity.Quo(fullLiquidity.Add(halfLiquidity))).TruncateInt() s.Require().Equal(expectesSpreadRewardsCollected.String(), spreadRewardsCollected.AmountOf(ETH).String()) // Create position in the default range 3. - positionIdThree, _, _, _, _, _, err := concentratedLiquidityKeeper.CreatePosition(ctx, pool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) + positionDataThree, err := concentratedLiquidityKeeper.CreatePosition(ctx, pool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) s.Require().NoError(err) - collectedThree, err := s.App.ConcentratedLiquidityKeeper.CollectSpreadRewards(ctx, owner, positionIdThree) + collectedThree, err := s.App.ConcentratedLiquidityKeeper.CollectSpreadRewards(ctx, owner, positionDataThree.ID) s.Require().NoError(err) s.Require().Equal(sdk.Coins(nil), collectedThree) } diff --git a/x/concentrated-liquidity/swaps.go b/x/concentrated-liquidity/swaps.go index fb5f0a3a3ae..7997a7a407b 100644 --- a/x/concentrated-liquidity/swaps.go +++ b/x/concentrated-liquidity/swaps.go @@ -306,10 +306,10 @@ func (k Keeper) swapSetup(ctx sdk.Context, // returns next initialized tick, next initialized tick sqrt price, implied sqrt price target, and error // the next initialized tick sqrt price return arg is unfortunately there due to some bigdec situations. // poolId as an argument is for debug info and should be removed in the future -func iteratorToNextInitializedTickSqrtPriceTarget(nextInitTickIter db.Iterator, _poolId uint64, swapstrat swapstrategy.SwapStrategy) (int64, sdk.Dec, sdk.Dec, error) { +func iteratorToNextInitializedTickSqrtPriceTarget(nextInitTickIter db.Iterator, _poolId uint64, swapstrat swapstrategy.SwapStrategy) (int64, osmomath.BigDec, osmomath.BigDec, error) { // Iterator must be valid to be able to retrieve the next tick from it below. if !nextInitTickIter.Valid() { - return 0, sdk.Dec{}, sdk.Dec{}, types.RanOutOfTicksForPoolError{PoolId: _poolId} + return 0, osmomath.BigDec{}, osmomath.BigDec{}, types.RanOutOfTicksForPoolError{PoolId: _poolId} } // We first check to see what the position of the nearest initialized tick is // if zeroForOneStrategy, we look to the left of the tick the current sqrt price is at @@ -317,13 +317,13 @@ func iteratorToNextInitializedTickSqrtPriceTarget(nextInitTickIter db.Iterator, // if no ticks are initialized (no users have created liquidity positions) then we return an error nextInitializedTick, err := types.TickIndexFromBytes(nextInitTickIter.Key()) if err != nil { - return 0, sdk.Dec{}, sdk.Dec{}, err + return 0, osmomath.BigDec{}, osmomath.BigDec{}, err } // Utilizing the next initialized tick, we find the corresponding nextInitializedTickSqrtPrice (the target sqrt price). _, nextInitializedTickSqrtPrice, err := math.TickToSqrtPrice(nextInitializedTick) if err != nil { - return 0, sdk.Dec{}, sdk.Dec{}, fmt.Errorf("could not convert next tick (%v) to nextSqrtPrice", nextInitializedTick) + return 0, osmomath.BigDec{}, osmomath.BigDec{}, fmt.Errorf("could not convert next tick (%v) to nextSqrtPrice", nextInitializedTick) } // If nextInitializedTickSqrtPrice exceeds the given price limit, we set the sqrtPriceTarget to the price limit. @@ -366,7 +366,7 @@ func (k Keeper) computeOutAmtGivenIn( // TODO: for now, we check if amountSpecifiedRemaining is GT 0.0000001. This is because there are times when the remaining // amount may be extremely small, and that small amount cannot generate and amountIn/amountOut and we are therefore left // in an infinite loop. - for swapState.amountSpecifiedRemaining.GT(smallestDec) && !swapState.sqrtPrice.Equal(osmomath.BigDecFromSDKDec(sqrtPriceLimit)) { + for swapState.amountSpecifiedRemaining.GT(smallestDec) && !swapState.sqrtPrice.Equal(sqrtPriceLimit) { // Log the sqrtPrice we start the iteration with sqrtPriceStart := swapState.sqrtPrice @@ -402,21 +402,19 @@ func (k Keeper) computeOutAmtGivenIn( // We add the amount of tokens we received (amountOut) from the ComputeSwapWithinBucketOutGivenIn(...) above to the amountCalculated accumulator swapState.amountCalculated.AddMut(amountOut) - nextInitializedTickSqrtPriceBigDec := osmomath.BigDecFromSDKDec(nextInitializedTickSqrtPrice) - // If ComputeSwapWithinBucketOutGivenIn(...) calculated a computedSqrtPrice that is equal to the nextInitializedTickSqrtPrice, this means all liquidity in the current // bucket has been consumed and we must move on to the next bucket to complete the swap - if nextInitializedTickSqrtPriceBigDec.Equal(computedSqrtPrice) { + if nextInitializedTickSqrtPrice.Equal(computedSqrtPrice) { swapState, err = k.swapCrossTickLogic(ctx, swapState, swapStrategy, nextInitializedTick, nextInitTickIter, p, spreadRewardAccumulator, uptimeAccums, tokenInMin.Denom) if err != nil { return sdk.Coin{}, sdk.Coin{}, PoolUpdates{}, sdk.Dec{}, err } - } else if edgeCaseInequalityBasedOnSwapStrategy(swapStrategy.ZeroForOne(), nextInitializedTickSqrtPriceBigDec, computedSqrtPrice) { + } else if edgeCaseInequalityBasedOnSwapStrategy(swapStrategy.ZeroForOne(), nextInitializedTickSqrtPrice, computedSqrtPrice) { // If, based on the swap strategy, the computedSqrtPrice matches the edge case inequality, we return an error. // This is an edge case that occurs when swapping at/near tick boundaries that will be fixed in the next release. // For now, we return an error and ask the user to try again with a different swap amount. - return sdk.Coin{}, sdk.Coin{}, PoolUpdates{}, sdk.Dec{}, types.ComputedSqrtPriceInequalityError{IsZeroForOne: swapStrategy.ZeroForOne(), ComputedSqrtPrice: computedSqrtPrice, NextInitializedTickSqrtPrice: nextInitializedTickSqrtPriceBigDec} + return sdk.Coin{}, sdk.Coin{}, PoolUpdates{}, sdk.Dec{}, types.ComputedSqrtPriceInequalityError{IsZeroForOne: swapStrategy.ZeroForOne(), ComputedSqrtPrice: computedSqrtPrice, NextInitializedTickSqrtPrice: nextInitializedTickSqrtPrice} } else if !sqrtPriceStart.Equal(computedSqrtPrice) { // Otherwise if the sqrtPrice calculated from ComputeSwapWithinBucketOutGivenIn(...) does not equal the sqrtPriceStart we started with at the // beginning of this iteration, we set the swapState tick to the corresponding tick of the computedSqrtPrice calculated from ComputeSwapWithinBucketOutGivenIn(...) @@ -494,7 +492,7 @@ func (k Keeper) computeInAmtGivenOut( // TODO: for now, we check if amountSpecifiedRemaining is GT 10^-18. This is because there are times when the remaining // amount may be extremely small, and that small amount cannot generate and amountIn/amountOut and we are therefore left // in an infinite loop. - for swapState.amountSpecifiedRemaining.GT(smallestDec) && !swapState.sqrtPrice.Equal(osmomath.BigDecFromSDKDec(sqrtPriceLimit)) { + for swapState.amountSpecifiedRemaining.GT(smallestDec) && !swapState.sqrtPrice.Equal(sqrtPriceLimit) { // log the sqrtPrice we start the iteration with sqrtPriceStart := swapState.sqrtPrice @@ -526,21 +524,19 @@ func (k Keeper) computeInAmtGivenOut( swapState.amountSpecifiedRemaining.SubMut(amountOut) swapState.amountCalculated.AddMut(amountIn.Add(spreadRewardChargeTotal)) - nextInitializedTickSqrtPriceBigDec := osmomath.BigDecFromSDKDec(nextInitializedTickSqrtPrice) - // If the ComputeSwapWithinBucketInGivenOut(...) calculated a computedSqrtPrice that is equal to the nextInitializedTickSqrtPrice, this means all liquidity in the current // bucket has been consumed and we must move on to the next bucket by crossing a tick to complete the swap - if nextInitializedTickSqrtPriceBigDec.Equal(computedSqrtPrice) { + if nextInitializedTickSqrtPrice.Equal(computedSqrtPrice) { swapState, err = k.swapCrossTickLogic(ctx, swapState, swapStrategy, nextInitializedTick, nextInitTickIter, p, spreadRewardAccumulator, uptimeAccums, tokenInDenom) if err != nil { return sdk.Coin{}, sdk.Coin{}, PoolUpdates{}, sdk.Dec{}, err } - } else if edgeCaseInequalityBasedOnSwapStrategy(swapStrategy.ZeroForOne(), nextInitializedTickSqrtPriceBigDec, computedSqrtPrice) { + } else if edgeCaseInequalityBasedOnSwapStrategy(swapStrategy.ZeroForOne(), nextInitializedTickSqrtPrice, computedSqrtPrice) { // If, based on the swap strategy, the computedSqrtPrice matches the edge case inequality, we return an error. // This is an edge case that occurs when swapping at/near tick boundaries that will be fixed in the next release. // For now, we return an error and ask the user to try again with a different swap amount. - return sdk.Coin{}, sdk.Coin{}, PoolUpdates{}, sdk.Dec{}, types.ComputedSqrtPriceInequalityError{IsZeroForOne: swapStrategy.ZeroForOne(), ComputedSqrtPrice: computedSqrtPrice, NextInitializedTickSqrtPrice: nextInitializedTickSqrtPriceBigDec} + return sdk.Coin{}, sdk.Coin{}, PoolUpdates{}, sdk.Dec{}, types.ComputedSqrtPriceInequalityError{IsZeroForOne: swapStrategy.ZeroForOne(), ComputedSqrtPrice: computedSqrtPrice, NextInitializedTickSqrtPrice: nextInitializedTickSqrtPrice} } else if !sqrtPriceStart.Equal(computedSqrtPrice) { // Otherwise, if the computedSqrtPrice calculated from ComputeSwapWithinBucketInGivenOut(...) does not equal the sqrtPriceStart we started with at the // beginning of this iteration, we set the swapState tick to the corresponding tick of the computedSqrtPrice calculated from ComputeSwapWithinBucketInGivenOut(...) @@ -724,13 +720,13 @@ func checkDenomValidity(inDenom, outDenom, asset0, asset1 string) error { return nil } -func (k Keeper) setupSwapStrategy(p types.ConcentratedPoolExtension, spreadFactor sdk.Dec, tokenInDenom string, priceLimit sdk.Dec) (strategy swapstrategy.SwapStrategy, sqrtPriceLimit sdk.Dec, err error) { +func (k Keeper) setupSwapStrategy(p types.ConcentratedPoolExtension, spreadFactor sdk.Dec, tokenInDenom string, priceLimit sdk.Dec) (strategy swapstrategy.SwapStrategy, sqrtPriceLimit osmomath.BigDec, err error) { zeroForOne := getZeroForOne(tokenInDenom, p.GetToken0()) // take provided price limit and turn this into a sqrt price limit since formulas use sqrtPrice sqrtPriceLimit, err = swapstrategy.GetSqrtPriceLimit(priceLimit, zeroForOne) if err != nil { - return strategy, sdk.Dec{}, types.SqrtRootCalculationError{SqrtPriceLimit: sqrtPriceLimit} + return strategy, osmomath.BigDec{}, types.SqrtRootCalculationError{SqrtPriceLimit: sqrtPriceLimit} } // set the swap strategy @@ -739,7 +735,7 @@ func (k Keeper) setupSwapStrategy(p types.ConcentratedPoolExtension, spreadFacto // get current sqrt price from pool curSqrtPrice := p.GetCurrentSqrtPrice() if err := swapStrategy.ValidateSqrtPrice(sqrtPriceLimit, curSqrtPrice); err != nil { - return strategy, sdk.Dec{}, err + return strategy, osmomath.BigDec{}, err } return swapStrategy, sqrtPriceLimit, nil @@ -870,10 +866,8 @@ func (k Keeper) ComputeMaxInAmtGivenMaxTicksCrossed( totalTokenOut = totalTokenOut.Add(amountOut) // Check if the tick needs to be updated - nextInitializedTickSqrtPriceBigDec := osmomath.BigDecFromSDKDec(nextInitializedTickSqrtPrice) - // We do not need to track spread rewards or uptime accums here since we are not actually swapping. - if nextInitializedTickSqrtPriceBigDec.Equal(computedSqrtPrice) { + if nextInitializedTickSqrtPrice.Equal(computedSqrtPrice) { nextInitializedTickInfo, err := ParseTickFromBz(nextInitTickIter.Value()) if err != nil { return sdk.Coin{}, sdk.Coin{}, err @@ -886,11 +880,11 @@ func (k Keeper) ComputeMaxInAmtGivenMaxTicksCrossed( swapState.liquidity.AddMut(liquidityNet) swapState.tick = swapStrategy.UpdateTickAfterCrossing(nextInitializedTick) - } else if edgeCaseInequalityBasedOnSwapStrategy(swapStrategy.ZeroForOne(), nextInitializedTickSqrtPriceBigDec, computedSqrtPrice) { + } else if edgeCaseInequalityBasedOnSwapStrategy(swapStrategy.ZeroForOne(), nextInitializedTickSqrtPrice, computedSqrtPrice) { return sdk.Coin{}, sdk.Coin{}, types.ComputedSqrtPriceInequalityError{ IsZeroForOne: swapStrategy.ZeroForOne(), ComputedSqrtPrice: computedSqrtPrice, - NextInitializedTickSqrtPrice: nextInitializedTickSqrtPriceBigDec, + NextInitializedTickSqrtPrice: nextInitializedTickSqrtPrice, } } else if !swapState.sqrtPrice.Equal(computedSqrtPrice) { newTick, err := math.CalculateSqrtPriceToTick(computedSqrtPrice) diff --git a/x/concentrated-liquidity/swaps_test.go b/x/concentrated-liquidity/swaps_test.go index a2385a3d2c7..3615d3cf8b0 100644 --- a/x/concentrated-liquidity/swaps_test.go +++ b/x/concentrated-liquidity/swaps_test.go @@ -9,7 +9,7 @@ import ( "github.com/osmosis-labs/osmosis/osmomath" "github.com/osmosis-labs/osmosis/v17/app/apptesting" cl "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity" - "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/math" + clmath "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/math" "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" poolmanagertypes "github.com/osmosis-labs/osmosis/v17/x/poolmanager/types" ) @@ -757,7 +757,7 @@ var ( expectedTokenIn: sdk.NewCoin("eth", sdk.NewInt(12892)), expectedTokenOut: sdk.NewCoin("usdc", sdk.NewInt(64417624)), expectedTick: func() int64 { - tick, _ := math.SqrtPriceToTickRoundDownSpacing(sqrt4994, DefaultTickSpacing) + tick, _ := clmath.SqrtPriceToTickRoundDownSpacing(osmomath.BigDecFromSDKDec(sqrt4994), DefaultTickSpacing) return tick }(), // Since the next sqrt price is based on the price limit, we can calculate this directly. @@ -895,7 +895,7 @@ var ( expectedTokenOut: sdk.NewCoin("usdc", sdk.NewInt(64417624)), expectedSpreadRewardGrowthAccumulatorValue: sdk.MustNewDecFromStr("0.000000085792039652"), expectedTick: func() int64 { - tick, _ := math.SqrtPriceToTickRoundDownSpacing(sqrt4994, DefaultTickSpacing) + tick, _ := clmath.SqrtPriceToTickRoundDownSpacing(osmomath.BigDecFromSDKDec(sqrt4994), DefaultTickSpacing) return tick }(), expectedSqrtPrice: osmomath.MustNewDecFromStr("70.668238976219012614"), @@ -2026,9 +2026,9 @@ func (s *KeeperTestSuite) getExpectedLiquidity(test SwapTest, pool types.Concent newLowerTick, newUpperTick := s.lowerUpperPricesToTick(test.newLowerPrice, test.newUpperPrice, pool.GetTickSpacing()) - _, lowerSqrtPrice, err := math.TickToSqrtPrice(newLowerTick) + _, lowerSqrtPrice, err := clmath.TickToSqrtPrice(newLowerTick) s.Require().NoError(err) - _, upperSqrtPrice, err := math.TickToSqrtPrice(newUpperTick) + _, upperSqrtPrice, err := clmath.TickToSqrtPrice(newUpperTick) s.Require().NoError(err) if test.poolLiqAmount0.IsNil() && test.poolLiqAmount1.IsNil() { @@ -2036,16 +2036,16 @@ func (s *KeeperTestSuite) getExpectedLiquidity(test SwapTest, pool types.Concent test.poolLiqAmount1 = DefaultAmt1 } - expectedLiquidity := math.GetLiquidityFromAmounts(DefaultCurrSqrtPrice, lowerSqrtPrice, upperSqrtPrice, test.poolLiqAmount0, test.poolLiqAmount1) + expectedLiquidity := clmath.GetLiquidityFromAmounts(DefaultCurrSqrtPrice, lowerSqrtPrice, upperSqrtPrice, test.poolLiqAmount0, test.poolLiqAmount1) return expectedLiquidity } func (s *KeeperTestSuite) lowerUpperPricesToTick(lowerPrice, upperPrice sdk.Dec, tickSpacing uint64) (int64, int64) { lowerSqrtPrice := osmomath.MustMonotonicSqrt(lowerPrice) - newLowerTick, err := math.SqrtPriceToTickRoundDownSpacing(lowerSqrtPrice, tickSpacing) + newLowerTick, err := clmath.SqrtPriceToTickRoundDownSpacing(osmomath.BigDecFromSDKDec(lowerSqrtPrice), tickSpacing) s.Require().NoError(err) upperSqrtPrice := osmomath.MustMonotonicSqrt(upperPrice) - newUpperTick, err := math.SqrtPriceToTickRoundDownSpacing(upperSqrtPrice, tickSpacing) + newUpperTick, err := clmath.SqrtPriceToTickRoundDownSpacing(osmomath.BigDecFromSDKDec(upperSqrtPrice), tickSpacing) s.Require().NoError(err) return newLowerTick, newUpperTick } @@ -2760,7 +2760,7 @@ func (s *KeeperTestSuite) setupSecondPosition(test SwapTest, pool types.Concentr if !test.secondPositionLowerPrice.IsNil() { newLowerTick, newUpperTick := s.lowerUpperPricesToTick(test.secondPositionLowerPrice, test.secondPositionUpperPrice, pool.GetTickSpacing()) - _, _, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, pool.GetId(), s.TestAccs[1], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), newLowerTick, newUpperTick) + _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, pool.GetId(), s.TestAccs[1], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), newLowerTick, newUpperTick) s.Require().NoError(err) } } @@ -3395,7 +3395,7 @@ func (s *KeeperTestSuite) TestInfiniteSwapLoop_OutGivenIn() { // Create position near min tick s.FundAcc(positionOwner, DefaultRangeTestParams.baseAssets.Add(DefaultRangeTestParams.baseAssets...)) - _, _, _, _, _, _, err := s.clk.CreatePosition(s.Ctx, pool.GetId(), positionOwner, DefaultRangeTestParams.baseAssets, sdk.ZeroInt(), sdk.ZeroInt(), -108000000, -107999900) + _, err := s.clk.CreatePosition(s.Ctx, pool.GetId(), positionOwner, DefaultRangeTestParams.baseAssets, sdk.ZeroInt(), sdk.ZeroInt(), -108000000, -107999900) s.Require().NoError(err) // Swap small amount to get current tick to position above, triggering the problematic function/branch (CalcAmount0Delta) @@ -3506,7 +3506,9 @@ func (s *KeeperTestSuite) TestComputeMaxInAmtGivenMaxTicksCrossed() { func (s *KeeperTestSuite) createPositionAndFundAcc(clPool types.ConcentratedPoolExtension, lowerTick, upperTick int64) (amt0, amt1 sdk.Int) { s.FundAcc(s.TestAccs[0], DefaultCoins) - _, amt0, amt1, _, _, _, _ = s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, clPool.GetId(), s.TestAccs[0], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), lowerTick, upperTick) + positionData, _ := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, clPool.GetId(), s.TestAccs[0], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), lowerTick, upperTick) + amt0 = positionData.Amount0 + amt1 = positionData.Amount1 return } diff --git a/x/concentrated-liquidity/swaps_tick_cross_test.go b/x/concentrated-liquidity/swaps_tick_cross_test.go index 40a5050f8cb..ce58e6591b5 100644 --- a/x/concentrated-liquidity/swaps_tick_cross_test.go +++ b/x/concentrated-liquidity/swaps_tick_cross_test.go @@ -50,19 +50,19 @@ func (s *KeeperTestSuite) CreatePositionTickSpacingsFromCurrentTick(poolId uint6 lowerTick := currentTick - int64(tickSpacingsAwayFromCurrentTick)*tickSpacing upperTick := currentTick + int64(tickSpacingsAwayFromCurrentTick)*tickSpacing s.FundAcc(s.TestAccs[0], DefaultCoins) - positionId, _, _, liquidityNarrowRangeTwo, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, pool.GetId(), s.TestAccs[0], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), lowerTick, upperTick) + positionData, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, pool.GetId(), s.TestAccs[0], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), lowerTick, upperTick) s.Require().NoError(err) return positionMeta{ - positionId: positionId, + positionId: positionData.ID, lowerTick: lowerTick, upperTick: upperTick, - liquidity: liquidityNarrowRangeTwo, + liquidity: positionData.Liquidity, } } // tickToSqrtPrice a helper to convert a tick to a sqrt price. -func (s *KeeperTestSuite) tickToSqrtPrice(tick int64) sdk.Dec { +func (s *KeeperTestSuite) tickToSqrtPrice(tick int64) osmomath.BigDec { _, sqrtPrice, err := math.TickToSqrtPrice(tick) s.Require().NoError(err) return sqrtPrice @@ -93,7 +93,7 @@ func (s *KeeperTestSuite) validateIteratorRightOneForZero(poolId uint64, expecte s.Require().NoError(err) // Setup swap strategy directly as it would fail validation if constructed via SetupSwapStrategy(...) - oneForZeroSwapStrategy := swapstrategy.New(false, types.MaxSqrtPrice, s.App.GetKey(types.ModuleName), sdk.ZeroDec()) + oneForZeroSwapStrategy := swapstrategy.New(false, osmomath.BigDecFromSDKDec(types.MaxSqrtPrice), s.App.GetKey(types.ModuleName), sdk.ZeroDec()) s.Require().NoError(err) initializedTickValue := pool.GetCurrentTick() iter := oneForZeroSwapStrategy.InitializeNextTickIterator(s.Ctx, pool.GetId(), initializedTickValue) @@ -204,7 +204,7 @@ func (s *KeeperTestSuite) setupPoolAndPositions(testTickSpacing uint64, position // Create a full range position s.FundAcc(s.TestAccs[0], DefaultCoins) - _, _, _, liquidityFullRange, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, poolId, s.TestAccs[0], initialCoins) + positionData, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, poolId, s.TestAccs[0], initialCoins) s.Require().NoError(err) // Refetch pool as the first position updated its state. @@ -214,7 +214,7 @@ func (s *KeeperTestSuite) setupPoolAndPositions(testTickSpacing uint64, position // Create all narrow range positions per given tick spacings away from the current tick // configuration. positionMetas := make([]positionMeta, len(positionTickSpacingsFromCurrTick)) - liquidityAllPositions := liquidityFullRange + liquidityAllPositions := positionData.Liquidity for i, tickSpacingsAway := range positionTickSpacingsFromCurrTick { // Create narrow range position tickSpacingsAway from the current tick positionMetas[i] = s.CreatePositionTickSpacingsFromCurrentTick(poolId, tickSpacingsAway) @@ -276,7 +276,7 @@ func (s *KeeperTestSuite) computeSwapAmounts(poolId uint64, curSqrtPrice osmomat currentTick := originalCurrentTick // compute current sqrt price if not provided if curSqrtPrice.IsNil() { - curSqrtPrice = osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(currentTick)) + curSqrtPrice = s.tickToSqrtPrice(currentTick) } // Start from current pool liquidity and zero amount in. @@ -286,7 +286,7 @@ func (s *KeeperTestSuite) computeSwapAmounts(poolId uint64, curSqrtPrice osmomat for i, liquidityNetEntry := range liquidityNetAmounts { // Initialize the next initialized tick and its sqrt price. nextInitializedTick := liquidityNetEntry.TickIndex - nextInitTickSqrtPrice := osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(nextInitializedTick)) + nextInitTickSqrtPrice := s.tickToSqrtPrice(nextInitializedTick) // Handle swap depending on the direction. // Left (zero for one) or right (one for zero) @@ -302,7 +302,7 @@ func (s *KeeperTestSuite) computeSwapAmounts(poolId uint64, curSqrtPrice osmomat shouldCrossTick := currentTick > expectedTickToSwapTo && !shouldStayWithinTheSameBucket if shouldCrossTick { // Runs regular tick crossing logic. - curSqrtPrice = osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(nextInitializedTick)) + curSqrtPrice = s.tickToSqrtPrice(nextInitializedTick) currentLiquidity = currentLiquidity.Sub(liquidityNetEntry.LiquidityNet) currentTick = nextInitializedTick - 1 } @@ -313,7 +313,7 @@ func (s *KeeperTestSuite) computeSwapAmounts(poolId uint64, curSqrtPrice osmomat // This in an edge case when going left in second swap after previously going right // and indetending to stay within the same bucket. if amountIn.IsZero() && isWithinDesiredBucketAfterSwap { - nextInitTickSqrtPrice := osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(liquidityNetAmounts[i+1].TickIndex)) + nextInitTickSqrtPrice := s.tickToSqrtPrice(liquidityNetAmounts[i+1].TickIndex) // We discount by half so that we do no cross any tick and remain in the same bucket. curAmountIn := math.CalcAmount0Delta(osmomath.BigDecFromSDKDec(currentLiquidity), curSqrtPrice, nextInitTickSqrtPrice, true).QuoInt64(2).SDKDecRoundUp() @@ -329,7 +329,7 @@ func (s *KeeperTestSuite) computeSwapAmounts(poolId uint64, curSqrtPrice osmomat shouldCrossTick := currentTick <= expectedTickToSwapTo && !shouldStayWithinTheSameBucket if shouldCrossTick { // Runs regular tick crossing logic. - curSqrtPrice = osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(nextInitializedTick)) + curSqrtPrice = s.tickToSqrtPrice(nextInitializedTick) currentLiquidity = currentLiquidity.Add(liquidityNetEntry.LiquidityNet) currentTick = nextInitializedTick } @@ -364,7 +364,7 @@ func (s *KeeperTestSuite) computeSwapAmountsInGivenOut(poolId uint64, curSqrtPri currentTick := originalCurrentTick // compute current sqrt price if not provided if curSqrtPrice.IsNil() { - curSqrtPrice = osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(currentTick)) + curSqrtPrice = s.tickToSqrtPrice(currentTick) } // Start from current pool liquidity and zero amount in. @@ -374,7 +374,7 @@ func (s *KeeperTestSuite) computeSwapAmountsInGivenOut(poolId uint64, curSqrtPri for i, liquidityNetEntry := range liquidityNetAmounts { // Initialize the next initialized tick and its sqrt price. nextInitializedTick := liquidityNetEntry.TickIndex - nextInitTickSqrtPrice := osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(nextInitializedTick)) + nextInitTickSqrtPrice := s.tickToSqrtPrice(nextInitializedTick) // Handle swap depending on the direction. // Left (zero for one) or right (one for zero) @@ -390,7 +390,7 @@ func (s *KeeperTestSuite) computeSwapAmountsInGivenOut(poolId uint64, curSqrtPri shouldCrossTick := currentTick > expectedTickToSwapTo && !shouldStayWithinTheSameBucket if shouldCrossTick { // Runs regular tick crossing logic. - curSqrtPrice = osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(nextInitializedTick)) + curSqrtPrice = s.tickToSqrtPrice(nextInitializedTick) currentLiquidity = currentLiquidity.Sub(osmomath.BigDecFromSDKDec(liquidityNetEntry.LiquidityNet)) currentTick = nextInitializedTick - 1 } @@ -401,7 +401,7 @@ func (s *KeeperTestSuite) computeSwapAmountsInGivenOut(poolId uint64, curSqrtPri // This in an edge case when going left in second swap after previously going right // and indetending to stay within the same bucket. if amountOut.IsZero() && isWithinDesiredBucketAfterSwap { - nextInitTickSqrtPrice := osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(liquidityNetAmounts[i+1].TickIndex)) + nextInitTickSqrtPrice := s.tickToSqrtPrice(liquidityNetAmounts[i+1].TickIndex) // We discount by two so that we do no cross any tick and remain in the same bucket. curAmountIn := math.CalcAmount1Delta(currentLiquidity, curSqrtPrice, nextInitTickSqrtPrice, false).QuoInt64(2) @@ -417,7 +417,7 @@ func (s *KeeperTestSuite) computeSwapAmountsInGivenOut(poolId uint64, curSqrtPri shouldCrossTick := currentTick <= expectedTickToSwapTo && !shouldStayWithinTheSameBucket if shouldCrossTick { // Runs regular tick crossing logic. - curSqrtPrice = osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(nextInitializedTick)) + curSqrtPrice = s.tickToSqrtPrice(nextInitializedTick) currentLiquidity = currentLiquidity.Add(osmomath.BigDecFromSDKDec(liquidityNetEntry.LiquidityNet)) currentTick = nextInitializedTick } @@ -626,7 +626,7 @@ func (s *KeeperTestSuite) TestSwapOutGivenIn_Tick_Initialization_And_Crossing() // Compute the sqrt price corresponding to the lower tick // of the narrow range position. - sqrtPriceTarget := osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(tickToSwapTo)) + sqrtPriceTarget := s.tickToSqrtPrice(tickToSwapTo) // Check that narrow range position is considered in range isNarrowInRange := pool.IsCurrentTickInRange(nr1Position.lowerTick, nr1Position.upperTick) @@ -640,7 +640,7 @@ func (s *KeeperTestSuite) TestSwapOutGivenIn_Tick_Initialization_And_Crossing() ) if tickToSwapTo < nr1Position.lowerTick { - sqrtPriceLowerTickOne := osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(nr1Position.lowerTick)) + sqrtPriceLowerTickOne := s.tickToSqrtPrice(nr1Position.lowerTick) amountZeroIn = math.CalcAmount0Delta(osmomath.BigDecFromSDKDec(liquidity), sqrtPriceLowerTickOne, sqrtPriceStart, true).SDKDec() @@ -731,7 +731,7 @@ func (s *KeeperTestSuite) TestSwapOutGivenIn_Tick_Initialization_And_Crossing() ) if tickToSwapTo >= nr1Position.upperTick { - sqrtPriceUpperOne := osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(nr1Position.upperTick)) + sqrtPriceUpperOne := s.tickToSqrtPrice(nr1Position.upperTick) amountOneIn = math.CalcAmount1Delta(osmomath.BigDecFromSDKDec(liquidity), sqrtPriceUpperOne, sqrtPriceStart, true).SDKDecRoundUp() @@ -742,7 +742,7 @@ func (s *KeeperTestSuite) TestSwapOutGivenIn_Tick_Initialization_And_Crossing() // This is the total amount necessary to cross the lower tick of narrow position. // Note it is rounded up to ensure that the tick is crossed. - amountOneIn = math.CalcAmount1Delta(osmomath.BigDecFromSDKDec(liquidity), osmomath.BigDecFromSDKDec(sqrtPriceTarget), sqrtPriceStart, true).SDKDecRoundUp().Add(amountOneIn) + amountOneIn = math.CalcAmount1Delta(osmomath.BigDecFromSDKDec(liquidity), sqrtPriceTarget, sqrtPriceStart, true).SDKDecRoundUp().Add(amountOneIn) tokenOneIn := sdk.NewCoin(pool.GetToken1(), amountOneIn.Ceil().TruncateInt()) @@ -1415,15 +1415,15 @@ func (s *KeeperTestSuite) TestSwapOutGivenIn_GetLiquidityFromAmountsPositionBoun positionThreeTS = positions[1] positionThreeTSLowerTick = positionThreeTS.lowerTick positionThreeTSUpperTick = positionThreeTS.upperTick - positionThreeTSLowerSqrtPrice = osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(positionThreeTSLowerTick)) - positionThreeTSUpperSqrtPrice = osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(positionThreeTSUpperTick)) + positionThreeTSLowerSqrtPrice = s.tickToSqrtPrice(positionThreeTSLowerTick) + positionThreeTSUpperSqrtPrice = s.tickToSqrtPrice(positionThreeTSUpperTick) // 2 tick spacings away [30999998, 31000002) (2TS) from the original current tick (31000000) positionTwoTS = positions[2] positionTwoTSLowerTick = positionTwoTS.lowerTick positionTwoTSUpperTick = positionTwoTS.upperTick - positionTwoTSLowerSqrtPrice = osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(positionTwoTSLowerTick)) - positionTwoTSUpperSqrtPrice = osmomath.BigDecFromSDKDec(s.tickToSqrtPrice(positionTwoTSUpperTick)) + positionTwoTSLowerSqrtPrice = s.tickToSqrtPrice(positionTwoTSLowerTick) + positionTwoTSUpperSqrtPrice = s.tickToSqrtPrice(positionTwoTSUpperTick) ) // Assert that the liquidity computed from amounts utilized the "in-range" option. @@ -1431,7 +1431,7 @@ func (s *KeeperTestSuite) TestSwapOutGivenIn_GetLiquidityFromAmountsPositionBoun liquidity0 := math.Liquidity0(DefaultAmt0, currentSqrtPrice, upperTickSqrtPrice) liquidity1 := math.Liquidity1(DefaultAmt1, currentSqrtPrice, lowerTickSqrtPrice) expectedLiquidity := sdk.MinDec(liquidity0, liquidity1) - actualLiquidity := math.GetLiquidityFromAmounts(currentSqrtPrice, lowerTickSqrtPrice.SDKDec(), upperTickSqrtPrice.SDKDec(), DefaultAmt0, DefaultAmt1) + actualLiquidity := math.GetLiquidityFromAmounts(currentSqrtPrice, lowerTickSqrtPrice, upperTickSqrtPrice, DefaultAmt0, DefaultAmt1) s.Require().Equal(expectedLiquidity, actualLiquidity) } @@ -1492,7 +1492,7 @@ func (s *KeeperTestSuite) TestSwapOutGivenIn_GetLiquidityFromAmountsPositionBoun // 2TS position should consist of token zero only as it is to the right of the active range. liquidity02TS := math.Liquidity0(DefaultAmt0, currentSqrtPrice, positionTwoTSUpperSqrtPrice) - actualLiquidity2Ts := math.GetLiquidityFromAmounts(pool.GetCurrentSqrtPrice(), positionTwoTSLowerSqrtPrice.SDKDec(), s.tickToSqrtPrice(positionTwoTS.upperTick), DefaultAmt0, DefaultAmt1) + actualLiquidity2Ts := math.GetLiquidityFromAmounts(pool.GetCurrentSqrtPrice(), positionTwoTSLowerSqrtPrice, s.tickToSqrtPrice(positionTwoTS.upperTick), DefaultAmt0, DefaultAmt1) s.Require().Equal(liquidity02TS, actualLiquidity2Ts) // Reset suite context @@ -1541,7 +1541,7 @@ func (s *KeeperTestSuite) TestSwapOutGivenIn_GetLiquidityFromAmountsPositionBoun // 2TS should consist of token one only as it is to the right of the active range. liquidity1Pos3 := math.Liquidity1(DefaultAmt1, currentSqrtPrice, positionTwoTSLowerSqrtPrice) - actualLiquidityPos3 := math.GetLiquidityFromAmounts(pool.GetCurrentSqrtPrice(), positionTwoTSLowerSqrtPrice.SDKDec(), positionTwoTSUpperSqrtPrice.SDKDec(), DefaultAmt0, DefaultAmt1) + actualLiquidityPos3 := math.GetLiquidityFromAmounts(pool.GetCurrentSqrtPrice(), positionTwoTSLowerSqrtPrice, positionTwoTSUpperSqrtPrice, DefaultAmt0, DefaultAmt1) s.Require().Equal(liquidity1Pos3, actualLiquidityPos3) // Reset suite context diff --git a/x/concentrated-liquidity/swapstrategy/one_for_zero.go b/x/concentrated-liquidity/swapstrategy/one_for_zero.go index c6ada4347eb..6fe8109c3ac 100644 --- a/x/concentrated-liquidity/swapstrategy/one_for_zero.go +++ b/x/concentrated-liquidity/swapstrategy/one_for_zero.go @@ -19,7 +19,7 @@ import ( // With this strategy, we are moving to the right of the current // tick index and square root price. type oneForZeroStrategy struct { - sqrtPriceLimit sdk.Dec + sqrtPriceLimit osmomath.BigDec storeKey sdk.StoreKey spreadFactor sdk.Dec } @@ -31,7 +31,7 @@ func (s oneForZeroStrategy) ZeroForOne() bool { return false } // GetSqrtTargetPrice returns the target square root price given the next tick square root price. // If the given nextTickSqrtPrice is greater than the sqrt price limit, the sqrt price limit is returned. // Otherwise, the input nextTickSqrtPrice is returned. -func (s oneForZeroStrategy) GetSqrtTargetPrice(nextTickSqrtPrice sdk.Dec) sdk.Dec { +func (s oneForZeroStrategy) GetSqrtTargetPrice(nextTickSqrtPrice osmomath.BigDec) osmomath.BigDec { if nextTickSqrtPrice.GT(s.sqrtPriceLimit) { return s.sqrtPriceLimit } @@ -57,13 +57,12 @@ func (s oneForZeroStrategy) GetSqrtTargetPrice(nextTickSqrtPrice sdk.Dec) sdk.De // // OneForZero details: // - oneForZeroStrategy assumes moving to the right of the current square root price. -func (s oneForZeroStrategy) ComputeSwapWithinBucketOutGivenIn(sqrtPriceCurrent osmomath.BigDec, sqrtPriceTarget, liquidity, amountOneInRemaining sdk.Dec) (osmomath.BigDec, sdk.Dec, sdk.Dec, sdk.Dec) { - sqrtPriceTargetBigDec := osmomath.BigDecFromSDKDec(sqrtPriceTarget) +func (s oneForZeroStrategy) ComputeSwapWithinBucketOutGivenIn(sqrtPriceCurrent, sqrtPriceTarget osmomath.BigDec, liquidity, amountOneInRemaining sdk.Dec) (osmomath.BigDec, sdk.Dec, sdk.Dec, sdk.Dec) { liquidityBigDec := osmomath.BigDecFromSDKDec(liquidity) amountOneInRemainingBigDec := osmomath.BigDecFromSDKDec(amountOneInRemaining) // Estimate the amount of token one needed until the target sqrt price is reached. - amountOneIn := math.CalcAmount1Delta(liquidityBigDec, sqrtPriceTargetBigDec, sqrtPriceCurrent, true) + amountOneIn := math.CalcAmount1Delta(liquidityBigDec, sqrtPriceTarget, sqrtPriceCurrent, true) // Calculate sqrtPriceNext on the amount of token remaining after spread reward. amountOneInRemainingLessSpreadReward := amountOneInRemainingBigDec.MulTruncate(oneBigDec.Sub(osmomath.BigDecFromSDKDec(s.spreadFactor))) @@ -72,13 +71,13 @@ func (s oneForZeroStrategy) ComputeSwapWithinBucketOutGivenIn(sqrtPriceCurrent o // If have more of the amount remaining after spread reward than estimated until target, // bound the next sqrtPriceNext by the target sqrt price. if amountOneInRemainingLessSpreadReward.GTE(amountOneIn) { - sqrtPriceNext = sqrtPriceTargetBigDec + sqrtPriceNext = sqrtPriceTarget } else { // Otherwise, compute the next sqrt price based on the amount remaining after spread reward. sqrtPriceNext = math.GetNextSqrtPriceFromAmount1InRoundingDown(sqrtPriceCurrent, liquidityBigDec, amountOneInRemainingLessSpreadReward) } - hasReachedTarget := sqrtPriceTargetBigDec.Equal(sqrtPriceNext) + hasReachedTarget := sqrtPriceTarget.Equal(sqrtPriceNext) // If the sqrt price target was not reached, recalculate how much of the amount remaining after spread reward was needed // to complete the swap step. This implies that some of the amount remaining after spread reward is left over after the @@ -121,14 +120,13 @@ func (s oneForZeroStrategy) ComputeSwapWithinBucketOutGivenIn(sqrtPriceCurrent o // // OneForZero details: // - oneForZeroStrategy assumes moving to the right of the current square root price. -func (s oneForZeroStrategy) ComputeSwapWithinBucketInGivenOut(sqrtPriceCurrent osmomath.BigDec, sqrtPriceTarget, liquidity, amountZeroRemainingOut sdk.Dec) (osmomath.BigDec, sdk.Dec, sdk.Dec, sdk.Dec) { +func (s oneForZeroStrategy) ComputeSwapWithinBucketInGivenOut(sqrtPriceCurrent, sqrtPriceTarget osmomath.BigDec, liquidity, amountZeroRemainingOut sdk.Dec) (osmomath.BigDec, sdk.Dec, sdk.Dec, sdk.Dec) { liquidityBigDec := osmomath.BigDecFromSDKDec(liquidity) - sqrtPriceTargetBigDec := osmomath.BigDecFromSDKDec(sqrtPriceTarget) amountZeroRemainingOutBigDec := osmomath.BigDecFromSDKDec(amountZeroRemainingOut) // Estimate the amount of token zero needed until the target sqrt price is reached. // N.B.: contrary to out given in, we do not round up because we do not want to exceed the initial amount out at the end. - amountZeroOut := math.CalcAmount0Delta(liquidityBigDec, sqrtPriceTargetBigDec, sqrtPriceCurrent, false) + amountZeroOut := math.CalcAmount0Delta(liquidityBigDec, sqrtPriceTarget, sqrtPriceCurrent, false) // Calculate sqrtPriceNext on the amount of token remaining. Note that the // spread reward is not charged as amountRemaining is amountOut, and we only charge spread reward on @@ -137,13 +135,13 @@ func (s oneForZeroStrategy) ComputeSwapWithinBucketInGivenOut(sqrtPriceCurrent o // If have more of the amount remaining after spread reward than estimated until target, // bound the next sqrtPriceNext by the target sqrt price. if amountZeroRemainingOutBigDec.GTE(amountZeroOut) { - sqrtPriceNext = sqrtPriceTargetBigDec + sqrtPriceNext = sqrtPriceTarget } else { // Otherwise, compute the next sqrt price based on the amount remaining after spread reward. sqrtPriceNext = math.GetNextSqrtPriceFromAmount0OutRoundingUp(sqrtPriceCurrent, liquidityBigDec, amountZeroRemainingOutBigDec) } - hasReachedTarget := sqrtPriceTargetBigDec.Equal(sqrtPriceNext) + hasReachedTarget := sqrtPriceTarget.Equal(sqrtPriceNext) // If the sqrt price target was not reached, recalculate how much of the amount remaining after spread reward was needed // to complete the swap step. This implies that some of the amount remaining after spread reward is left over after the @@ -254,12 +252,10 @@ func (s oneForZeroStrategy) UpdateTickAfterCrossing(nextTick int64) int64 { // oneForZeroStrategy assumes moving to the right of the current square root price. // Therefore, the following invariant must hold: // current square root price <= sqrtPrice <= types.MaxSqrtRatio -func (s oneForZeroStrategy) ValidateSqrtPrice(sqrtPrice sdk.Dec, currentSqrtPrice osmomath.BigDec) error { - sqrtPriceBigDec := osmomath.BigDecFromSDKDec(sqrtPrice) - +func (s oneForZeroStrategy) ValidateSqrtPrice(sqrtPrice osmomath.BigDec, currentSqrtPrice osmomath.BigDec) error { // check that the price limit is above the current sqrt price but lower than the maximum sqrt price since we are swapping asset1 for asset0 - if sqrtPriceBigDec.LT(currentSqrtPrice) || sqrtPriceBigDec.GT(types.MaxSqrtPriceBigDec) { - return types.SqrtPriceValidationError{SqrtPriceLimit: sqrtPriceBigDec, LowerBound: currentSqrtPrice, UpperBound: types.MaxSqrtPriceBigDec} + if sqrtPrice.LT(currentSqrtPrice) || sqrtPrice.GT(types.MaxSqrtPriceBigDec) { + return types.SqrtPriceValidationError{SqrtPriceLimit: sqrtPrice, LowerBound: currentSqrtPrice, UpperBound: types.MaxSqrtPriceBigDec} } return nil } diff --git a/x/concentrated-liquidity/swapstrategy/one_for_zero_test.go b/x/concentrated-liquidity/swapstrategy/one_for_zero_test.go index 02cf0b829ba..8bcec2ae16f 100644 --- a/x/concentrated-liquidity/swapstrategy/one_for_zero_test.go +++ b/x/concentrated-liquidity/swapstrategy/one_for_zero_test.go @@ -11,7 +11,7 @@ import ( func (suite *StrategyTestSuite) setupNewOneForZeroSwapStrategy(sqrtPriceLimit sdk.Dec, spread sdk.Dec) swapstrategy.SwapStrategy { suite.SetupTest() - return swapstrategy.New(false, sqrtPriceLimit, suite.App.GetKey(types.ModuleName), spread) + return swapstrategy.New(false, osmomath.BigDecFromSDKDec(sqrtPriceLimit), suite.App.GetKey(types.ModuleName), spread) } func (suite *StrategyTestSuite) TestGetSqrtTargetPrice_OneForZero() { @@ -40,8 +40,8 @@ func (suite *StrategyTestSuite) TestGetSqrtTargetPrice_OneForZero() { for name, tc := range tests { suite.Run(name, func() { strategy := suite.setupNewOneForZeroSwapStrategy(tc.sqrtPriceLimit, zero) - actualSqrtTargetPrice := strategy.GetSqrtTargetPrice(tc.nextTickSqrtPrice) - suite.Require().Equal(tc.expectedResult, actualSqrtTargetPrice) + actualSqrtTargetPrice := strategy.GetSqrtTargetPrice(osmomath.BigDecFromSDKDec(tc.nextTickSqrtPrice)) + suite.Require().Equal(osmomath.BigDecFromSDKDec(tc.expectedResult), actualSqrtTargetPrice) }) } } @@ -133,7 +133,7 @@ func (suite *StrategyTestSuite) TestComputeSwapStepOutGivenIn_OneForZero() { "5: custom amounts at high price levels - reach target": { sqrtPriceCurrent: osmomath.BigDecFromSDKDec(sqrt(100_000_000)), sqrtPriceTarget: sqrt(100_000_100), - liquidity: math.GetLiquidityFromAmounts(osmomath.OneDec(), sqrt(100_000_000), sqrt(100_000_100), defaultAmountZero.TruncateInt(), defaultAmountOne.TruncateInt()), + liquidity: math.GetLiquidityFromAmounts(osmomath.OneDec(), osmomath.BigDecFromSDKDec(sqrt(100_000_000)), osmomath.BigDecFromSDKDec(sqrt(100_000_100)), defaultAmountZero.TruncateInt(), defaultAmountOne.TruncateInt()), // this value is exactly enough to reach the target amountOneInRemaining: sdk.NewDec(1336900668450), @@ -208,7 +208,7 @@ func (suite *StrategyTestSuite) TestComputeSwapStepOutGivenIn_OneForZero() { tc := tc suite.Run(name, func() { strategy := suite.setupNewOneForZeroSwapStrategy(types.MaxSqrtPrice, tc.spreadFactor) - sqrtPriceNext, amountInConsumed, amountZeroOut, spreadRewardChargeTotal := strategy.ComputeSwapWithinBucketOutGivenIn(tc.sqrtPriceCurrent, tc.sqrtPriceTarget, tc.liquidity, tc.amountOneInRemaining) + sqrtPriceNext, amountInConsumed, amountZeroOut, spreadRewardChargeTotal := strategy.ComputeSwapWithinBucketOutGivenIn(tc.sqrtPriceCurrent, osmomath.BigDecFromSDKDec(tc.sqrtPriceTarget), tc.liquidity, tc.amountOneInRemaining) suite.Require().Equal(tc.expectedSqrtPriceNext, sqrtPriceNext) suite.Require().Equal(tc.expectedAmountInConsumed, amountInConsumed) @@ -424,7 +424,7 @@ func (suite *StrategyTestSuite) TestComputeSwapStepInGivenOut_OneForZero() { for name, tc := range tests { suite.Run(name, func() { strategy := suite.setupNewOneForZeroSwapStrategy(types.MaxSqrtPrice, tc.spreadFactor) - sqrtPriceNext, amountZeroOutConsumed, amountOneIn, spreadRewardChargeTotal := strategy.ComputeSwapWithinBucketInGivenOut(tc.sqrtPriceCurrent, tc.sqrtPriceTarget, tc.liquidity, tc.amountZeroOutRemaining) + sqrtPriceNext, amountZeroOutConsumed, amountOneIn, spreadRewardChargeTotal := strategy.ComputeSwapWithinBucketInGivenOut(tc.sqrtPriceCurrent, osmomath.BigDecFromSDKDec(tc.sqrtPriceTarget), tc.liquidity, tc.amountZeroOutRemaining) suite.Require().Equal(tc.expectedSqrtPriceNext, sqrtPriceNext) suite.Require().Equal(tc.expectedAmountZeroOutConsumed.String(), amountZeroOutConsumed.String()) diff --git a/x/concentrated-liquidity/swapstrategy/swap_strategy.go b/x/concentrated-liquidity/swapstrategy/swap_strategy.go index 9396c541109..bd4213b2b30 100644 --- a/x/concentrated-liquidity/swapstrategy/swap_strategy.go +++ b/x/concentrated-liquidity/swapstrategy/swap_strategy.go @@ -16,7 +16,7 @@ type SwapStrategy interface { // GetSqrtTargetPrice returns the target square root price given the next tick square root price // upon comparing it to sqrt price limit. // See oneForZeroStrategy or zeroForOneStrategy for implementation details. - GetSqrtTargetPrice(nextTickSqrtPrice sdk.Dec) sdk.Dec + GetSqrtTargetPrice(nextTickSqrtPrice osmomath.BigDec) osmomath.BigDec // ComputeSwapWithinBucketOutGivenIn calculates the next sqrt price, the amount of token in consumed, the amount out to return to the user, and total spread reward charge on token in. // This assumes swapping over a single bucket where the liqudiity stays constant until we cross the next initialized tick of the next bucket. // Parameters: @@ -34,7 +34,7 @@ type SwapStrategy interface { // * amountOutComputed is the amount of token out computed. It is the amount of token out to return to the user. // * spreadRewardChargeTotal is the total spread reward charge. The spread reward is charged on the amount of token in. // See oneForZeroStrategy or zeroForOneStrategy for implementation details. - ComputeSwapWithinBucketOutGivenIn(sqrtPriceCurrent osmomath.BigDec, sqrtPriceTarget, liquidity, amountRemainingIn sdk.Dec) (sqrtPriceNext osmomath.BigDec, amountInConsumed, amountOutComputed, spreadRewardChargeTotal sdk.Dec) + ComputeSwapWithinBucketOutGivenIn(sqrtPriceCurrent, sqrtPriceTarget osmomath.BigDec, liquidity, amountRemainingIn sdk.Dec) (sqrtPriceNext osmomath.BigDec, amountInConsumed, amountOutComputed, spreadRewardChargeTotal sdk.Dec) // ComputeSwapWithinBucketInGivenOut calculates the next sqrt price, the amount of token out consumed, the amount in to charge to the user for requested out, and total spread reward charge on token in. // This assumes swapping over a single bucket where the liqudiity stays constant until we cross the next initialized tick of the next bucket. // Parameters: @@ -52,7 +52,7 @@ type SwapStrategy interface { // * amountInComputed is the amount of token in computed. It is the amount of token in to charge to the user for the desired amount out. // * spreadRewardChargeTotal is the total spread reward charge. The spread reward is charged on the amount of token in. // See oneForZeroStrategy or zeroForOneStrategy for implementation details. - ComputeSwapWithinBucketInGivenOut(sqrtPriceCurrent osmomath.BigDec, sqrtPriceTarget, liquidity, amountRemainingOut sdk.Dec) (sqrtPriceNext osmomath.BigDec, amountOutConsumed, amountInComputed, spreadRewardChargeTotal sdk.Dec) + ComputeSwapWithinBucketInGivenOut(sqrtPriceCurrent, sqrtPriceTarget osmomath.BigDec, liquidity, amountRemainingOut sdk.Dec) (sqrtPriceNext osmomath.BigDec, amountOutConsumed, amountInComputed, spreadRewardChargeTotal sdk.Dec) // InitializeNextTickIterator returns iterator that seeks to the next tick from the given tickIndex. // If nex tick relative to tickINdex does not exist in the store, it will return an invalid iterator. // See oneForZeroStrategy or zeroForOneStrategy for implementation details. @@ -82,7 +82,7 @@ type SwapStrategy interface { // relative to the current square root price on one side of the bound // and the min/max sqrt price on the other side. // See oneForZeroStrategy or zeroForOneStrategy for implementation details. - ValidateSqrtPrice(sqrtPriceLimit sdk.Dec, currentSqrtPrice osmomath.BigDec) error + ValidateSqrtPrice(sqrtPriceLimit osmomath.BigDec, currentSqrtPrice osmomath.BigDec) error ZeroForOne() bool } @@ -94,7 +94,7 @@ var ( // New returns a swap strategy based on the provided zeroForOne parameter // with sqrtPriceLimit for the maximum square root price until which to perform // the swap and the stor key of the module that stores swap data. -func New(zeroForOne bool, sqrtPriceLimit sdk.Dec, storeKey sdk.StoreKey, spreadFactor sdk.Dec) SwapStrategy { +func New(zeroForOne bool, sqrtPriceLimit osmomath.BigDec, storeKey sdk.StoreKey, spreadFactor sdk.Dec) SwapStrategy { if zeroForOne { return &zeroForOneStrategy{sqrtPriceLimit: sqrtPriceLimit, storeKey: storeKey, spreadFactor: spreadFactor} } @@ -111,13 +111,18 @@ func GetPriceLimit(zeroForOne bool) sdk.Dec { return types.MaxSpotPrice } -func GetSqrtPriceLimit(priceLimit sdk.Dec, zeroForOne bool) (sdk.Dec, error) { +func GetSqrtPriceLimit(priceLimit sdk.Dec, zeroForOne bool) (osmomath.BigDec, error) { if priceLimit.IsZero() { if zeroForOne { - return types.MinSqrtPrice, nil + return osmomath.BigDecFromSDKDec(types.MinSqrtPrice), nil } - return types.MaxSqrtPrice, nil + return osmomath.BigDecFromSDKDec(types.MaxSqrtPrice), nil } - return osmomath.MonotonicSqrt(priceLimit) + sqrtPriceLimit, err := osmomath.MonotonicSqrt(priceLimit) + if err != nil { + return osmomath.BigDec{}, err + } + + return osmomath.BigDecFromSDKDec(sqrtPriceLimit), nil } diff --git a/x/concentrated-liquidity/swapstrategy/swap_strategy_test.go b/x/concentrated-liquidity/swapstrategy/swap_strategy_test.go index eab89922058..172c361d1b9 100644 --- a/x/concentrated-liquidity/swapstrategy/swap_strategy_test.go +++ b/x/concentrated-liquidity/swapstrategy/swap_strategy_test.go @@ -219,13 +219,13 @@ func (suite *StrategyTestSuite) TestComputeSwapState_Inverse() { for name, tc := range testCases { tc := tc suite.Run(name, func() { - sut := swapstrategy.New(tc.zeroForOne, sdk.ZeroDec(), suite.App.GetKey(types.ModuleName), sdk.ZeroDec()) - sqrtPriceNextOutGivenIn, amountInOutGivenIn, amountOutOutGivenIn, _ := sut.ComputeSwapWithinBucketOutGivenIn(osmomath.BigDecFromSDKDec(tc.sqrtPriceCurrent), tc.sqrtPriceTarget, tc.liquidity, tc.amountIn) + sut := swapstrategy.New(tc.zeroForOne, osmomath.ZeroDec(), suite.App.GetKey(types.ModuleName), sdk.ZeroDec()) + sqrtPriceNextOutGivenIn, amountInOutGivenIn, amountOutOutGivenIn, _ := sut.ComputeSwapWithinBucketOutGivenIn(osmomath.BigDecFromSDKDec(tc.sqrtPriceCurrent), osmomath.BigDecFromSDKDec(tc.sqrtPriceTarget), tc.liquidity, tc.amountIn) suite.Require().Equal(tc.expectedSqrtPriceNextOutGivenIn.String(), sqrtPriceNextOutGivenIn.String()) fmt.Println("amountOutOutGivenIn", amountOutOutGivenIn) - sqrtPriceNextInGivenOut, amountOutInGivenOut, amountInInGivenOut, _ := sut.ComputeSwapWithinBucketInGivenOut(osmomath.BigDecFromSDKDec(tc.sqrtPriceCurrent), tc.sqrtPriceTarget, tc.liquidity, amountOutOutGivenIn) + sqrtPriceNextInGivenOut, amountOutInGivenOut, amountInInGivenOut, _ := sut.ComputeSwapWithinBucketInGivenOut(osmomath.BigDecFromSDKDec(tc.sqrtPriceCurrent), osmomath.BigDecFromSDKDec(tc.sqrtPriceTarget), tc.liquidity, amountOutOutGivenIn) suite.Require().Equal(tc.expectedSqrtPriceNextInGivenOut.String(), sqrtPriceNextInGivenOut.String()) diff --git a/x/concentrated-liquidity/swapstrategy/zero_for_one.go b/x/concentrated-liquidity/swapstrategy/zero_for_one.go index 01247b1be2d..db8e9a5d185 100644 --- a/x/concentrated-liquidity/swapstrategy/zero_for_one.go +++ b/x/concentrated-liquidity/swapstrategy/zero_for_one.go @@ -19,7 +19,7 @@ import ( // With this strategy, we are moving to the left of the current // tick index and square root price. type zeroForOneStrategy struct { - sqrtPriceLimit sdk.Dec + sqrtPriceLimit osmomath.BigDec storeKey sdk.StoreKey spreadFactor sdk.Dec } @@ -31,7 +31,7 @@ func (s zeroForOneStrategy) ZeroForOne() bool { return true } // GetSqrtTargetPrice returns the target square root price given the next tick square root price. // If the given nextTickSqrtPrice is less than the sqrt price limit, the sqrt price limit is returned. // Otherwise, the input nextTickSqrtPrice is returned. -func (s zeroForOneStrategy) GetSqrtTargetPrice(nextTickSqrtPrice sdk.Dec) sdk.Dec { +func (s zeroForOneStrategy) GetSqrtTargetPrice(nextTickSqrtPrice osmomath.BigDec) osmomath.BigDec { if nextTickSqrtPrice.LT(s.sqrtPriceLimit) { return s.sqrtPriceLimit } @@ -57,13 +57,12 @@ func (s zeroForOneStrategy) GetSqrtTargetPrice(nextTickSqrtPrice sdk.Dec) sdk.De // // ZeroForOne details: // - zeroForOneStrategy assumes moving to the left of the current square root price. -func (s zeroForOneStrategy) ComputeSwapWithinBucketOutGivenIn(sqrtPriceCurrent osmomath.BigDec, sqrtPriceTarget, liquidity, amountZeroInRemaining sdk.Dec) (osmomath.BigDec, sdk.Dec, sdk.Dec, sdk.Dec) { +func (s zeroForOneStrategy) ComputeSwapWithinBucketOutGivenIn(sqrtPriceCurrent, sqrtPriceTarget osmomath.BigDec, liquidity, amountZeroInRemaining sdk.Dec) (osmomath.BigDec, sdk.Dec, sdk.Dec, sdk.Dec) { liquidityBigDec := osmomath.BigDecFromSDKDec(liquidity) - sqrtPriceTargetBigDec := osmomath.BigDecFromSDKDec(sqrtPriceTarget) amountZeroInRemainingBigDec := osmomath.BigDecFromSDKDec(amountZeroInRemaining) // Estimate the amount of token zero needed until the target sqrt price is reached. - amountZeroIn := math.CalcAmount0Delta(liquidityBigDec, sqrtPriceTargetBigDec, sqrtPriceCurrent, true) // N.B.: if this is false, causes infinite loop + amountZeroIn := math.CalcAmount0Delta(liquidityBigDec, sqrtPriceTarget, sqrtPriceCurrent, true) // N.B.: if this is false, causes infinite loop // Calculate sqrtPriceNext on the amount of token remaining after spread reward. amountZeroInRemainingLessSpreadReward := amountZeroInRemainingBigDec.Mul(oneBigDec.Sub(osmomath.BigDecFromSDKDec(s.spreadFactor))) @@ -72,13 +71,13 @@ func (s zeroForOneStrategy) ComputeSwapWithinBucketOutGivenIn(sqrtPriceCurrent o // If have more of the amount remaining after spread reward than estimated until target, // bound the next sqrtPriceNext by the target sqrt price. if amountZeroInRemainingLessSpreadReward.GTE(amountZeroIn) { - sqrtPriceNext = sqrtPriceTargetBigDec + sqrtPriceNext = sqrtPriceTarget } else { // Otherwise, compute the next sqrt price based on the amount remaining after spread reward. sqrtPriceNext = math.GetNextSqrtPriceFromAmount0InRoundingUp(sqrtPriceCurrent, liquidityBigDec, amountZeroInRemainingLessSpreadReward) } - hasReachedTarget := sqrtPriceTargetBigDec.Equal(sqrtPriceNext) + hasReachedTarget := sqrtPriceTarget.Equal(sqrtPriceNext) // If the sqrt price target was not reached, recalculate how much of the amount remaining after spread reward was needed // to complete the swap step. This implies that some of the amount remaining after spread reward is left over after the @@ -121,13 +120,12 @@ func (s zeroForOneStrategy) ComputeSwapWithinBucketOutGivenIn(sqrtPriceCurrent o // // ZeroForOne details: // - zeroForOneStrategy assumes moving to the left of the current square root price. -func (s zeroForOneStrategy) ComputeSwapWithinBucketInGivenOut(sqrtPriceCurrent osmomath.BigDec, sqrtPriceTarget, liquidity, amountOneRemainingOut sdk.Dec) (osmomath.BigDec, sdk.Dec, sdk.Dec, sdk.Dec) { +func (s zeroForOneStrategy) ComputeSwapWithinBucketInGivenOut(sqrtPriceCurrent, sqrtPriceTarget osmomath.BigDec, liquidity, amountOneRemainingOut sdk.Dec) (osmomath.BigDec, sdk.Dec, sdk.Dec, sdk.Dec) { liquidityBigDec := osmomath.BigDecFromSDKDec(liquidity) - sqrtPriceTargetBigDec := osmomath.BigDecFromSDKDec(sqrtPriceTarget) amountOneRemainingOutBigDec := osmomath.BigDecFromSDKDec(amountOneRemainingOut) // Estimate the amount of token one needed until the target sqrt price is reached. - amountOneOut := math.CalcAmount1Delta(liquidityBigDec, sqrtPriceTargetBigDec, sqrtPriceCurrent, false) + amountOneOut := math.CalcAmount1Delta(liquidityBigDec, sqrtPriceTarget, sqrtPriceCurrent, false) // Calculate sqrtPriceNext on the amount of token remaining. Note that the // spread reward is not charged as amountRemaining is amountOut, and we only charge spread reward on @@ -136,13 +134,13 @@ func (s zeroForOneStrategy) ComputeSwapWithinBucketInGivenOut(sqrtPriceCurrent o // If have more of the amount remaining after spread reward than estimated until target, // bound the next sqrtPriceNext by the target sqrt price. if amountOneRemainingOutBigDec.GTE(amountOneOut) { - sqrtPriceNext = sqrtPriceTargetBigDec + sqrtPriceNext = sqrtPriceTarget } else { // Otherwise, compute the next sqrt price based on the amount remaining after spread reward. sqrtPriceNext = math.GetNextSqrtPriceFromAmount1OutRoundingDown(sqrtPriceCurrent, liquidityBigDec, amountOneRemainingOutBigDec) } - hasReachedTarget := sqrtPriceTargetBigDec.Equal(sqrtPriceNext) + hasReachedTarget := sqrtPriceTarget.Equal(sqrtPriceNext) // If the sqrt price target was not reached, recalculate how much of the amount remaining after spread reward was needed // to complete the swap step. This implies that some of the amount remaining after spread reward is left over after the @@ -252,12 +250,10 @@ func (s zeroForOneStrategy) UpdateTickAfterCrossing(nextTick int64) int64 { // zeroForOneStrategy assumes moving to the left of the current square root price. // Therefore, the following invariant must hold: // types.MinSqrtRatio <= sqrtPrice <= current square root price -func (s zeroForOneStrategy) ValidateSqrtPrice(sqrtPrice sdk.Dec, currentSqrtPrice osmomath.BigDec) error { - sqrtPriceBigDec := osmomath.BigDecFromSDKDec(sqrtPrice) - +func (s zeroForOneStrategy) ValidateSqrtPrice(sqrtPrice osmomath.BigDec, currentSqrtPrice osmomath.BigDec) error { // check that the price limit is below the current sqrt price but not lower than the minimum sqrt price if we are swapping asset0 for asset1 - if sqrtPriceBigDec.GT(currentSqrtPrice) || sqrtPrice.LT(types.MinSqrtPrice) { - return types.SqrtPriceValidationError{SqrtPriceLimit: sqrtPriceBigDec, LowerBound: types.MinSqrtPriceBigDec, UpperBound: currentSqrtPrice} + if sqrtPrice.GT(currentSqrtPrice) || sqrtPrice.LT(osmomath.BigDecFromSDKDec(types.MinSqrtPrice)) { + return types.SqrtPriceValidationError{SqrtPriceLimit: sqrtPrice, LowerBound: types.MinSqrtPriceBigDec, UpperBound: currentSqrtPrice} } return nil } diff --git a/x/concentrated-liquidity/swapstrategy/zero_for_one_test.go b/x/concentrated-liquidity/swapstrategy/zero_for_one_test.go index 4b11408839f..9f361a64593 100644 --- a/x/concentrated-liquidity/swapstrategy/zero_for_one_test.go +++ b/x/concentrated-liquidity/swapstrategy/zero_for_one_test.go @@ -10,7 +10,7 @@ import ( func (suite *StrategyTestSuite) setupNewZeroForOneSwapStrategy(sqrtPriceLimit sdk.Dec, spread sdk.Dec) swapstrategy.SwapStrategy { suite.SetupTest() - return swapstrategy.New(true, sqrtPriceLimit, suite.App.GetKey(types.ModuleName), spread) + return swapstrategy.New(true, osmomath.BigDecFromSDKDec(sqrtPriceLimit), suite.App.GetKey(types.ModuleName), spread) } func (suite *StrategyTestSuite) TestGetSqrtTargetPrice_ZeroForOne() { @@ -41,8 +41,8 @@ func (suite *StrategyTestSuite) TestGetSqrtTargetPrice_ZeroForOne() { tc := tc suite.Run(name, func() { sut := suite.setupNewZeroForOneSwapStrategy(tc.sqrtPriceLimit, zero) - actualSqrtTargetPrice := sut.GetSqrtTargetPrice(tc.nextTickSqrtPrice) - suite.Require().Equal(tc.expectedResult, actualSqrtTargetPrice) + actualSqrtTargetPrice := sut.GetSqrtTargetPrice(osmomath.BigDecFromSDKDec(tc.nextTickSqrtPrice)) + suite.Require().Equal(osmomath.BigDecFromSDKDec(tc.expectedResult), actualSqrtTargetPrice) }) } } @@ -202,7 +202,7 @@ func (suite *StrategyTestSuite) TestComputeSwapStepOutGivenIn_ZeroForOne() { for name, tc := range tests { suite.Run(name, func() { strategy := suite.setupNewZeroForOneSwapStrategy(types.MaxSqrtPrice, tc.spreadFactor) - sqrtPriceNext, amountZeroIn, amountOneOut, spreadRewardChargeTotal := strategy.ComputeSwapWithinBucketOutGivenIn(tc.sqrtPriceCurrent, tc.sqrtPriceTarget, tc.liquidity, tc.amountZeroInRemaining) + sqrtPriceNext, amountZeroIn, amountOneOut, spreadRewardChargeTotal := strategy.ComputeSwapWithinBucketOutGivenIn(tc.sqrtPriceCurrent, osmomath.BigDecFromSDKDec(tc.sqrtPriceTarget), tc.liquidity, tc.amountZeroInRemaining) suite.Require().Equal(tc.expectedSqrtPriceNext, sqrtPriceNext) suite.Require().Equal(tc.expectedAmountOneOut, amountOneOut) @@ -333,7 +333,7 @@ func (suite *StrategyTestSuite) TestComputeSwapStepInGivenOut_ZeroForOne() { for name, tc := range tests { suite.Run(name, func() { strategy := suite.setupNewZeroForOneSwapStrategy(types.MaxSqrtPrice, tc.spreadFactor) - sqrtPriceNext, amountOneOut, amountZeroIn, spreadRewardChargeTotal := strategy.ComputeSwapWithinBucketInGivenOut(tc.sqrtPriceCurrent, tc.sqrtPriceTarget, tc.liquidity, tc.amountOneOutRemaining) + sqrtPriceNext, amountOneOut, amountZeroIn, spreadRewardChargeTotal := strategy.ComputeSwapWithinBucketInGivenOut(tc.sqrtPriceCurrent, osmomath.BigDecFromSDKDec(tc.sqrtPriceTarget), tc.liquidity, tc.amountOneOutRemaining) suite.Require().Equal(tc.expectedSqrtPriceNext, sqrtPriceNext) suite.Require().Equal(tc.amountOneOutConsumed, amountOneOut) diff --git a/x/concentrated-liquidity/tick.go b/x/concentrated-liquidity/tick.go index fb52ace4012..5fcb4fdce60 100644 --- a/x/concentrated-liquidity/tick.go +++ b/x/concentrated-liquidity/tick.go @@ -5,6 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/osmosis-labs/osmosis/osmomath" "github.com/osmosis-labs/osmosis/osmoutils" "github.com/osmosis-labs/osmosis/osmoutils/accum" "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/math" @@ -209,7 +210,7 @@ func validateTickRangeIsValid(tickSpacing uint64, lowerTick int64, upperTick int // the first tick (given our precision) that is able to represent this price is -161000000, so we use this tick instead. // // This really only applies to very small tick values, as the increment of a single tick continues to get smaller as the tick value gets smaller. -func roundTickToCanonicalPriceTick(lowerTick, upperTick int64, sqrtPriceTickLower, sqrtPriceTickUpper sdk.Dec, tickSpacing uint64) (int64, int64, error) { +func roundTickToCanonicalPriceTick(lowerTick, upperTick int64, sqrtPriceTickLower, sqrtPriceTickUpper osmomath.BigDec, tickSpacing uint64) (int64, int64, error) { newLowerTick, err := math.SqrtPriceToTickRoundDownSpacing(sqrtPriceTickLower, tickSpacing) if err != nil { return 0, 0, err diff --git a/x/concentrated-liquidity/tick_test.go b/x/concentrated-liquidity/tick_test.go index 5e0e1ba2d71..948f7810a06 100644 --- a/x/concentrated-liquidity/tick_test.go +++ b/x/concentrated-liquidity/tick_test.go @@ -609,7 +609,7 @@ func (s *KeeperTestSuite) TestCrossTick() { if test.poolToGet == validPoolId { s.FundAcc(s.TestAccs[0], sdk.NewCoins(DefaultCoin0, DefaultCoin1)) - _, _, _, _, _, _, err := s.clk.CreatePosition(s.Ctx, test.poolToGet, s.TestAccs[0], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) + _, err := s.clk.CreatePosition(s.Ctx, test.poolToGet, s.TestAccs[0], DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick) s.Require().NoError(err) } diff --git a/x/concentrated-liquidity/types/cl.go b/x/concentrated-liquidity/types/cl.go index 5e345f298e8..825c1002f20 100644 --- a/x/concentrated-liquidity/types/cl.go +++ b/x/concentrated-liquidity/types/cl.go @@ -2,9 +2,31 @@ package types import ( fmt "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // GetConcentratedLockupDenomFromPoolId returns the concentrated lockup denom for a given pool id. func GetConcentratedLockupDenomFromPoolId(poolId uint64) string { return fmt.Sprintf("%s/%d", ConcentratedLiquidityTokenPrefix, poolId) } + +// CreateFullRangePositionData represents the return data from any method +// that creates a full range position. We have multipl variants to +// account for varying locking scenarios. +type CreateFullRangePositionData struct { + ID uint64 + Amount0 sdk.Int + Amount1 sdk.Int + Liquidity sdk.Dec +} + +// UpdatePositionData represents the return data from updating a position. +// Tick flags are used to signal if tick is not referenced by any liquidity after the update +// for removal purposes. +type UpdatePositionData struct { + Amount0 sdk.Int + Amount1 sdk.Int + LowerTickIsEmpty bool + UpperTickIsEmpty bool +} diff --git a/x/concentrated-liquidity/types/errors.go b/x/concentrated-liquidity/types/errors.go index 30cd0ee4c01..7248094c4b0 100644 --- a/x/concentrated-liquidity/types/errors.go +++ b/x/concentrated-liquidity/types/errors.go @@ -828,7 +828,7 @@ func (e RanOutOfTicksForPoolError) Error() string { } type SqrtRootCalculationError struct { - SqrtPriceLimit sdk.Dec + SqrtPriceLimit osmomath.BigDec } func (e SqrtRootCalculationError) Error() string { diff --git a/x/cosmwasmpool/model/pool.go b/x/cosmwasmpool/model/pool.go index 10014ca1a25..79b833a9196 100644 --- a/x/cosmwasmpool/model/pool.go +++ b/x/cosmwasmpool/model/pool.go @@ -71,7 +71,7 @@ func (p Pool) IsActive(ctx sdk.Context) bool { } // SpotPrice returns the spot price of the pool. -func (p Pool) SpotPrice(ctx sdk.Context, baseAssetDenom string, quoteAssetDenom string) (sdk.Dec, error) { +func (p Pool) SpotPrice(ctx sdk.Context, quoteAssetDenom string, baseAssetDenom string) (sdk.Dec, error) { request := msg.SpotPriceQueryMsg{ SpotPrice: msg.SpotPrice{ QuoteAssetDenom: quoteAssetDenom, diff --git a/x/downtime-detector/client/cli/query.go b/x/downtime-detector/client/cli/query.go index f03680e0dbe..f7c6895e65f 100644 --- a/x/downtime-detector/client/cli/query.go +++ b/x/downtime-detector/client/cli/query.go @@ -30,6 +30,7 @@ downtime-duration is a duration, but is restricted to a smaller set. Heres a few }, &queryproto.RecoveredSinceDowntimeOfLengthRequest{} } +//nolint:unparam func parseDowntimeDuration(arg string, _ *pflag.FlagSet) (any, osmocli.FieldReadLocation, error) { dur, err := time.ParseDuration(arg) if err != nil { diff --git a/x/gamm/keeper/migrate.go b/x/gamm/keeper/migrate.go index 0b5edfc304c..3be49c10f44 100644 --- a/x/gamm/keeper/migrate.go +++ b/x/gamm/keeper/migrate.go @@ -10,6 +10,7 @@ import ( "github.com/osmosis-labs/osmosis/v17/x/gamm/types" gammmigration "github.com/osmosis-labs/osmosis/v17/x/gamm/types/migration" poolmanagertypes "github.com/osmosis-labs/osmosis/v17/x/poolmanager/types" + superfluidtypes "github.com/osmosis-labs/osmosis/v17/x/superfluid/types" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" @@ -20,41 +21,44 @@ import ( func (k Keeper) MigrateUnlockedPositionFromBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins, -) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, poolIdLeaving, poolIdEntering uint64, err error) { +) (cltypes.CreateFullRangePositionData, superfluidtypes.MigrationPoolIDs, error) { // Get the balancer poolId by parsing the gamm share denom. - poolIdLeaving, err = types.GetPoolIdFromShareDenom(sharesToMigrate.Denom) + poolIdLeaving, err := types.GetPoolIdFromShareDenom(sharesToMigrate.Denom) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, err + return cltypes.CreateFullRangePositionData{}, superfluidtypes.MigrationPoolIDs{}, err } // Find the governance sanctioned link between the balancer pool and a concentrated pool. - poolIdEntering, err = k.GetLinkedConcentratedPoolID(ctx, poolIdLeaving) + poolIdEntering, err := k.GetLinkedConcentratedPoolID(ctx, poolIdLeaving) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, err + return cltypes.CreateFullRangePositionData{}, superfluidtypes.MigrationPoolIDs{}, err } // Get the concentrated pool from the message and type cast it to ConcentratedPoolExtension. concentratedPool, err := k.concentratedLiquidityKeeper.GetConcentratedPoolById(ctx, poolIdEntering) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, err + return cltypes.CreateFullRangePositionData{}, superfluidtypes.MigrationPoolIDs{}, err } // Exit the balancer pool position. exitCoins, err := k.ExitPool(ctx, sender, poolIdLeaving, sharesToMigrate.Amount, tokenOutMins) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, err + return cltypes.CreateFullRangePositionData{}, superfluidtypes.MigrationPoolIDs{}, err } // Defense in depth, ensuring we are returning exactly two coins. if len(exitCoins) != 2 { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, fmt.Errorf("Balancer pool must have exactly two tokens") + return cltypes.CreateFullRangePositionData{}, superfluidtypes.MigrationPoolIDs{}, fmt.Errorf("Balancer pool must have exactly two tokens") } // Create a full range (min to max tick) concentrated liquidity position. - positionId, amount0, amount1, liquidity, err = k.concentratedLiquidityKeeper.CreateFullRangePosition(ctx, concentratedPool.GetId(), sender, exitCoins) + positionData, err := k.concentratedLiquidityKeeper.CreateFullRangePosition(ctx, concentratedPool.GetId(), sender, exitCoins) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, err + return cltypes.CreateFullRangePositionData{}, superfluidtypes.MigrationPoolIDs{}, err } - return positionId, amount0, amount1, liquidity, poolIdLeaving, poolIdEntering, nil + return positionData, superfluidtypes.MigrationPoolIDs{ + LeavingID: poolIdLeaving, + EnteringID: poolIdEntering, + }, nil } // GetAllMigrationInfo gets all existing links between Balancer Pool and Concentrated Pool, diff --git a/x/gamm/keeper/migrate_test.go b/x/gamm/keeper/migrate_test.go index 5da5871029a..4ab67a98863 100644 --- a/x/gamm/keeper/migrate_test.go +++ b/x/gamm/keeper/migrate_test.go @@ -229,15 +229,15 @@ func (s *KeeperTestSuite) TestMigrate() { // Migrate the user's gamm shares to a full range concentrated liquidity position userBalancesBeforeMigration := s.App.BankKeeper.GetAllBalances(s.Ctx, test.param.sender) - positionId, amount0, amount1, _, poolIdLeaving, poolIdEntering, err := keeper.MigrateUnlockedPositionFromBalancerToConcentrated(s.Ctx, test.param.sender, sharesToMigrate, test.tokenOutMins) + positionData, migratedPools, err := keeper.MigrateUnlockedPositionFromBalancerToConcentrated(s.Ctx, test.param.sender, sharesToMigrate, test.tokenOutMins) userBalancesAfterMigration := s.App.BankKeeper.GetAllBalances(s.Ctx, test.param.sender) if test.expectedErr != nil { s.Require().Error(err) s.Require().ErrorContains(err, test.expectedErr.Error()) // Expect zero values for both pool ids - s.Require().Zero(poolIdLeaving) - s.Require().Zero(poolIdEntering) + s.Require().Zero(migratedPools.LeavingID) + s.Require().Zero(migratedPools.EnteringID) // Assure the user's gamm shares still exist userGammBalanceAfterFailedMigration := s.App.BankKeeper.GetBalance(s.Ctx, test.param.sender, "gamm/pool/1") @@ -251,7 +251,7 @@ func (s *KeeperTestSuite) TestMigrate() { // Assure the position was not created. // TODO: When we implement lock breaking, we need to change time.Time{} to the lock's end time. - _, err := s.App.ConcentratedLiquidityKeeper.GetPositionLiquidity(s.Ctx, positionId) + _, err := s.App.ConcentratedLiquidityKeeper.GetPositionLiquidity(s.Ctx, positionData.ID) s.Require().Error(err) continue } @@ -259,19 +259,19 @@ func (s *KeeperTestSuite) TestMigrate() { // Expect the poolIdLeaving to be the balancer pool id // Expect the poolIdEntering to be the concentrated liquidity pool id - s.Require().Equal(balancerPoolId, poolIdLeaving) - s.Require().Equal(clPool.GetId(), poolIdEntering) + s.Require().Equal(balancerPoolId, migratedPools.LeavingID) + s.Require().Equal(clPool.GetId(), migratedPools.EnteringID) // Determine how much of the user's balance was not used in the migration // This amount should be returned to the user. - expectedUserFinalEthBalanceDiff := expectedCoinsOut.AmountOf(ETH).Sub(amount0) - expectedUserFinalUsdcBalanceDiff := expectedCoinsOut.AmountOf(USDC).Sub(amount1) + expectedUserFinalEthBalanceDiff := expectedCoinsOut.AmountOf(ETH).Sub(positionData.Amount0) + expectedUserFinalUsdcBalanceDiff := expectedCoinsOut.AmountOf(USDC).Sub(positionData.Amount1) s.Require().Equal(userBalancesBeforeMigration.AmountOf(ETH).Add(expectedUserFinalEthBalanceDiff).String(), userBalancesAfterMigration.AmountOf(ETH).String()) s.Require().Equal(userBalancesBeforeMigration.AmountOf(USDC).Add(expectedUserFinalUsdcBalanceDiff).String(), userBalancesAfterMigration.AmountOf(USDC).String()) // Assure the expected position was created. // TODO: When we implement lock breaking, we need to change time.Time{} to the lock's end time. - position, err := s.App.ConcentratedLiquidityKeeper.GetPositionLiquidity(s.Ctx, positionId) + position, err := s.App.ConcentratedLiquidityKeeper.GetPositionLiquidity(s.Ctx, positionData.ID) s.Require().NoError(err) s.Require().Equal(test.expectedLiquidity, position) @@ -294,8 +294,8 @@ func (s *KeeperTestSuite) TestMigrate() { // Assert user amount transferred to cl pool from gamm pool should be equal to the amount we migrated from the migrate message. // This test is within 100 shares due to rounding that occurs from utilizing .000000000000000001 instead of 0. - s.Require().Equal(0, test.errTolerance.Compare(userEthBalanceTransferredToClPool.Amount, amount0)) - s.Require().Equal(0, test.errTolerance.Compare(userUsdcBalanceTransferredToClPool.Amount, amount1)) + s.Require().Equal(0, test.errTolerance.Compare(userEthBalanceTransferredToClPool.Amount, positionData.Amount0)) + s.Require().Equal(0, test.errTolerance.Compare(userUsdcBalanceTransferredToClPool.Amount, positionData.Amount0)) } } diff --git a/x/gamm/simulation/sim_msgs.go b/x/gamm/simulation/sim_msgs.go index 27c2fb4e235..ef2ae316ad4 100644 --- a/x/gamm/simulation/sim_msgs.go +++ b/x/gamm/simulation/sim_msgs.go @@ -87,7 +87,7 @@ func RandomExitPoolMsg(k keeper.Keeper, sim *simtypes.SimCtx, ctx sdk.Context) ( func RandomCreateUniV2Msg(k keeper.Keeper, sim *simtypes.SimCtx, ctx sdk.Context) (*balancertypes.MsgCreateBalancerPool, error) { var poolAssets []balancertypes.PoolAsset // find an address with two or more distinct denoms in their wallet - sender, senderExists := sim.RandomSimAccountWithConstraint(createPoolRestriction(k, sim, ctx)) + sender, senderExists := sim.RandomSimAccountWithConstraint(createPoolRestriction(sim, ctx)) if !senderExists { return nil, errors.New("no sender with two different denoms & pool creation fee exists") } @@ -405,7 +405,7 @@ func deriveRealMinShareOutAmt(ctx sdk.Context, tokenIn sdk.Coins, pool types.CFM return minShareOutAmt, nil } -func createPoolRestriction(k keeper.Keeper, sim *simtypes.SimCtx, ctx sdk.Context) simtypes.SimAccountConstraint { +func createPoolRestriction(sim *simtypes.SimCtx, ctx sdk.Context) simtypes.SimAccountConstraint { return func(acc legacysimulationtype.Account) bool { accCoins := sim.BankKeeper().SpendableCoins(ctx, acc.Address) hasTwoCoins := len(accCoins) >= 2 diff --git a/x/gamm/types/expected_keepers.go b/x/gamm/types/expected_keepers.go index adce072e991..dd21efda346 100644 --- a/x/gamm/types/expected_keepers.go +++ b/x/gamm/types/expected_keepers.go @@ -55,7 +55,7 @@ type CommunityPoolKeeper interface { type ConcentratedLiquidityKeeper interface { SetParams(ctx sdk.Context, params cltypes.Params) GetConcentratedPoolById(ctx sdk.Context, poolId uint64) (cltypes.ConcentratedPoolExtension, error) - CreateFullRangePosition(ctx sdk.Context, clPoolId uint64, owner sdk.AccAddress, coins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, err error) + CreateFullRangePosition(ctx sdk.Context, clPoolId uint64, owner sdk.AccAddress, coins sdk.Coins) (positionData cltypes.CreateFullRangePositionData, err error) } // PoolManager defines the interface needed to be fulfilled for diff --git a/x/ibc-hooks/README.md b/x/ibc-hooks/README.md index ed18cfd1459..45150e93f4c 100644 --- a/x/ibc-hooks/README.md +++ b/x/ibc-hooks/README.md @@ -182,6 +182,124 @@ pub enum SudoMsg { } ``` +### Async Acks + +IBC supports the ability to send an ack back to the sender of the packet asynchronously. This is useful for +cases where the packet is received, but the ack is not immediately known. For example, if the packet is being +forwarded to another chain, the ack may not be known until the packet is received on the other chain. + +Note this ACK does not imply full revertability. It is possible that unrevertable actions have occurred +even if there is an Ack Error. (This is distinct from the behavior of ICS-20 transfers). If you want to ensure +revertability, your contract should be implemented in a way that actions are not finalized until a success ack +is received. + +#### Use case + +Async acks are useful in cases where the contract needs to wait for a response from another chain before +returning a result to the caller. + +For example, if you want to send tokens to another chain after the contract is executed you need to +add a new ibc packet and wait for its ack. + +In the synchronous acks case, the caller will receive an ack from the contract before the second packet +has been processed. This means that the caller will have to wait (and potentially track) if the second +packet has been processed successfully or not. + +With async acks, you contract can take this responsibility and only send an ack to the caller once the +second packet has been processed + +#### Making contract Acks async + +To support this, we allow contracts to return an `IBCAsync` response from the function being executed when the +packet is received. That response specifies that the ack should be handled asynchronously. + +Concretely the contract should return: + +```rust +#[cw_serde] +pub struct OnRecvPacketAsyncResponse { + pub is_async_ack: bool, +} +``` + +if `is_async_ack` is set to true, `OnRecvPacket` will return `nil` and the ack will not be written. Instead, the +contract wil be stored as the "ack actor" for the packet so that only that contract is allowed to send an ack +for it. + +It is up to the contract developers to decide which conditions will trigger the ack to be sent. + +#### Sending an async ack + +To send the async ack, the contract needs to send the MsgEmitIBCAck message to the chain. This message will +then make a sudo call to the contract requesting the ack and write the ack to state. + +That message can be specified in the contract as: + +```rust +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.ibchooks.MsgEmitIBCAck")] +pub struct MsgEmitIBCAck { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(uint64, tag = "2")] + pub packet_sequence: u64, + #[prost(string, tag = "3")] + pub channel: ::prost::alloc::string::String, +} +``` + +The contract is expected to implement the following sudo message handler: + +```rust +#[cw_serde] +pub enum IBCAsyncOptions { + #[serde(rename = "request_ack")] + RequestAck { + /// The source channel (osmosis side) of the IBC packet + source_channel: String, + /// The sequence number that the packet was sent with + packet_sequence: u64, + }, +} + +#[cw_serde] +pub enum SudoMsg { + #[serde(rename = "ibc_async")] + IBCAsync(IBCAsyncOptions), +} +``` + +and that sudo call should return an `IBCAckResponse`: + +```rust +#[cw_serde] +#[serde(tag = "type", content = "content")] +pub enum IBCAck { + AckResponse{ + packet: Packet, + contract_ack: ContractAck, + }, + AckError { + packet: Packet, + error_description: String, + error_response: String, + } +} +``` + +Note: the sudo call is required to potentially allow anyone to send the MsgEmitIBCAck message. For now, however, +this is artificially limited so that the message can only be send by the same contract. This could be expanded in +the future if needed. + # Testing strategy -See go tests. \ No newline at end of file +See go tests.` \ No newline at end of file diff --git a/x/ibc-hooks/go.mod b/x/ibc-hooks/go.mod index 1ce9fca9429..ae3e2dc1a8c 100644 --- a/x/ibc-hooks/go.mod +++ b/x/ibc-hooks/go.mod @@ -5,13 +5,16 @@ go 1.20 require ( cosmossdk.io/errors v1.0.0 github.com/CosmWasm/wasmd v0.31.0 + github.com/cosmos/cosmos-proto v1.0.0-beta.2 github.com/cosmos/cosmos-sdk v0.47.4 github.com/cosmos/ibc-go/v4 v4.4.1 + github.com/gogo/protobuf v1.3.3 github.com/gorilla/mux v1.8.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230510161551-8bf252f26bae github.com/spf13/cobra v1.7.0 github.com/tendermint/tendermint v0.37.0-rc1 + google.golang.org/grpc v1.56.2 ) require ( @@ -31,7 +34,6 @@ require ( github.com/coinbase/rosetta-sdk-go v0.7.9 // indirect github.com/confio/ics23/go v0.9.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect - github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogoproto v1.4.6 // indirect github.com/cosmos/gorocksdb v1.2.0 // indirect @@ -54,7 +56,6 @@ require ( github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/gateway v1.1.0 // indirect - github.com/gogo/protobuf v1.3.3 // indirect github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -134,7 +135,6 @@ require ( google.golang.org/genproto v0.0.0-20230629202037-9506855d4529 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 // indirect - google.golang.org/grpc v1.56.2 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/x/ibc-hooks/go.sum b/x/ibc-hooks/go.sum index 00ee079ad11..909bcae95cc 100644 --- a/x/ibc-hooks/go.sum +++ b/x/ibc-hooks/go.sum @@ -40,6 +40,7 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= cosmossdk.io/errors v1.0.0 h1:nxF07lmlBbB8NKQhtJ+sJm6ef5uV1XkvPXG2bUntb04= +cosmossdk.io/errors v1.0.0/go.mod h1:+hJZLuhdDE0pYN8HkOrVNwrIOYvUGnn6+4fjnJs/oV0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= @@ -203,6 +204,7 @@ github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw github.com/cosmos/iavl v0.19.5 h1:rGA3hOrgNxgRM5wYcSCxgQBap7fW82WZgY78V9po/iY= github.com/cosmos/iavl v0.19.5/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw= github.com/cosmos/ibc-go/v4 v4.4.1 h1:pHPLEpQStGuHZe5J17WvG7w0VGwTmfsoAHrs45+vPfw= +github.com/cosmos/ibc-go/v4 v4.4.1/go.mod h1:UkKEQAPWckLuomhqG8XzeE5nWQPdiEYF8EIDWXBKSXA= github.com/cosmos/interchain-accounts v0.2.6 h1:TV2M2g1/Rb9MCNw1YePdBKE0rcEczNj1RGHT+2iRYas= github.com/cosmos/ledger-cosmos-go v0.12.2 h1:/XYaBlE2BJxtvpkHiBm97gFGSGmYGKunKyF3nNqAXZA= github.com/cosmos/ledger-cosmos-go v0.12.2/go.mod h1:ZcqYgnfNJ6lAXe4HPtWgarNEY+B74i+2/8MhZw4ziiI= @@ -588,6 +590,7 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= @@ -687,6 +690,7 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -812,6 +816,7 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -964,6 +969,7 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -979,6 +985,7 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4= +golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1058,6 +1065,7 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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= @@ -1154,14 +1162,17 @@ golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.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.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= 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= @@ -1172,6 +1183,7 @@ 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/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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= @@ -1238,6 +1250,7 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= @@ -1321,8 +1334,11 @@ google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20230629202037-9506855d4529 h1:9JucMWR7sPvCxUFd6UsOUNmA5kCcWOfORaT3tpAsKQs= +google.golang.org/genproto v0.0.0-20230629202037-9506855d4529/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e h1:AZX1ra8YbFMSb7+1pI8S9v4rrgRR7jU1FmuFSSjTVcQ= +google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 h1:2FZP5XuJY9zQyGM5N0rtovnoXjiMUEIUMvw0m9wlpLc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1362,6 +1378,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/x/ibc-hooks/keeper/keeper.go b/x/ibc-hooks/keeper/keeper.go index 9d14337bc4d..b4bc23cf269 100644 --- a/x/ibc-hooks/keeper/keeper.go +++ b/x/ibc-hooks/keeper/keeper.go @@ -1,11 +1,18 @@ package keeper import ( + "encoding/hex" + "encoding/json" "fmt" - + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" "github.com/cosmos/cosmos-sdk/types/address" - + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + "github.com/osmosis-labs/osmosis/osmoutils" + "github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/libs/log" + "strings" "github.com/osmosis-labs/osmosis/x/ibc-hooks/types" @@ -14,16 +21,29 @@ import ( type ( Keeper struct { - storeKey sdk.StoreKey + storeKey sdk.StoreKey + paramSpace paramtypes.Subspace + + channelKeeper types.ChannelKeeper + ContractKeeper *wasmkeeper.PermissionedKeeper } ) // NewKeeper returns a new instance of the x/ibchooks keeper func NewKeeper( storeKey sdk.StoreKey, -) Keeper { - return Keeper{ - storeKey: storeKey, + paramSpace paramtypes.Subspace, + channelKeeper types.ChannelKeeper, + contractKeeper *wasmkeeper.PermissionedKeeper, +) *Keeper { + if !paramSpace.HasKeyTable() { + paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) + } + return &Keeper{ + storeKey: storeKey, + paramSpace: paramSpace, + channelKeeper: channelKeeper, + ContractKeeper: contractKeeper, } } @@ -32,31 +52,221 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } -func GetPacketKey(channel string, packetSequence uint64) []byte { +// GetParams returns the total set of the module's parameters. +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { + k.paramSpace.GetParamSet(ctx, ¶ms) + return params +} + +// SetParams sets the module's parameters with the provided parameters. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + k.paramSpace.SetParamSet(ctx, ¶ms) +} + +func (k Keeper) InitGenesis(ctx sdk.Context, genState types.GenesisState) { + k.SetParams(ctx, genState.Params) +} + +func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { + return &types.GenesisState{ + Params: k.GetParams(ctx), + } +} + +func GetPacketCallbackKey(channel string, packetSequence uint64) []byte { return []byte(fmt.Sprintf("%s::%d", channel, packetSequence)) } +func GetPacketAckKey(channel string, packetSequence uint64) []byte { + return []byte(fmt.Sprintf("%s::%d::ack", channel, packetSequence)) +} + +func GeneratePacketAckValue(packet channeltypes.Packet, contract string) ([]byte, error) { + if _, err := sdk.AccAddressFromBech32(contract); err != nil { + return nil, sdkerrors.Wrap(types.ErrInvalidContractAddr, contract) + } + + packetHash, err := hashPacket(packet) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not hash packet") + } + + return []byte(fmt.Sprintf("%s::%s", contract, packetHash)), nil +} + // StorePacketCallback stores which contract will be listening for the ack or timeout of a packet func (k Keeper) StorePacketCallback(ctx sdk.Context, channel string, packetSequence uint64, contract string) { store := ctx.KVStore(k.storeKey) - store.Set(GetPacketKey(channel, packetSequence), []byte(contract)) + store.Set(GetPacketCallbackKey(channel, packetSequence), []byte(contract)) } // GetPacketCallback returns the bech32 addr of the contract that is expecting a callback from a packet func (k Keeper) GetPacketCallback(ctx sdk.Context, channel string, packetSequence uint64) string { store := ctx.KVStore(k.storeKey) - return string(store.Get(GetPacketKey(channel, packetSequence))) + return string(store.Get(GetPacketCallbackKey(channel, packetSequence))) +} + +// IsInAllowList checks the params to see if the contract is in the KeyAsyncAckAllowList param +func (k Keeper) IsInAllowList(ctx sdk.Context, contract string) bool { + var allowList []string + k.paramSpace.GetIfExists(ctx, types.KeyAsyncAckAllowList, &allowList) + for _, addr := range allowList { + if addr == contract { + return true + } + } + return false } // DeletePacketCallback deletes the callback from storage once it has been processed func (k Keeper) DeletePacketCallback(ctx sdk.Context, channel string, packetSequence uint64) { store := ctx.KVStore(k.storeKey) - store.Delete(GetPacketKey(channel, packetSequence)) + store.Delete(GetPacketCallbackKey(channel, packetSequence)) } +// StorePacketAckActor stores which contract is allowed to send an ack for the packet +func (k Keeper) StorePacketAckActor(ctx sdk.Context, packet channeltypes.Packet, contract string) { + store := ctx.KVStore(k.storeKey) + channel := packet.GetSourceChannel() + packetSequence := packet.GetSequence() + + val, err := GeneratePacketAckValue(packet, contract) + if err != nil { + panic(err) + } + store.Set(GetPacketAckKey(channel, packetSequence), val) +} + +// GetPacketAckActor returns the bech32 addr of the contract that is allowed to send an ack for the packet and the packet hash +func (k Keeper) GetPacketAckActor(ctx sdk.Context, channel string, packetSequence uint64) (string, string) { + store := ctx.KVStore(k.storeKey) + rawData := store.Get(GetPacketAckKey(channel, packetSequence)) + if rawData == nil { + return "", "" + } + data := strings.Split(string(rawData), "::") + if len(data) != 2 { + return "", "" + } + // validate that the contract is a valid bech32 addr + if _, err := sdk.AccAddressFromBech32(data[0]); err != nil { + return "", "" + } + // validate that the hash is a valid sha256sum hash + if _, err := hex.DecodeString(data[1]); err != nil { + return "", "" + } + + return data[0], data[1] +} + +// DeletePacketAckActor deletes the ack actor from storage once it has been used +func (k Keeper) DeletePacketAckActor(ctx sdk.Context, channel string, packetSequence uint64) { + store := ctx.KVStore(k.storeKey) + store.Delete(GetPacketAckKey(channel, packetSequence)) +} + +// DeriveIntermediateSender derives the sender address to be used when calling wasm hooks func DeriveIntermediateSender(channel, originalSender, bech32Prefix string) (string, error) { senderStr := fmt.Sprintf("%s/%s", channel, originalSender) senderHash32 := address.Hash(types.SenderPrefix, []byte(senderStr)) sender := sdk.AccAddress(senderHash32[:]) return sdk.Bech32ifyAddressBytes(bech32Prefix, sender) } + +// EmitIBCAck emits an event that the IBC packet has been acknowledged +func (k Keeper) EmitIBCAck(ctx sdk.Context, sender, channel string, packetSequence uint64) ([]byte, error) { + contract, packetHash := k.GetPacketAckActor(ctx, channel, packetSequence) + if contract == "" { + return nil, fmt.Errorf("no ack actor set for channel %s packet %d", channel, packetSequence) + } + // Only the contract itself can request for the ack to be emitted. This will generally happen as a callback + // when the result of other IBC actions has finished, but it could be exposed directly by the contract if the + // proper checks are made + if sender != contract { + return nil, fmt.Errorf("sender %s is not allowed to send an ack for channel %s packet %d", sender, channel, packetSequence) + } + + // Write the acknowledgement + _, cap, err := k.channelKeeper.LookupModuleByChannel(ctx, "transfer", channel) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + } + + // Calling the contract. This could be made generic by using an interface if we want + // to support other types of AckActors, but keeping it here for now for simplicity. + contractAddr, err := sdk.AccAddressFromBech32(contract) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not parse contract address") + } + + msg := types.IBCAsync{ + RequestAck: types.RequestAck{RequestAckI: types.RequestAckI{ + PacketSequence: packetSequence, + SourceChannel: channel, + }}, + } + msgBytes, err := json.Marshal(msg) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not marshal message") + } + bz, err := k.ContractKeeper.Sudo(ctx, contractAddr, msgBytes) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not execute contract") + } + + ack, err := types.UnmarshalIBCAck(bz) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not unmarshal into IBCAckResponse or IBCAckError") + + } + var newAck channeltypes.Acknowledgement + var packet channeltypes.Packet + + switch ack.Type { + case "ack_response": + jsonAck, err := json.Marshal(ack.AckResponse.ContractAck) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not marshal acknowledgement") + } + packet = ack.AckResponse.Packet + newAck = channeltypes.NewResultAcknowledgement(jsonAck) + case "ack_error": + packet = ack.AckError.Packet + newAck = osmoutils.NewSuccessAckRepresentingAnError(ctx, types.ErrAckFromContract, []byte(ack.AckError.ErrorResponse), ack.AckError.ErrorDescription) + default: + return nil, sdkerrors.Wrap(err, "could not unmarshal into IBCAckResponse or IBCAckError") + } + + // Validate that the packet returned by the contract matches the one we stored when sending + receivedPacketHash, err := hashPacket(packet) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not hash packet") + } + if receivedPacketHash != packetHash { + return nil, sdkerrors.Wrap(types.ErrAckPacketMismatch, fmt.Sprintf("packet hash mismatch. Expected %s, got %s", packetHash, receivedPacketHash)) + } + + // Now we can write the acknowledgement + err = k.channelKeeper.WriteAcknowledgement(ctx, cap, packet, newAck) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not write acknowledgement") + } + + response, err := json.Marshal(newAck) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not marshal acknowledgement") + } + return response, nil +} + +func hashPacket(packet channeltypes.Packet) (string, error) { + // ignore the data here. We only care about the channel information + packet.Data = nil + bz, err := json.Marshal(packet) + if err != nil { + return "", sdkerrors.Wrap(err, "could not marshal packet") + } + packetHash := tmhash.Sum(bz) + return hex.EncodeToString(packetHash), nil +} diff --git a/x/ibc-hooks/keeper/msg_server.go b/x/ibc-hooks/keeper/msg_server.go new file mode 100644 index 00000000000..fbb7ed5b133 --- /dev/null +++ b/x/ibc-hooks/keeper/msg_server.go @@ -0,0 +1,40 @@ +package keeper + +import ( + "context" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/osmosis-labs/osmosis/x/ibc-hooks/types" + "strconv" +) + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +var _ types.MsgServer = msgServer{} + +func (m msgServer) EmitIBCAck(goCtx context.Context, msg *types.MsgEmitIBCAck) (*types.MsgEmitIBCAckResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.MsgEmitAckKey, + sdk.NewAttribute(types.AttributeSender, msg.Sender), + sdk.NewAttribute(types.AttributeChannel, msg.Channel), + sdk.NewAttribute(types.AttributePacketSequence, strconv.FormatUint(msg.PacketSequence, 10)), + ), + ) + + ack, err := m.Keeper.EmitIBCAck(ctx, msg.Sender, msg.Channel, msg.PacketSequence) + if err != nil { + return nil, err + } + + return &types.MsgEmitIBCAckResponse{ContractResult: string(ack), IbcAck: string(ack)}, nil +} diff --git a/x/ibc-hooks/sdkmodule.go b/x/ibc-hooks/sdkmodule.go index 160e5945808..1f4cccda6aa 100644 --- a/x/ibc-hooks/sdkmodule.go +++ b/x/ibc-hooks/sdkmodule.go @@ -3,6 +3,7 @@ package ibc_hooks import ( "encoding/json" "fmt" + "github.com/osmosis-labs/osmosis/x/ibc-hooks/keeper" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" @@ -38,21 +39,28 @@ func (AppModuleBasic) Name() string { } // RegisterLegacyAminoCodec registers the ibc-hooks module's types on the given LegacyAmino codec. -func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {} +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterCodec(cdc) +} -// RegisterInterfaces registers the module's interface types. -func (b AppModuleBasic) RegisterInterfaces(_ cdctypes.InterfaceRegistry) {} +// RegisterInterfaces registers the module's interface types +func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) { + types.RegisterInterfaces(reg) +} // DefaultGenesis returns default genesis state as raw bytes for the // module. func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { - emptyString := "{}" - return []byte(emptyString) + return cdc.MustMarshalJSON(types.DefaultGenesis()) } // ValidateGenesis performs genesis state validation for the ibc-hooks module. func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { - return nil + var genState types.GenesisState + if err := cdc.UnmarshalJSON(bz, &genState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + return genState.Validate() } // RegisterRESTRoutes registers the REST routes for the ibc-hooks module. @@ -76,13 +84,15 @@ type AppModule struct { AppModuleBasic authKeeper osmoutils.AccountKeeper + keeper keeper.Keeper } // NewAppModule creates a new AppModule object. -func NewAppModule(ak osmoutils.AccountKeeper) AppModule { +func NewAppModule(ak osmoutils.AccountKeeper, keeper keeper.Keeper) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, authKeeper: ak, + keeper: keeper, } } @@ -112,16 +122,24 @@ func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sd // RegisterServices registers a gRPC query service to respond to the // module-specific gRPC queries. func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) } // InitGenesis performs genesis initialization for the ibc-hooks module. It returns // no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { + var genState types.GenesisState + // Initialize global index to index in genesis state + cdc.MustUnmarshalJSON(data, &genState) + + am.keeper.InitGenesis(ctx, genState) + return []abci.ValidatorUpdate{} } func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { - return json.RawMessage([]byte("{}")) + genState := am.keeper.ExportGenesis(ctx) + return cdc.MustMarshalJSON(genState) } // BeginBlock returns the begin blocker for the ibc-hooks module. diff --git a/x/ibc-hooks/types/codec.go b/x/ibc-hooks/types/codec.go new file mode 100644 index 00000000000..e4b1dcd78bc --- /dev/null +++ b/x/ibc-hooks/types/codec.go @@ -0,0 +1,38 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" + + // this line is used by starport scaffolding # 1 + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +func RegisterCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgEmitIBCAck{}, "osmosis/ibc-hooks/emit-ibc-ack", nil) +} + +func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgEmitIBCAck{}, + ) + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry()) +) + +func init() { + RegisterCodec(amino) + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + sdk.RegisterLegacyAminoCodec(amino) + RegisterCodec(authzcodec.Amino) + + amino.Seal() +} diff --git a/x/ibc-hooks/types/errors.go b/x/ibc-hooks/types/errors.go index 717e33bddd4..31722619aaf 100644 --- a/x/ibc-hooks/types/errors.go +++ b/x/ibc-hooks/types/errors.go @@ -8,10 +8,14 @@ var ( ErrBadMetadataFormatMsg = "wasm metadata not properly formatted for: '%v'. %s" ErrBadExecutionMsg = "cannot execute contract: %v" - ErrMsgValidation = errorsmod.Register("wasm-hooks", 2, "error in wasmhook message validation") - ErrMarshaling = errorsmod.Register("wasm-hooks", 3, "cannot marshal the ICS20 packet") - ErrInvalidPacket = errorsmod.Register("wasm-hooks", 4, "invalid packet data") - ErrBadResponse = errorsmod.Register("wasm-hooks", 5, "cannot create response") - ErrWasmError = errorsmod.Register("wasm-hooks", 6, "wasm error") - ErrBadSender = errorsmod.Register("wasm-hooks", 7, "bad sender") + ErrMsgValidation = errorsmod.Register("wasm-hooks", 2, "error in wasmhook message validation") + ErrMarshaling = errorsmod.Register("wasm-hooks", 3, "cannot marshal the ICS20 packet") + ErrInvalidPacket = errorsmod.Register("wasm-hooks", 4, "invalid packet data") + ErrBadResponse = errorsmod.Register("wasm-hooks", 5, "cannot create response") + ErrWasmError = errorsmod.Register("wasm-hooks", 6, "wasm error") + ErrBadSender = errorsmod.Register("wasm-hooks", 7, "bad sender") + ErrAckFromContract = errorsmod.Register("wasm-hooks", 8, "contract returned error ack") + ErrAsyncAckNotAllowed = errorsmod.Register("wasm-hooks", 9, "contract not allowed to send async acks") + ErrAckPacketMismatch = errorsmod.Register("wasm-hooks", 10, "packet does not match the expected packet") + ErrInvalidContractAddr = errorsmod.Register("wasm-hooks", 11, "invalid contract address") ) diff --git a/x/ibc-hooks/types/expected_keepers.go b/x/ibc-hooks/types/expected_keepers.go new file mode 100644 index 00000000000..0110dcd9c8e --- /dev/null +++ b/x/ibc-hooks/types/expected_keepers.go @@ -0,0 +1,16 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v4/modules/core/exported" +) + +type ChannelKeeper interface { + GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) + GetPacketCommitment(ctx sdk.Context, portID, channelID string, sequence uint64) []byte + GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) + LookupModuleByChannel(ctx sdk.Context, portID, channelID string) (string, *capabilitytypes.Capability, error) + WriteAcknowledgement(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet exported.PacketI, acknowledgement exported.Acknowledgement) error +} diff --git a/x/ibc-hooks/types/genesis.go b/x/ibc-hooks/types/genesis.go new file mode 100644 index 00000000000..dd111be6489 --- /dev/null +++ b/x/ibc-hooks/types/genesis.go @@ -0,0 +1,16 @@ +package types + +// DefaultGenesis returns the default GenesisState for the concentrated-liquidity module. +func DefaultGenesis() *GenesisState { + return &GenesisState{ + Params: DefaultParams(), + } +} + +// Validate performs basic genesis state validation returning an error upon any failure. +func (gs GenesisState) Validate() error { + if err := gs.Params.Validate(); err != nil { + return err + } + return nil +} diff --git a/x/ibc-hooks/types/genesis.pb.go b/x/ibc-hooks/types/genesis.pb.go new file mode 100644 index 00000000000..51b10276c82 --- /dev/null +++ b/x/ibc-hooks/types/genesis.pb.go @@ -0,0 +1,322 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/ibc-hooks/genesis.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type GenesisState struct { + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_a335b3a5deaed556, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "osmosis.ibchooks.GenesisState") +} + +func init() { proto.RegisterFile("osmosis/ibc-hooks/genesis.proto", fileDescriptor_a335b3a5deaed556) } + +var fileDescriptor_a335b3a5deaed556 = []byte{ + // 212 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcf, 0x2f, 0xce, 0xcd, + 0x2f, 0xce, 0x2c, 0xd6, 0xcf, 0x4c, 0x4a, 0xd6, 0xcd, 0xc8, 0xcf, 0xcf, 0x2e, 0xd6, 0x4f, 0x4f, + 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x80, 0x2a, 0xd0, + 0xcb, 0x4c, 0x4a, 0x06, 0xcb, 0x4b, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x25, 0xf5, 0x41, 0x2c, + 0x88, 0x3a, 0x29, 0xc9, 0x64, 0xb0, 0xc2, 0x78, 0x88, 0x04, 0x84, 0x03, 0x95, 0x92, 0xc3, 0xb4, + 0xa3, 0x20, 0xb1, 0x28, 0x31, 0x17, 0x2a, 0xaf, 0xe4, 0xc6, 0xc5, 0xe3, 0x0e, 0xb1, 0x33, 0xb8, + 0x24, 0xb1, 0x24, 0x55, 0xc8, 0x8c, 0x8b, 0x0d, 0x22, 0x2f, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x6d, + 0x24, 0xa1, 0x87, 0xee, 0x06, 0xbd, 0x00, 0xb0, 0xbc, 0x13, 0xcb, 0x89, 0x7b, 0xf2, 0x0c, 0x41, + 0x50, 0xd5, 0x4e, 0xfe, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, + 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0x65, 0x9a, + 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x0f, 0x35, 0x4b, 0x37, 0x27, 0x31, + 0xa9, 0x18, 0xc6, 0xd1, 0x2f, 0x33, 0x34, 0xd7, 0xaf, 0x40, 0x72, 0x5f, 0x49, 0x65, 0x41, 0x6a, + 0x71, 0x12, 0x1b, 0xd8, 0x7d, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x94, 0x67, 0x2a, 0x86, + 0x25, 0x01, 0x00, 0x00, +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ibc-hooks/types/keys.go b/x/ibc-hooks/types/keys.go index 9a3e7ded1bc..db7527bd1fb 100644 --- a/x/ibc-hooks/types/keys.go +++ b/x/ibc-hooks/types/keys.go @@ -1,8 +1,17 @@ package types const ( - ModuleName = "ibchooks" - StoreKey = "hooks-for-ibc" // not using the module name because of collisions with key "ibc" + ModuleName = "ibchooks" + RouterKey = ModuleName + StoreKey = "hooks-for-ibc" // not using the module name because of collisions with key "ibc" + IBCCallbackKey = "ibc_callback" - SenderPrefix = "ibc-wasm-hook-intermediary" + IBCAsyncAckKey = "ibc_async_ack" + + MsgEmitAckKey = "emit_ack" + AttributeSender = "sender" + AttributeChannel = "channel" + AttributePacketSequence = "sequence" + + SenderPrefix = "ibc-wasm-hook-intermediary" ) diff --git a/x/ibc-hooks/types/msgs.go b/x/ibc-hooks/types/msgs.go new file mode 100644 index 00000000000..85ccb3b8cb3 --- /dev/null +++ b/x/ibc-hooks/types/msgs.go @@ -0,0 +1,32 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// constants. +const ( + TypeMsgEmitIBCAck = "emit-ibc-ack" +) + +var _ sdk.Msg = &MsgEmitIBCAck{} + +func (m MsgEmitIBCAck) Route() string { return RouterKey } +func (m MsgEmitIBCAck) Type() string { return TypeMsgEmitIBCAck } +func (m MsgEmitIBCAck) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(m.Sender) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + return nil +} + +func (m MsgEmitIBCAck) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m)) +} + +func (m MsgEmitIBCAck) GetSigners() []sdk.AccAddress { + sender, _ := sdk.AccAddressFromBech32(m.Sender) + return []sdk.AccAddress{sender} +} diff --git a/x/ibc-hooks/types/params.go b/x/ibc-hooks/types/params.go new file mode 100644 index 00000000000..091fbb14683 --- /dev/null +++ b/x/ibc-hooks/types/params.go @@ -0,0 +1,62 @@ +package types + +import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +// Parameter store keys. +var ( + KeyAsyncAckAllowList = []byte("AsyncAckAllowList") + + _ paramtypes.ParamSet = &Params{} +) + +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) +} + +func NewParams(allowedAsyncAckContracts []string) Params { + return Params{ + AllowedAsyncAckContracts: allowedAsyncAckContracts, + } +} + +// DefaultParams returns default concentrated-liquidity module parameters. +func DefaultParams() Params { + return Params{ + AllowedAsyncAckContracts: []string{}, + } +} + +// ParamSetPairs implements params.ParamSet. +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(KeyAsyncAckAllowList, &p.AllowedAsyncAckContracts, validateAsyncAckAllowList), + } +} + +// Validate params. +func (p Params) Validate() error { + if err := validateAsyncAckAllowList(p.AllowedAsyncAckContracts); err != nil { + return err + } + return nil +} + +func validateAsyncAckAllowList(i interface{}) error { + allowedContracts, ok := i.([]string) + + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + for _, contract := range allowedContracts { + if _, err := sdk.AccAddressFromBech32(contract); err != nil { + return err + } + } + + return nil +} diff --git a/x/ibc-hooks/types/params.pb.go b/x/ibc-hooks/types/params.pb.go new file mode 100644 index 00000000000..6ab409300d2 --- /dev/null +++ b/x/ibc-hooks/types/params.pb.go @@ -0,0 +1,327 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/ibc-hooks/params.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + _ "github.com/gogo/protobuf/types" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Params struct { + AllowedAsyncAckContracts []string `protobuf:"bytes,1,rep,name=allowed_async_ack_contracts,json=allowedAsyncAckContracts,proto3" json:"allowed_async_ack_contracts,omitempty" yaml:"allowed_async_ack_contracts"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_a8a3c4779e5e4552, []int{0} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetAllowedAsyncAckContracts() []string { + if m != nil { + return m.AllowedAsyncAckContracts + } + return nil +} + +func init() { + proto.RegisterType((*Params)(nil), "osmosis.ibchooks.Params") +} + +func init() { proto.RegisterFile("osmosis/ibc-hooks/params.proto", fileDescriptor_a8a3c4779e5e4552) } + +var fileDescriptor_a8a3c4779e5e4552 = []byte{ + // 254 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcb, 0x2f, 0xce, 0xcd, + 0x2f, 0xce, 0x2c, 0xd6, 0xcf, 0x4c, 0x4a, 0xd6, 0xcd, 0xc8, 0xcf, 0xcf, 0x2e, 0xd6, 0x2f, 0x48, + 0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x80, 0xca, 0xeb, 0x65, + 0x26, 0x25, 0x83, 0xa5, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x92, 0xfa, 0x20, 0x16, 0x44, + 0x9d, 0x94, 0x64, 0x32, 0x58, 0x61, 0x3c, 0x44, 0x02, 0xc2, 0x81, 0x4a, 0xc9, 0xa5, 0xe7, 0xe7, + 0xa7, 0xe7, 0xa4, 0xea, 0x83, 0x79, 0x49, 0xa5, 0x69, 0xfa, 0x29, 0xa5, 0x45, 0x89, 0x25, 0x99, + 0xf9, 0x79, 0x10, 0x79, 0xa5, 0x7c, 0x2e, 0xb6, 0x00, 0xb0, 0x95, 0x42, 0xa9, 0x5c, 0xd2, 0x89, + 0x39, 0x39, 0xf9, 0xe5, 0xa9, 0x29, 0xf1, 0x89, 0xc5, 0x95, 0x79, 0xc9, 0xf1, 0x89, 0xc9, 0xd9, + 0xf1, 0xc9, 0xf9, 0x79, 0x25, 0x45, 0x89, 0xc9, 0x25, 0xc5, 0x12, 0x8c, 0x0a, 0xcc, 0x1a, 0x9c, + 0x4e, 0x6a, 0x9f, 0xee, 0xc9, 0x2b, 0x55, 0x26, 0xe6, 0xe6, 0x58, 0x29, 0xe1, 0x51, 0xac, 0x14, + 0x24, 0x01, 0x95, 0x75, 0x04, 0x49, 0x3a, 0x26, 0x67, 0x3b, 0xc3, 0xa4, 0x9c, 0xfc, 0x4f, 0x3c, + 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, + 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0xca, 0x34, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, + 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0xea, 0x71, 0xdd, 0x9c, 0xc4, 0xa4, 0x62, 0x18, 0x47, 0xbf, 0xcc, + 0xd0, 0x5c, 0xbf, 0x02, 0x29, 0xac, 0x4a, 0x2a, 0x0b, 0x52, 0x8b, 0x93, 0xd8, 0xc0, 0x1e, 0x31, + 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x54, 0xb5, 0xc2, 0xdb, 0x4d, 0x01, 0x00, 0x00, +} + +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AllowedAsyncAckContracts) > 0 { + for iNdEx := len(m.AllowedAsyncAckContracts) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.AllowedAsyncAckContracts[iNdEx]) + copy(dAtA[i:], m.AllowedAsyncAckContracts[iNdEx]) + i = encodeVarintParams(dAtA, i, uint64(len(m.AllowedAsyncAckContracts[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintParams(dAtA []byte, offset int, v uint64) int { + offset -= sovParams(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.AllowedAsyncAckContracts) > 0 { + for _, s := range m.AllowedAsyncAckContracts { + l = len(s) + n += 1 + l + sovParams(uint64(l)) + } + } + return n +} + +func sovParams(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozParams(x uint64) (n int) { + return sovParams(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowedAsyncAckContracts", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AllowedAsyncAckContracts = append(m.AllowedAsyncAckContracts, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipParams(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthParams + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipParams(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowParams + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowParams + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowParams + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthParams + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupParams + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthParams + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthParams = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowParams = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupParams = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ibc-hooks/types/tx.pb.go b/x/ibc-hooks/types/tx.pb.go new file mode 100644 index 00000000000..ba689b967dc --- /dev/null +++ b/x/ibc-hooks/types/tx.pb.go @@ -0,0 +1,724 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/ibc-hooks/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type MsgEmitIBCAck struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + PacketSequence uint64 `protobuf:"varint,2,opt,name=packet_sequence,json=packetSequence,proto3" json:"packet_sequence,omitempty" yaml:"packet_sequence"` + Channel string `protobuf:"bytes,3,opt,name=channel,proto3" json:"channel,omitempty" yaml:"channel"` +} + +func (m *MsgEmitIBCAck) Reset() { *m = MsgEmitIBCAck{} } +func (m *MsgEmitIBCAck) String() string { return proto.CompactTextString(m) } +func (*MsgEmitIBCAck) ProtoMessage() {} +func (*MsgEmitIBCAck) Descriptor() ([]byte, []int) { + return fileDescriptor_93268c51ed820a58, []int{0} +} +func (m *MsgEmitIBCAck) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgEmitIBCAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgEmitIBCAck.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgEmitIBCAck) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgEmitIBCAck.Merge(m, src) +} +func (m *MsgEmitIBCAck) XXX_Size() int { + return m.Size() +} +func (m *MsgEmitIBCAck) XXX_DiscardUnknown() { + xxx_messageInfo_MsgEmitIBCAck.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgEmitIBCAck proto.InternalMessageInfo + +func (m *MsgEmitIBCAck) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgEmitIBCAck) GetPacketSequence() uint64 { + if m != nil { + return m.PacketSequence + } + return 0 +} + +func (m *MsgEmitIBCAck) GetChannel() string { + if m != nil { + return m.Channel + } + return "" +} + +type MsgEmitIBCAckResponse struct { + ContractResult string `protobuf:"bytes,1,opt,name=contract_result,json=contractResult,proto3" json:"contract_result,omitempty" yaml:"contract_result"` + IbcAck string `protobuf:"bytes,2,opt,name=ibc_ack,json=ibcAck,proto3" json:"ibc_ack,omitempty" yaml:"ibc_ack"` +} + +func (m *MsgEmitIBCAckResponse) Reset() { *m = MsgEmitIBCAckResponse{} } +func (m *MsgEmitIBCAckResponse) String() string { return proto.CompactTextString(m) } +func (*MsgEmitIBCAckResponse) ProtoMessage() {} +func (*MsgEmitIBCAckResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_93268c51ed820a58, []int{1} +} +func (m *MsgEmitIBCAckResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgEmitIBCAckResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgEmitIBCAckResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgEmitIBCAckResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgEmitIBCAckResponse.Merge(m, src) +} +func (m *MsgEmitIBCAckResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgEmitIBCAckResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgEmitIBCAckResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgEmitIBCAckResponse proto.InternalMessageInfo + +func (m *MsgEmitIBCAckResponse) GetContractResult() string { + if m != nil { + return m.ContractResult + } + return "" +} + +func (m *MsgEmitIBCAckResponse) GetIbcAck() string { + if m != nil { + return m.IbcAck + } + return "" +} + +func init() { + proto.RegisterType((*MsgEmitIBCAck)(nil), "osmosis.ibchooks.MsgEmitIBCAck") + proto.RegisterType((*MsgEmitIBCAckResponse)(nil), "osmosis.ibchooks.MsgEmitIBCAckResponse") +} + +func init() { proto.RegisterFile("osmosis/ibc-hooks/tx.proto", fileDescriptor_93268c51ed820a58) } + +var fileDescriptor_93268c51ed820a58 = []byte{ + // 367 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xcf, 0x4a, 0x2b, 0x31, + 0x14, 0xc6, 0x9b, 0xdb, 0x4b, 0xcb, 0x0d, 0xb4, 0xf7, 0xde, 0x41, 0xa5, 0xcc, 0x62, 0xa6, 0x64, + 0x63, 0x45, 0x3b, 0x83, 0x8a, 0x08, 0xee, 0x3a, 0xc5, 0x85, 0x8b, 0x22, 0x44, 0x70, 0x21, 0x48, + 0x99, 0xc4, 0x30, 0x0d, 0xf3, 0x27, 0xe3, 0x24, 0x95, 0xf6, 0x11, 0xdc, 0xf9, 0x22, 0xbe, 0x87, + 0xcb, 0x2e, 0x5d, 0x15, 0x69, 0xdf, 0xa0, 0x4f, 0x20, 0x9d, 0xc9, 0x40, 0xdb, 0x8d, 0xbb, 0xe4, + 0xfb, 0x7e, 0x27, 0xf9, 0xce, 0x49, 0xa0, 0x29, 0x64, 0x2c, 0x24, 0x97, 0x2e, 0x27, 0xb4, 0x3b, + 0x12, 0x22, 0x94, 0xae, 0x9a, 0x38, 0x69, 0x26, 0x94, 0x30, 0xfe, 0x69, 0xcf, 0xe1, 0x84, 0xe6, + 0x96, 0xb9, 0x17, 0x88, 0x40, 0xe4, 0xa6, 0xbb, 0x5e, 0x15, 0x1c, 0x7a, 0x07, 0xb0, 0x31, 0x90, + 0xc1, 0x75, 0xcc, 0xd5, 0x8d, 0xd7, 0xef, 0xd1, 0xd0, 0x38, 0x82, 0x35, 0xc9, 0x92, 0x27, 0x96, + 0xb5, 0x40, 0x1b, 0x74, 0xfe, 0x78, 0xff, 0x57, 0x73, 0xbb, 0x31, 0xf5, 0xe3, 0xe8, 0x0a, 0x15, + 0x3a, 0xc2, 0x1a, 0x30, 0xfa, 0xf0, 0x6f, 0xea, 0xd3, 0x90, 0xa9, 0xa1, 0x64, 0xcf, 0x63, 0x96, + 0x50, 0xd6, 0xfa, 0xd5, 0x06, 0x9d, 0xdf, 0x9e, 0xb9, 0x9a, 0xdb, 0x07, 0x45, 0xcd, 0x0e, 0x80, + 0x70, 0xb3, 0x50, 0xee, 0xb4, 0x60, 0x9c, 0xc0, 0x3a, 0x1d, 0xf9, 0x49, 0xc2, 0xa2, 0x56, 0x35, + 0xbf, 0xd0, 0x58, 0xcd, 0xed, 0x66, 0x51, 0xac, 0x0d, 0x84, 0x4b, 0x04, 0xbd, 0x02, 0xb8, 0xbf, + 0x95, 0x17, 0x33, 0x99, 0x8a, 0x44, 0xb2, 0x75, 0x18, 0x2a, 0x12, 0x95, 0xf9, 0x54, 0x0d, 0x33, + 0x26, 0xc7, 0x91, 0xd2, 0x0d, 0x6c, 0x84, 0xd9, 0x01, 0x10, 0x6e, 0x96, 0x0a, 0xce, 0x05, 0xe3, + 0x18, 0xd6, 0x39, 0xa1, 0x43, 0x9f, 0x86, 0x79, 0x27, 0x5b, 0x61, 0xb4, 0x81, 0x70, 0x8d, 0x13, + 0xda, 0xa3, 0xe1, 0xd9, 0x23, 0xac, 0x0e, 0x64, 0x60, 0xdc, 0x43, 0xb8, 0x31, 0x3e, 0xdb, 0xd9, + 0x9d, 0xbc, 0xb3, 0x95, 0xd7, 0x3c, 0xfc, 0x01, 0x28, 0x1b, 0xf2, 0x6e, 0x3f, 0x16, 0x16, 0x98, + 0x2d, 0x2c, 0xf0, 0xb5, 0xb0, 0xc0, 0xdb, 0xd2, 0xaa, 0xcc, 0x96, 0x56, 0xe5, 0x73, 0x69, 0x55, + 0x1e, 0x2e, 0x02, 0xae, 0x46, 0x63, 0xe2, 0x50, 0x11, 0xbb, 0xfa, 0xb0, 0x6e, 0xe4, 0x13, 0x59, + 0x6e, 0xdc, 0x97, 0xd3, 0x4b, 0x77, 0xb2, 0xf9, 0x2d, 0xa6, 0x29, 0x93, 0xa4, 0x96, 0x3f, 0xf9, + 0xf9, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6c, 0xeb, 0x3e, 0x0e, 0x38, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // EmitIBCAck checks the sender can emit the ack and writes the IBC + // acknowledgement + EmitIBCAck(ctx context.Context, in *MsgEmitIBCAck, opts ...grpc.CallOption) (*MsgEmitIBCAckResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) EmitIBCAck(ctx context.Context, in *MsgEmitIBCAck, opts ...grpc.CallOption) (*MsgEmitIBCAckResponse, error) { + out := new(MsgEmitIBCAckResponse) + err := c.cc.Invoke(ctx, "/osmosis.ibchooks.Msg/EmitIBCAck", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // EmitIBCAck checks the sender can emit the ack and writes the IBC + // acknowledgement + EmitIBCAck(context.Context, *MsgEmitIBCAck) (*MsgEmitIBCAckResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) EmitIBCAck(ctx context.Context, req *MsgEmitIBCAck) (*MsgEmitIBCAckResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method EmitIBCAck not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_EmitIBCAck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgEmitIBCAck) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).EmitIBCAck(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.ibchooks.Msg/EmitIBCAck", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).EmitIBCAck(ctx, req.(*MsgEmitIBCAck)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "osmosis.ibchooks.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "EmitIBCAck", + Handler: _Msg_EmitIBCAck_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "osmosis/ibc-hooks/tx.proto", +} + +func (m *MsgEmitIBCAck) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgEmitIBCAck) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgEmitIBCAck) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Channel) > 0 { + i -= len(m.Channel) + copy(dAtA[i:], m.Channel) + i = encodeVarintTx(dAtA, i, uint64(len(m.Channel))) + i-- + dAtA[i] = 0x1a + } + if m.PacketSequence != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.PacketSequence)) + i-- + dAtA[i] = 0x10 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgEmitIBCAckResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgEmitIBCAckResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgEmitIBCAckResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.IbcAck) > 0 { + i -= len(m.IbcAck) + copy(dAtA[i:], m.IbcAck) + i = encodeVarintTx(dAtA, i, uint64(len(m.IbcAck))) + i-- + dAtA[i] = 0x12 + } + if len(m.ContractResult) > 0 { + i -= len(m.ContractResult) + copy(dAtA[i:], m.ContractResult) + i = encodeVarintTx(dAtA, i, uint64(len(m.ContractResult))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgEmitIBCAck) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.PacketSequence != 0 { + n += 1 + sovTx(uint64(m.PacketSequence)) + } + l = len(m.Channel) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgEmitIBCAckResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ContractResult) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.IbcAck) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgEmitIBCAck) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgEmitIBCAck: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgEmitIBCAck: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PacketSequence", wireType) + } + m.PacketSequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PacketSequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Channel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgEmitIBCAckResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgEmitIBCAckResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgEmitIBCAckResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractResult", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContractResult = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IbcAck", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IbcAck = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ibc-hooks/types/types.go b/x/ibc-hooks/types/types.go new file mode 100644 index 00000000000..c2afc5e3e89 --- /dev/null +++ b/x/ibc-hooks/types/types.go @@ -0,0 +1,84 @@ +package types + +import ( + "encoding/json" + channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" +) + +// Async: The following types represent the response sent by a contract on OnRecvPacket when it wants the ack to be async + +// OnRecvPacketAsyncAckResponse the response a contract sends to instruct the module to make the ack async +type OnRecvPacketAsyncAckResponse struct { + IsAsyncAck bool `json:"is_async_ack"` +} + +// Async The following types are used to ask a contract that has sent a packet to generate an ack for it + +// RequestAckI internals of IBCAsync +type RequestAckI struct { + PacketSequence uint64 `json:"packet_sequence"` + SourceChannel string `json:"source_channel"` +} + +// RequestAck internals of IBCAsync +type RequestAck struct { + RequestAckI `json:"request_ack"` +} + +// IBCAsync is the sudo message to be sent to the contract for it to generate an ack for a sent packet +type IBCAsync struct { + RequestAck `json:"ibc_async"` +} + +// General + +// ContractAck is the response to be stored when a wasm hook is executed +type ContractAck struct { + ContractResult []byte `json:"contract_result"` + IbcAck []byte `json:"ibc_ack"` +} + +// IBCAckResponse is the response that a contract returns from the sudo() call on OnRecvPacket or RequestAck +type IBCAckResponse struct { + Packet channeltypes.Packet `json:"packet"` + ContractAck ContractAck `json:"contract_ack"` +} + +// IBCAckError is the error that a contract returns from the sudo() call on RequestAck +type IBCAckError struct { + Packet channeltypes.Packet `json:"packet"` + ErrorDescription string `json:"error_description"` + ErrorResponse string `json:"error_response"` +} + +type IBCAck struct { + Type string `json:"type"` + Content json.RawMessage `json:"content"` + // Note: These two fields have to be pointers so that they can be null + // If they are not pointers, they will be empty structs when null, + // which will cause issues with json.Unmarshal. + AckResponse *IBCAckResponse `json:"response,omitempty"` + AckError *IBCAckError `json:"error,omitempty"` +} + +func UnmarshalIBCAck(bz []byte) (*IBCAck, error) { + var ack IBCAck + if err := json.Unmarshal(bz, &ack); err != nil { + return nil, err + } + + switch ack.Type { + case "ack_response": + ack.AckResponse = &IBCAckResponse{} + if err := json.Unmarshal(ack.Content, ack.AckResponse); err != nil { + return nil, err + } + case "ack_error": + ack.AckError = &IBCAckError{} + if err := json.Unmarshal(ack.Content, ack.AckError); err != nil { + return nil, err + } + } + + return &ack, nil +} diff --git a/x/ibc-hooks/wasm_hook.go b/x/ibc-hooks/wasm_hook.go index 75ed8e5ef5b..8c169649b31 100644 --- a/x/ibc-hooks/wasm_hook.go +++ b/x/ibc-hooks/wasm_hook.go @@ -3,18 +3,17 @@ package ibc_hooks import ( "encoding/json" "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" errorsmod "cosmossdk.io/errors" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" "github.com/osmosis-labs/osmosis/x/ibc-hooks/keeper" - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - "github.com/osmosis-labs/osmosis/osmoutils" - sdk "github.com/cosmos/cosmos-sdk/types" transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" ibcexported "github.com/cosmos/ibc-go/v4/modules/core/exported" @@ -22,11 +21,6 @@ import ( "github.com/osmosis-labs/osmosis/x/ibc-hooks/types" ) -type ContractAck struct { - ContractResult []byte `json:"contract_result"` - IbcAck []byte `json:"ibc_ack"` -} - type WasmHooks struct { ContractKeeper *wasmkeeper.PermissionedKeeper ibcHooksKeeper *keeper.Keeper @@ -117,7 +111,24 @@ func (h WasmHooks) OnRecvPacketOverride(im IBCMiddleware, ctx sdk.Context, packe return osmoutils.NewEmitErrorAcknowledgement(ctx, types.ErrWasmError, err.Error()) } - fullAck := ContractAck{ContractResult: response.Data, IbcAck: ack.Acknowledgement()} + // Check if the contract is requesting for the ack to be async. + var asyncAckRequest types.OnRecvPacketAsyncAckResponse + err = json.Unmarshal(response.Data, &asyncAckRequest) + if err == nil { + // If unmarshalling succeeds, the contract is requesting for the ack to be async. + if asyncAckRequest.IsAsyncAck { // in which case IsAsyncAck is expected to be set to true + if !h.ibcHooksKeeper.IsInAllowList(ctx, contractAddr.String()) { + // Only allowed contracts can send async acks + return osmoutils.NewEmitErrorAcknowledgement(ctx, types.ErrAsyncAckNotAllowed) + } + // Store the contract as the packet's ack actor and return nil + h.ibcHooksKeeper.StorePacketAckActor(ctx, packet, contractAddr.String()) + return nil + } + } + + // If the ack is not async, we continue generating the ack and return it + fullAck := types.ContractAck{ContractResult: response.Data, IbcAck: ack.Acknowledgement()} bz, err = json.Marshal(fullAck) if err != nil { return osmoutils.NewEmitErrorAcknowledgement(ctx, types.ErrBadResponse, err.Error()) diff --git a/x/ibc-rate-limit/bytecode/rate_limiter.wasm b/x/ibc-rate-limit/bytecode/rate_limiter.wasm index 9dee6eabb6f..30b545e0612 100644 Binary files a/x/ibc-rate-limit/bytecode/rate_limiter.wasm and b/x/ibc-rate-limit/bytecode/rate_limiter.wasm differ diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/sudo.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/sudo.rs index 80e6985d578..c42b5ea7e2d 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/src/sudo.rs +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/sudo.rs @@ -21,7 +21,8 @@ pub fn process_packet( #[cfg(test)] channel_value_mock: Option, ) -> Result { let (channel_id, denom) = packet.path_data(&direction); - let path = &Path::new(channel_id, denom); + #[allow(clippy::needless_borrow)] + let path = &Path::new(&channel_id, &denom); let funds = packet.get_funds(); #[cfg(test)] @@ -144,7 +145,8 @@ fn add_rate_limit_attributes(response: Response, result: &RateLimit) -> Response pub fn undo_send(deps: DepsMut, packet: Packet) -> Result { // Sudo call. Only go modules should be allowed to access this let (channel_id, denom) = packet.path_data(&FlowType::Out); // Sends have direction out. - let path = &Path::new(channel_id, &denom); + #[allow(clippy::needless_borrow)] + let path = &Path::new(&channel_id, &denom); let any_path = Path::new("any", &denom); let funds = packet.get_funds(); diff --git a/x/incentives/keeper/distribute.go b/x/incentives/keeper/distribute.go index d99e4b28cda..f8cee21bd3e 100644 --- a/x/incentives/keeper/distribute.go +++ b/x/incentives/keeper/distribute.go @@ -266,6 +266,69 @@ func (k Keeper) distributeSyntheticInternal( return k.distributeInternal(ctx, gauge, sortedAndTrimmedQualifiedLocks, distrInfo) } +// AllocateAcrossGauges gets all the active groupGauges and distributes tokens evenly based on the internalGauges set for that +// groupGauge. After each iteration we update the groupGauge by modifying filledEpoch and distributed coins. +func (k Keeper) AllocateAcrossGauges(ctx sdk.Context) error { + currTime := ctx.BlockTime() + + groupGauges, err := k.GetAllGroupGauges(ctx) + if err != nil { + return err + } + + for _, groupGauge := range groupGauges { + gauge, err := k.GetGaugeByID(ctx, groupGauge.GroupGaugeId) + if err != nil { + return err + } + + // only allow distribution if the GroupGauge is Active + if gauge.IsActiveGauge(currTime) { + coinsToDistributePerInternalGauge, coinsToDistributeThisEpoch, err := k.calcSplitPolicyCoins(ctx, groupGauge.SplittingPolicy, gauge, groupGauge) + if err != nil { + return err + } + + for _, internalGaugeId := range groupGauge.InternalIds { + err = k.AddToGaugeRewardsFromGauge(ctx, groupGauge.GroupGaugeId, coinsToDistributePerInternalGauge, internalGaugeId) + if err != nil { + return err + } + } + + // we distribute tokens from groupGauge to internal gauge therefore update groupGauge fields + // updates filledEpoch and distributedCoins + k.updateGaugePostDistribute(ctx, *gauge, coinsToDistributeThisEpoch) + } + } + + return nil +} + +// calcSplitPolicyCoins calculates tokens to split given a policy and groupGauge. +// TODO: add volume split policy +func (k Keeper) calcSplitPolicyCoins(ctx sdk.Context, policy types.SplittingPolicy, groupGauge *types.Gauge, groupGaugeObj types.GroupGauge) (sdk.Coins, sdk.Coins, error) { + if policy == types.Evenly { + remainCoins := groupGauge.Coins.Sub(groupGauge.DistributedCoins) + + var coinsDistPerInternalGauge, coinsDistThisEpoch sdk.Coins + for _, coin := range remainCoins { + epochDiff := groupGauge.NumEpochsPaidOver - groupGauge.FilledEpochs + internalGaugeLen := len(groupGaugeObj.InternalIds) + + distPerEpoch := coin.Amount.Quo(sdk.NewIntFromUint64(epochDiff)) + distPerGauge := distPerEpoch.Quo(sdk.NewInt(int64(internalGaugeLen))) + + coinsDistThisEpoch = coinsDistThisEpoch.Add(sdk.NewCoin(coin.Denom, distPerEpoch)) + coinsDistPerInternalGauge = coinsDistPerInternalGauge.Add(sdk.NewCoin(coin.Denom, distPerGauge)) + } + + return coinsDistPerInternalGauge, coinsDistThisEpoch, nil + } else { + return nil, nil, fmt.Errorf("GroupGauge id %d doesnot have enought coins to distribute.", &groupGauge.Id) + } +} + // distributeInternal runs the distribution logic for a gauge, and adds the sends to // the distrInfo struct. It also updates the gauge for the distribution. // It handles any kind of gauges: @@ -285,6 +348,7 @@ func (k Keeper) distributeInternal( totalDistrCoins := sdk.NewCoins() remainCoins := gauge.Coins.Sub(gauge.DistributedCoins) + // if its a perpetual gauge, we set remaining epochs to 1. // otherwise is is a non perpetual gauge and we determine how many epoch payouts are left remainEpochs := uint64(1) @@ -329,7 +393,6 @@ func (k Keeper) distributeInternal( // for ex: 10000uosmo to be distributed over 1day epoch will be 1000 tokens ÷ 86,400 seconds ≈ 0.01157 tokens per second (truncated) // Note: reason why we do millisecond conversion is because floats are non-deterministic. emissionRate := sdk.NewDecFromInt(remainAmountPerEpoch).QuoTruncate(sdk.NewDec(currentEpoch.Duration.Milliseconds()).QuoInt(sdk.NewInt(1000))) - ctx.Logger().Debug("distributeInternal, CreateIncentiveRecord NoLock gauge", "module", types.ModuleName, "gaugeId", gauge.Id, "poolId", pool.GetId(), "remainCoinPerEpoch", remainCoinPerEpoch, "height", ctx.BlockHeight()) _, err := k.clk.CreateIncentive(ctx, pool.GetId(), @@ -346,7 +409,6 @@ func (k Keeper) distributeInternal( if err != nil { return nil, err } - totalDistrCoins = totalDistrCoins.Add(remainCoinPerEpoch) } } else { @@ -441,6 +503,11 @@ func (k Keeper) Distribute(ctx sdk.Context, gauges []types.Gauge) (sdk.Coins, er ctx.Logger().Debug("distributeSyntheticInternal, gauge id %d, %d", "module", types.ModuleName, "gaugeId", gauge.Id, "height", ctx.BlockHeight()) gaugeDistributedCoins, err = k.distributeSyntheticInternal(ctx, gauge, filteredLocks, &distrInfo) } else { + // Do not distribute if LockQueryType = Group, because if we distribute here we will be double distributing. + if gauge.DistributeTo.LockQueryType == lockuptypes.ByGroup { + continue + } + gaugeDistributedCoins, err = k.distributeInternal(ctx, gauge, filteredLocks, &distrInfo) } if err != nil { diff --git a/x/incentives/keeper/distribute_test.go b/x/incentives/keeper/distribute_test.go index 21139702898..bfe8f4d49dd 100644 --- a/x/incentives/keeper/distribute_test.go +++ b/x/incentives/keeper/distribute_test.go @@ -7,7 +7,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" - osmoutils "github.com/osmosis-labs/osmosis/osmoutils" appParams "github.com/osmosis-labs/osmosis/v17/app/params" "github.com/osmosis-labs/osmosis/v17/x/incentives/types" lockuptypes "github.com/osmosis-labs/osmosis/v17/x/lockup/types" @@ -17,6 +16,13 @@ import ( var _ = suite.TestingSuite(nil) +type GroupGaugeCreationFields struct { + coins sdk.Coins + numEpochPaidOver uint64 + owner sdk.AccAddress + internalGaugeIds []uint64 +} + // TestDistribute tests that when the distribute command is executed on a provided gauge // that the correct amount of rewards is sent to the correct lock owners. func (s *KeeperTestSuite) TestDistribute() { @@ -921,14 +927,6 @@ func (s *KeeperTestSuite) TestFunctionalInternalExternalCLGauge() { internalGaugeCoins = sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(defaultInternalGaugeValue))) // distributed full sum at epoch externalGaugeCoins = sdk.NewCoins(sdk.NewCoin("eth", sdk.NewInt(defaultExternalGaugeValue)), sdk.NewCoin("usdc", sdk.NewInt(defaultExternalGaugeValue))) // distributed full sum at epoch halfOfExternalGaugeCoins = sdk.NewCoins(sdk.NewCoin("eth", sdk.NewInt(defaultExternalGaugeValue/numEpochsPaidOverGaugeTwo)), sdk.NewCoin("usdc", sdk.NewInt(defaultExternalGaugeValue/numEpochsPaidOverGaugeTwo))) // distributed at each epoch for non-perp gauge with numEpoch = 2 - - internalGaugeDecCoins = osmoutils.ConvertCoinsToDecCoins(internalGaugeCoins) - externalGaugeDecCoins = osmoutils.ConvertCoinsToDecCoins(externalGaugeCoins) - halfOfExternalGaugeDecCoins = osmoutils.ConvertCoinsToDecCoins(halfOfExternalGaugeCoins) - - emissionRateForPool1 = sdk.NewDecFromInt(sdk.NewInt(defaultExternalGaugeValue)).QuoTruncate(sdk.NewDec(epochInfo.Duration.Milliseconds()).QuoInt(sdk.NewInt(1000))) - emissionRateForPool2 = sdk.NewDecFromInt(sdk.NewInt(defaultExternalGaugeValue / 2)).QuoTruncate(sdk.NewDec(epochInfo.Duration.Milliseconds()).QuoInt(sdk.NewInt(1000))) - emissionRateForInternalTokens = sdk.NewDecFromInt(sdk.NewInt(defaultInternalGaugeValue)).QuoTruncate(sdk.NewDec(epochInfo.Duration.Milliseconds()).QuoInt(sdk.NewInt(1000))) ) s.FundAcc(s.TestAccs[1], requiredBalances) @@ -995,17 +993,17 @@ func (s *KeeperTestSuite) TestFunctionalInternalExternalCLGauge() { s.Require().Equal(2, len(clPool2IncentiveRecordsAtEpoch1)) s.Require().Equal(2, len(clPool3IncentiveRecordsAtEpoch1)) - s.ValidateIncentiveRecord(clPoolId1.GetId(), externalGaugeDecCoins[0], emissionRateForPool1, clPool1IncentiveRecordsAtEpoch1[0]) - s.ValidateIncentiveRecord(clPoolId1.GetId(), externalGaugeDecCoins[1], emissionRateForPool1, clPool1IncentiveRecordsAtEpoch1[1]) + s.ValidateIncentiveRecord(clPoolId1.GetId(), externalGaugeCoins[0], clPool1IncentiveRecordsAtEpoch1[0]) + s.ValidateIncentiveRecord(clPoolId1.GetId(), externalGaugeCoins[1], clPool1IncentiveRecordsAtEpoch1[1]) // Note: ClPool2 will receive 500kusdc, 500keth in this epoch. - s.ValidateIncentiveRecord(clPoolId2.GetId(), halfOfExternalGaugeDecCoins[0], emissionRateForPool2, clPool2IncentiveRecordsAtEpoch1[0]) - s.ValidateIncentiveRecord(clPoolId2.GetId(), halfOfExternalGaugeDecCoins[1], emissionRateForPool2, clPool2IncentiveRecordsAtEpoch1[1]) + s.ValidateIncentiveRecord(clPoolId2.GetId(), halfOfExternalGaugeCoins[0], clPool2IncentiveRecordsAtEpoch1[0]) + s.ValidateIncentiveRecord(clPoolId2.GetId(), halfOfExternalGaugeCoins[1], clPool2IncentiveRecordsAtEpoch1[1]) // Note: ClPool3 will receive full 1Musdc, 1Meth in this epoch. // Note: emission rate is the same as CLPool1 because we are distributed same amount over 1 epoch. - s.ValidateIncentiveRecord(clPoolId3.GetId(), externalGaugeDecCoins[0], emissionRateForPool1, clPool3IncentiveRecordsAtEpoch1[0]) - s.ValidateIncentiveRecord(clPoolId3.GetId(), externalGaugeDecCoins[1], emissionRateForPool1, clPool3IncentiveRecordsAtEpoch1[1]) + s.ValidateIncentiveRecord(clPoolId3.GetId(), externalGaugeCoins[0], clPool3IncentiveRecordsAtEpoch1[0]) + s.ValidateIncentiveRecord(clPoolId3.GetId(), externalGaugeCoins[1], clPool3IncentiveRecordsAtEpoch1[1]) // 6. Remove distribution records for internal incentives using HandleReplacePoolIncentivesProposal s.IncentivizeInternalGauge([]uint64{clPoolId1.GetId(), clPoolId2.GetId()}, epochInfo.Duration, true) @@ -1041,16 +1039,16 @@ func (s *KeeperTestSuite) TestFunctionalInternalExternalCLGauge() { s.Require().Equal(2, len(clPool3IncentiveRecordsAtEpoch2)) // Note: ClPool1 will receive 1Musdc, 1Meth (from epoch1) as external incentive, 750Kstake as internal incentive. - s.ValidateIncentiveRecord(clPoolId1.GetId(), externalGaugeDecCoins[0], emissionRateForPool1, clPool1IncentiveRecordsAtEpoch2[0]) - s.ValidateIncentiveRecord(clPoolId1.GetId(), externalGaugeDecCoins[1], emissionRateForPool1, clPool1IncentiveRecordsAtEpoch2[1]) - s.ValidateIncentiveRecord(clPoolId1.GetId(), internalGaugeDecCoins[0], emissionRateForInternalTokens, clPool1IncentiveRecordsAtEpoch2[2]) + s.ValidateIncentiveRecord(clPoolId1.GetId(), externalGaugeCoins[0], clPool1IncentiveRecordsAtEpoch2[0]) + s.ValidateIncentiveRecord(clPoolId1.GetId(), externalGaugeCoins[1], clPool1IncentiveRecordsAtEpoch2[1]) + s.ValidateIncentiveRecord(clPoolId1.GetId(), internalGaugeCoins[0], clPool1IncentiveRecordsAtEpoch2[2]) // Note: ClPool2 will receive 500kusdc, 500keth (from epoch1) as external incentive, 500kusdc, 500keth (from epoch 2) as external incentive and 750Kstake as internal incentive. - s.ValidateIncentiveRecord(clPoolId2.GetId(), halfOfExternalGaugeDecCoins[1], emissionRateForPool2, clPool2IncentiveRecordsAtEpoch2[0]) // new record - s.ValidateIncentiveRecord(clPoolId2.GetId(), halfOfExternalGaugeDecCoins[0], emissionRateForPool2, clPool2IncentiveRecordsAtEpoch2[1]) // new record - s.ValidateIncentiveRecord(clPoolId2.GetId(), halfOfExternalGaugeDecCoins[1], emissionRateForPool2, clPool2IncentiveRecordsAtEpoch2[2]) // new record - s.ValidateIncentiveRecord(clPoolId2.GetId(), internalGaugeDecCoins[0], emissionRateForInternalTokens, clPool2IncentiveRecordsAtEpoch2[3]) // old record - s.ValidateIncentiveRecord(clPoolId2.GetId(), halfOfExternalGaugeDecCoins[0], emissionRateForPool2, clPool2IncentiveRecordsAtEpoch2[4]) // old record + s.ValidateIncentiveRecord(clPoolId2.GetId(), halfOfExternalGaugeCoins[1], clPool2IncentiveRecordsAtEpoch2[0]) // new record + s.ValidateIncentiveRecord(clPoolId2.GetId(), halfOfExternalGaugeCoins[0], clPool2IncentiveRecordsAtEpoch2[1]) // new record + s.ValidateIncentiveRecord(clPoolId2.GetId(), halfOfExternalGaugeCoins[1], clPool2IncentiveRecordsAtEpoch2[2]) // new record + s.ValidateIncentiveRecord(clPoolId2.GetId(), internalGaugeCoins[0], clPool2IncentiveRecordsAtEpoch2[3]) // old record + s.ValidateIncentiveRecord(clPoolId2.GetId(), halfOfExternalGaugeCoins[0], clPool2IncentiveRecordsAtEpoch2[4]) // old record // all incentive for ClPoolId3 have already been distributed in epoch1. There is nothing left to distribute. s.Require().Equal(clPool3IncentiveRecordsAtEpoch1, clPool3IncentiveRecordsAtEpoch2) @@ -1080,13 +1078,8 @@ func (s *KeeperTestSuite) TestFunctionalInternalExternalCLGauge() { } func (s *KeeperTestSuite) CreateNoLockExternalGauges(clPoolId uint64, externalGaugeCoins sdk.Coins, gaugeCreator sdk.AccAddress, numEpochsPaidOver uint64) uint64 { - isPerp := false - if numEpochsPaidOver == uint64(1) { - isPerp = true - } - // Create 1 external no-lock gauge perpetual over 1 epochs MsgCreateGauge - clPoolExternalGaugeId, err := s.App.IncentivesKeeper.CreateGauge(s.Ctx, isPerp, gaugeCreator, externalGaugeCoins, + clPoolExternalGaugeId, err := s.App.IncentivesKeeper.CreateGauge(s.Ctx, numEpochsPaidOver == 1, gaugeCreator, externalGaugeCoins, lockuptypes.QueryCondition{ LockQueryType: lockuptypes.NoLock, }, @@ -1127,3 +1120,252 @@ func (s *KeeperTestSuite) IncentivizeInternalGauge(poolIds []uint64, epochDurati ) s.Require().NoError(err) } +func (s *KeeperTestSuite) TestAllocateAcrossGauges() { + tests := []struct { + name string + GroupGauge types.GroupGauge + expectedAllocationPerGroupGauge sdk.Coins + expectedAllocationPerInternalGauge sdk.Coins + expectError bool + }{ + { + name: "Happy case: Valid perp Group Gauge", + GroupGauge: types.GroupGauge{ + GroupGaugeId: 9, + InternalIds: []uint64{2, 3, 4}, + SplittingPolicy: types.Evenly, + }, + expectedAllocationPerGroupGauge: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000))), + expectedAllocationPerInternalGauge: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(33_333_333))), + expectError: false, + }, + { + name: "Happy Case: Valid non-perp Group Gauge", + GroupGauge: types.GroupGauge{ + GroupGaugeId: 10, + InternalIds: []uint64{5, 6, 7}, + SplittingPolicy: types.Evenly, + }, + expectedAllocationPerGroupGauge: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(50_000_000))), + expectedAllocationPerInternalGauge: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(16_666_666))), + expectError: false, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + s.SetupTest() + s.FundAcc(s.TestAccs[1], sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(200_000_000)))) + clPool := s.PrepareConcentratedPool() + + // create 3 internal Gauge + internalGauges := s.setupNoLockInternalGauge(clPool.GetId(), uint64(6)) // gauge Id = 2,3,4,5,6,7 + + // create non-perp internal Gauge + s.CreateNoLockExternalGauges(clPool.GetId(), sdk.NewCoins(), s.TestAccs[1], uint64(2)) // gaugeid= 8 + + // create perp group gauge + _, err := s.App.IncentivesKeeper.CreateGroupGauge(s.Ctx, sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000))), uint64(1), s.TestAccs[1], internalGauges[:3], lockuptypes.ByGroup, types.Evenly) // gauge id = 2,3,4 + s.Require().NoError(err) + + // create non-perp group gauge + _, err = s.App.IncentivesKeeper.CreateGroupGauge(s.Ctx, sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000))), uint64(2), s.TestAccs[1], internalGauges[len(internalGauges)-3:], lockuptypes.ByGroup, types.Evenly) // gauge id = 5,6,7 + s.Require().NoError(err) + + // Call Testing function + err = s.App.IncentivesKeeper.AllocateAcrossGauges(s.Ctx) + if tc.expectError { + s.Require().Error(err) + } else { + s.Require().NoError(err) + + groupGaugePostAllocate, err := s.App.IncentivesKeeper.GetGaugeByID(s.Ctx, tc.GroupGauge.GroupGaugeId) + s.Require().NoError(err) + + s.Require().Equal(groupGaugePostAllocate.DistributedCoins, tc.expectedAllocationPerGroupGauge) + + for _, gauge := range tc.GroupGauge.InternalIds { + internalGauge, err := s.App.IncentivesKeeper.GetGaugeByID(s.Ctx, gauge) + s.Require().NoError(err) + + s.Require().Equal(internalGauge.Coins, tc.expectedAllocationPerInternalGauge) + } + } + }) + } +} + +func (s *KeeperTestSuite) WithBaseCaseDifferentCoins(baseCase GroupGaugeCreationFields, newCoins sdk.Coins) GroupGaugeCreationFields { + baseCase.coins = newCoins + return baseCase +} + +func (s *KeeperTestSuite) WithBaseCaseDifferentEpochPaidOver(baseCase GroupGaugeCreationFields, numEpochPaidOver uint64) GroupGaugeCreationFields { + baseCase.numEpochPaidOver = numEpochPaidOver + return baseCase +} + +func (s *KeeperTestSuite) WithBaseCaseDifferentInternalGauges(baseCase GroupGaugeCreationFields, internalGauges []uint64) GroupGaugeCreationFields { + baseCase.internalGaugeIds = internalGauges + return baseCase +} + +func (s *KeeperTestSuite) TestCreateGroupGaugeAndDistribute() { + hundredKUosmo := sdk.NewCoin("uosmo", sdk.NewInt(100_000_000)) + hundredKUatom := sdk.NewCoin("uatom", sdk.NewInt(100_000_000)) + fifetyKUosmo := sdk.NewCoin("uosmo", sdk.NewInt(50_000_000)) + fifetyKUatom := sdk.NewCoin("uatom", sdk.NewInt(50_000_000)) + twentyfiveKUosmo := sdk.NewCoin("uosmo", sdk.NewInt(25_000_000)) + twentyfiveKUatom := sdk.NewCoin("uatom", sdk.NewInt(25_000_000)) + + baseCase := &GroupGaugeCreationFields{ + coins: sdk.NewCoins(hundredKUosmo), + numEpochPaidOver: 1, + owner: s.TestAccs[1], + internalGaugeIds: []uint64{2, 3, 4, 5}, + } + + tests := []struct { + name string + createGauge GroupGaugeCreationFields + expectedCoinsPerInternalGauge sdk.Coins + expectedCoinsDistributedPerEpoch sdk.Coins + expectCreateGroupGaugeError bool + expectDistributeToInternalGaugeError bool + }{ + { + name: "Valid case: Valid perp-GroupGauge Creation and Distribution", + createGauge: *baseCase, + expectedCoinsPerInternalGauge: sdk.NewCoins(twentyfiveKUosmo), // 100osmo / 4 = 25osmo + expectedCoinsDistributedPerEpoch: sdk.NewCoins(hundredKUosmo), + }, + { + name: "Valid case: Valid perp-GroupGauge Creation with only CL internal gauges and Distribution", + createGauge: s.WithBaseCaseDifferentInternalGauges(*baseCase, []uint64{2, 3, 4}), + expectedCoinsPerInternalGauge: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(33_333_333))), + expectedCoinsDistributedPerEpoch: sdk.NewCoins(hundredKUosmo), + }, + { + name: "Valid case: Valid perp-GroupGauge Creation with only GAMM internal gauge and Distribution", + createGauge: s.WithBaseCaseDifferentInternalGauges(*baseCase, []uint64{5}), + expectedCoinsPerInternalGauge: sdk.NewCoins(hundredKUosmo), + expectedCoinsDistributedPerEpoch: sdk.NewCoins(hundredKUosmo), + }, + { + name: "Valid case: Valid non-perpGroupGauge Creation with and Distribution", + createGauge: s.WithBaseCaseDifferentEpochPaidOver(*baseCase, uint64(4)), + expectedCoinsPerInternalGauge: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(6_250_000))), + expectedCoinsDistributedPerEpoch: sdk.NewCoins(twentyfiveKUosmo), + }, + { + name: "Valid case: Valid perp-GroupGauge Creation with 2 coins and Distribution", + createGauge: s.WithBaseCaseDifferentCoins(*baseCase, sdk.NewCoins(hundredKUosmo, hundredKUatom)), + expectedCoinsPerInternalGauge: sdk.NewCoins(twentyfiveKUosmo, twentyfiveKUatom), + expectedCoinsDistributedPerEpoch: sdk.NewCoins(hundredKUosmo, hundredKUatom), + }, + { + name: "Valid case: Valid non-perp GroupGauge Creation with 2 coins and Distribution", + createGauge: s.WithBaseCaseDifferentEpochPaidOver(s.WithBaseCaseDifferentCoins(*baseCase, sdk.NewCoins(hundredKUosmo, hundredKUatom)), uint64(2)), + expectedCoinsPerInternalGauge: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(12_500_000)), sdk.NewCoin("uatom", sdk.NewInt(12_500_000))), + expectedCoinsDistributedPerEpoch: sdk.NewCoins(fifetyKUosmo, fifetyKUatom), + }, + { + name: "InValid case: Creating a GroupGauge with invalid internalIds", + createGauge: s.WithBaseCaseDifferentInternalGauges(*baseCase, []uint64{100, 101}), + expectCreateGroupGaugeError: true, + }, + { + name: "InValid case: Creating a GroupGauge with non-perpetual internalId", + createGauge: s.WithBaseCaseDifferentInternalGauges(*baseCase, []uint64{2, 3, 4, 6}), + expectCreateGroupGaugeError: true, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + s.SetupTest() + s.FundAcc(s.TestAccs[1], sdk.NewCoins(hundredKUosmo, hundredKUatom)) // 100osmo, 100atom + + // Setup + clPool := s.PrepareConcentratedPool() + lockOwner := sdk.AccAddress([]byte("addr1---------------")) + epochInfo := s.App.IncentivesKeeper.GetEpochInfo(s.Ctx) + s.SetupGroupGauge(clPool.GetId(), lockOwner, uint64(3), uint64(1)) + + //create 1 non-perp internal Gauge + s.CreateNoLockExternalGauges(clPool.GetId(), sdk.NewCoins(), s.TestAccs[1], uint64(2)) // gauge id = 6 + + groupGaugeId, err := s.App.IncentivesKeeper.CreateGroupGauge(s.Ctx, tc.createGauge.coins, tc.createGauge.numEpochPaidOver, tc.createGauge.owner, tc.createGauge.internalGaugeIds, lockuptypes.ByGroup, types.Evenly) // gauge id = 6 + if tc.expectCreateGroupGaugeError { + s.Require().Error(err) + return + } + + s.Require().NoError(err) + + groupGaugeObj, err := s.App.IncentivesKeeper.GetGroupGaugeById(s.Ctx, groupGaugeId) + s.Require().NoError(err) + + // check internalGauges matches what we expect + s.Require().Equal(groupGaugeObj.InternalIds, tc.createGauge.internalGaugeIds) + + for epoch := uint64(1); epoch <= tc.createGauge.numEpochPaidOver; epoch++ { + // ******************** EPOCH PASSED ********************* // + s.Ctx = s.Ctx.WithBlockTime(s.Ctx.BlockTime().Add(epochInfo.Duration)) + s.App.EpochsKeeper.AfterEpochEnd(s.Ctx, epochInfo.GetIdentifier(), int64(epoch)) + + // Validate GroupGauge + groupGauge, err := s.App.IncentivesKeeper.GetGaugeByID(s.Ctx, groupGaugeId) + s.Require().NoError(err) + + var expectedDistributedCoins []sdk.Coin + for _, coin := range tc.expectedCoinsDistributedPerEpoch { + expectedDistributedCoins = append(expectedDistributedCoins, sdk.NewCoin(coin.Denom, coin.Amount.Mul(sdk.NewIntFromUint64(epoch)))) + } + + s.ValidateDistributedGauge(groupGauge.Id, epoch, expectedDistributedCoins) + + // Validate Internal Gauges + internalGauges, err := s.App.IncentivesKeeper.GetGaugeFromIDs(s.Ctx, tc.createGauge.internalGaugeIds) + s.Require().NoError(err) + + for _, internalGauge := range internalGauges { + var expectedDistributedCoinsPerInternalGauge []sdk.Coin + for _, coin := range tc.expectedCoinsPerInternalGauge { + expectedDistributedCoinsPerInternalGauge = append(expectedDistributedCoinsPerInternalGauge, (sdk.NewCoin(coin.Denom, coin.Amount.Mul(sdk.NewIntFromUint64(epoch))))) + } + s.ValidateDistributedGauge(internalGauge.Id, epoch, expectedDistributedCoinsPerInternalGauge) + } + + // Validate CL Incentive distribution + poolIncentives, err := s.App.ConcentratedLiquidityKeeper.GetAllIncentiveRecordsForPool(s.Ctx, clPool.GetId()) + s.Require().NoError(err) + + for i := 0; i < len(poolIncentives); i++ { + idx := 0 + // the logic below is for indexing incentiveRecord, flips idx from 0,1,0,1 or 1,0,1,0 etc. + if len(tc.expectedCoinsPerInternalGauge) > 1 { + if epoch == 2 { + idx = 1 - (i % 2) + } else { + idx = i % 2 + } + } + s.ValidateIncentiveRecord(clPool.GetId(), tc.expectedCoinsPerInternalGauge[idx], poolIncentives[i]) + } + + // Validate GAMM incentive distribution + balances := s.App.BankKeeper.GetAllBalances(s.Ctx, lockOwner) + if len(balances) != 0 { + var coins sdk.Coins + for _, bal := range tc.expectedCoinsPerInternalGauge { + coin := sdk.NewCoin(bal.Denom, bal.Amount.Mul(sdk.NewIntFromUint64(epoch))) + coins = append(coins, coin) + } + + s.Require().Equal(balances, coins) + } + } + }) + } +} diff --git a/x/incentives/keeper/gauge.go b/x/incentives/keeper/gauge.go index e9601ec9e3e..499efbb478d 100644 --- a/x/incentives/keeper/gauge.go +++ b/x/incentives/keeper/gauge.go @@ -208,6 +208,129 @@ func (k Keeper) CreateGauge(ctx sdk.Context, isPerpetual bool, owner sdk.AccAddr return gauge.Id, nil } +// CreateGroupGauge creates a new gauge, that allocates rewards dynamically across its internal gauges based on the given splitting policy. +// Note: we should expect that the internal gauges consist of the gauges that are automatically created for each pool upon pool creation, as even non-perpetual +// external incentives would likely want to route through these. +func (k Keeper) CreateGroupGauge(ctx sdk.Context, coins sdk.Coins, numEpochPaidOver uint64, owner sdk.AccAddress, internalGaugeIds []uint64, gaugetype lockuptypes.LockQueryType, splittingPolicy types.SplittingPolicy) (uint64, error) { + if len(internalGaugeIds) == 0 { + return 0, fmt.Errorf("No internalGauge provided.") + } + + if gaugetype != lockuptypes.ByGroup { + return 0, fmt.Errorf("Invalid gauge type needs to be ByGroup, got %s.", gaugetype) + } + + // TODO: remove this check once volume splitting is implemented + if splittingPolicy != types.Evenly { + return 0, fmt.Errorf("Invalid splitting policy, needs to be Evenly got %s", splittingPolicy) + } + + // check that all the internalGaugeIds exist + internalGauges, err := k.GetGaugeFromIDs(ctx, internalGaugeIds) + if err != nil { + return 0, fmt.Errorf("Invalid internalGaugeIds, please make sure all the internalGauge have been created.") + } + + // check that all internalGauges are perp + for _, gauge := range internalGauges { + if !gauge.IsPerpetual { + return 0, fmt.Errorf("Internal Gauge id %d is non-perp, all internalGauge must be perpetual Gauge.", gauge.Id) + } + } + + nextGaugeId := k.GetLastGaugeID(ctx) + 1 + + gauge := types.Gauge{ + Id: nextGaugeId, + IsPerpetual: numEpochPaidOver == 1, + DistributeTo: lockuptypes.QueryCondition{ + LockQueryType: gaugetype, + }, + Coins: coins, + StartTime: ctx.BlockTime(), + NumEpochsPaidOver: numEpochPaidOver, + } + + if err := k.bk.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, gauge.Coins); err != nil { + return 0, err + } + + if err := k.setGauge(ctx, &gauge); err != nil { + return 0, err + } + + newGroupGauge := types.GroupGauge{ + GroupGaugeId: nextGaugeId, + InternalIds: internalGaugeIds, + SplittingPolicy: splittingPolicy, + } + + k.SetGroupGauge(ctx, newGroupGauge) + k.SetLastGaugeID(ctx, gauge.Id) + + // TODO: check if this is necessary, will investigate this in following PR. + combinedKeys := combineKeys(types.KeyPrefixUpcomingGauges, getTimeKey(gauge.StartTime)) + activeOrUpcomingGauge := true + + if err := k.CreateGaugeRefKeys(ctx, &gauge, combinedKeys, activeOrUpcomingGauge); err != nil { + return 0, err + } + k.hooks.AfterCreateGauge(ctx, gauge.Id) + + return nextGaugeId, nil +} + +// AddToGaugeRewardsFromGauge transfer coins from groupGaugeId to InternalGaugeId. +// Prior to calling this function, we make sure that the internalGaugeId is linked with the associated groupGaugeId. +// Note: we donot have to bankSend for this gauge transfer because all the available incentive has already been bank sent +// when we create Group Gauge. Now we are just allocating funds from groupGauge to internalGauge. +func (k Keeper) AddToGaugeRewardsFromGauge(ctx sdk.Context, groupGaugeId uint64, coins sdk.Coins, internalGaugeId uint64) error { + // check if the internalGaugeId is present in groupGauge + groupGaugeObj, err := k.GetGroupGaugeById(ctx, groupGaugeId) + if err != nil { + return err + } + found := false + for _, val := range groupGaugeObj.InternalIds { + if val == internalGaugeId { + found = true + break + } + } + + if !found { + return fmt.Errorf("InternalGaugeId %d is not present in groupGauge: %v", internalGaugeId, groupGaugeObj.InternalIds) + } + + groupGauge, err := k.GetGaugeByID(ctx, groupGaugeId) + if err != nil { + return err + } + + internalGauge, err := k.GetGaugeByID(ctx, internalGaugeId) + if err != nil { + return err + } + + if internalGauge.IsFinishedGauge(ctx.BlockTime()) { + return errors.New("gauge is already completed") + } + + // check if there is sufficient funds in groupGauge to make the transfer + remainingCoins := groupGauge.Coins.Sub(groupGauge.DistributedCoins) + if remainingCoins.IsAllLT(coins) { + return fmt.Errorf("group gauge id: %d doesnot have enough tokens to transfer", groupGaugeId) + } + + internalGauge.Coins = internalGauge.Coins.Add(coins...) + err = k.setGauge(ctx, internalGauge) + if err != nil { + return err + } + + return nil +} + // AddToGaugeRewards adds coins to gauge. func (k Keeper) AddToGaugeRewards(ctx sdk.Context, owner sdk.AccAddress, coins sdk.Coins, gaugeID uint64) error { gauge, err := k.GetGaugeByID(ctx, gaugeID) @@ -230,6 +353,7 @@ func (k Keeper) AddToGaugeRewards(ctx sdk.Context, owner sdk.AccAddress, coins s if err != nil { return err } + k.hooks.AfterAddToGauge(ctx, gauge.Id) return nil } diff --git a/x/incentives/keeper/gauge_test.go b/x/incentives/keeper/gauge_test.go index 7243dc98122..d2e5dd8c372 100644 --- a/x/incentives/keeper/gauge_test.go +++ b/x/incentives/keeper/gauge_test.go @@ -582,3 +582,187 @@ func (s *KeeperTestSuite) TestCreateGauge_NoLockGauges() { }) } } + +func (s *KeeperTestSuite) TestCreateGroupGauge() { + coinsToAdd := sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000))) + tests := []struct { + name string + coins sdk.Coins + numEpochPaidOver uint64 + internalGaugeIds []uint64 + gaugeType lockuptypes.LockQueryType + splittiingPolicy types.SplittingPolicy + expectErr bool + }{ + { + name: "Happy case: created valid gauge", + coins: coinsToAdd, + numEpochPaidOver: 1, + internalGaugeIds: []uint64{2, 3, 4}, + gaugeType: lockuptypes.ByGroup, + splittiingPolicy: types.Evenly, + expectErr: false, + }, + + { + name: "Error: Invalid InternalGauge Id", + coins: coinsToAdd, + numEpochPaidOver: 1, + internalGaugeIds: []uint64{2, 3, 4, 5}, + gaugeType: lockuptypes.ByGroup, + splittiingPolicy: types.Evenly, + expectErr: true, + }, + { + name: "Error: owner doesnot have enough funds", + coins: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(200_000_000))), + numEpochPaidOver: 1, + internalGaugeIds: []uint64{2, 3, 4}, + gaugeType: lockuptypes.ByGroup, + splittiingPolicy: types.Evenly, + expectErr: true, + }, + { + name: "Error: One of the internal Gauge is non-perp", + coins: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(200_000_000))), + numEpochPaidOver: 1, + internalGaugeIds: []uint64{2, 3, 4, 5}, + gaugeType: lockuptypes.ByGroup, + splittiingPolicy: types.Evenly, + expectErr: true, + }, + { + name: "Error: No InternalGaugeIds provided", + coins: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(200_000_000))), + numEpochPaidOver: 1, + internalGaugeIds: []uint64{}, + splittiingPolicy: types.Evenly, + gaugeType: lockuptypes.ByGroup, + expectErr: true, + }, + { + name: "Error: Invalid Splitting Policy", + coins: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(200_000_000))), + numEpochPaidOver: 1, + internalGaugeIds: []uint64{}, + gaugeType: lockuptypes.ByGroup, + splittiingPolicy: types.Volume, + expectErr: true, + }, + { + name: "Error: Invalid gauge type", + coins: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(200_000_000))), + numEpochPaidOver: 1, + internalGaugeIds: []uint64{}, + gaugeType: lockuptypes.NoLock, + splittiingPolicy: types.Evenly, + expectErr: true, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + s.SetupTest() + s.FundAcc(s.TestAccs[1], sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000)))) // 1,000 osmo + clPool := s.PrepareConcentratedPool() // gaugeid = 1 + + // create 3 perp-internal Gauge + for i := 0; i <= 2; i++ { + s.CreateNoLockExternalGauges(clPool.GetId(), sdk.NewCoins(), s.TestAccs[1], uint64(1)) // gauge id = 2,3,4 + } + + //create 1 non-perp internal Gauge + s.CreateNoLockExternalGauges(clPool.GetId(), sdk.NewCoins(), s.TestAccs[1], uint64(2)) // gauge id = 5 + + groupGaugeId, err := s.App.IncentivesKeeper.CreateGroupGauge(s.Ctx, tc.coins, tc.numEpochPaidOver, s.TestAccs[1], tc.internalGaugeIds, tc.gaugeType, tc.splittiingPolicy) // gauge id = 6 + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + + // check that the gauge has been create with right value + groupGauge, err := s.App.IncentivesKeeper.GetGaugeByID(s.Ctx, groupGaugeId) + s.Require().NoError(err) + + s.Require().Equal(groupGauge.Coins, tc.coins) + s.Require().Equal(groupGauge.NumEpochsPaidOver, tc.numEpochPaidOver) + s.Require().Equal(groupGauge.IsPerpetual, true) + s.Require().Equal(groupGauge.DistributeTo.LockQueryType, lockuptypes.ByGroup) + + // check that GroupGauge has been added to state + groupGaugeObj, err := s.App.IncentivesKeeper.GetGroupGaugeById(s.Ctx, groupGaugeId) + s.Require().NoError(err) + + s.Require().Equal(groupGaugeObj.InternalIds, tc.internalGaugeIds) + } + }) + } +} + +func (s *KeeperTestSuite) TestAddToGaugeRewardsFromGauge() { + coinsToTransfer := sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000))) + tests := []struct { + name string + groupGaugeId uint64 + internalGaugeId uint64 + coinsToTransfer sdk.Coins + expectErr bool + }{ + { + name: "Happy case: Valid gaugeId with valid Internal GaugeId", + groupGaugeId: 3, + internalGaugeId: 2, + coinsToTransfer: coinsToTransfer, + expectErr: false, + }, + { + name: "Error: InternalGauge is not present in groupGauge", + groupGaugeId: 3, + internalGaugeId: 1, + coinsToTransfer: coinsToTransfer, + expectErr: true, + }, + { + name: "Error: Not enough tokens to transfer", + groupGaugeId: 3, + internalGaugeId: 2, + coinsToTransfer: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(200_000_000))), + expectErr: true, + }, + { + name: "Error: GroupGaugeId doesnot exist", + groupGaugeId: 5, + internalGaugeId: 2, + coinsToTransfer: coinsToTransfer, + expectErr: true, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + s.SetupTest() + s.FundAcc(s.TestAccs[1], sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000)))) // 1,000 osmo + clPool := s.PrepareConcentratedPool() // gaugeid = 1 + + // create internal Gauge + internalGauge1 := s.CreateNoLockExternalGauges(clPool.GetId(), sdk.NewCoins(), s.TestAccs[1], uint64(1)) // gauge id = 2 + + // create group gauge + _, err := s.App.IncentivesKeeper.CreateGroupGauge(s.Ctx, sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000))), uint64(1), s.TestAccs[1], []uint64{internalGauge1}, lockuptypes.ByGroup, types.Evenly) // gauge id = 3 + s.Require().NoError(err) + + err = s.App.IncentivesKeeper.AddToGaugeRewardsFromGauge(s.Ctx, tc.groupGaugeId, tc.coinsToTransfer, tc.internalGaugeId) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + + // check that the coins have been transferred + gauge, err := s.App.IncentivesKeeper.GetGaugeByID(s.Ctx, tc.groupGaugeId) + s.Require().NoError(err) + + s.Require().Equal(gauge.Coins, tc.coinsToTransfer) + } + }) + } +} diff --git a/x/incentives/keeper/hooks.go b/x/incentives/keeper/hooks.go index 7454824c295..0ef2bf1aa1a 100644 --- a/x/incentives/keeper/hooks.go +++ b/x/incentives/keeper/hooks.go @@ -18,6 +18,7 @@ func (k Keeper) BeforeEpochStart(ctx sdk.Context, epochIdentifier string, epochN // AfterEpochEnd is the epoch end hook. func (k Keeper) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, epochNumber int64) error { params := k.GetParams(ctx) + if epochIdentifier == params.DistrEpochIdentifier { // begin distribution if it's start time gauges := k.GetUpcomingGauges(ctx) @@ -34,6 +35,11 @@ func (k Keeper) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, epochNumb ctx.EventManager().IncreaseCapacity(2e6) } + err := k.AllocateAcrossGauges(ctx) + if err != nil { + return err + } + // distribute due to epoch event gauges = k.GetActiveGauges(ctx) // only distribute to active gauges that are for native denoms @@ -48,7 +54,7 @@ func (k Keeper) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, epochNumb } ctx.Logger().Info("AfterEpochEnd: distributing to gauges", "module", types.ModuleName, "numGauges", len(distrGauges), "height", ctx.BlockHeight()) - _, err := k.Distribute(ctx, distrGauges) + _, err = k.Distribute(ctx, distrGauges) if err != nil { return err } diff --git a/x/incentives/keeper/keeper_test.go b/x/incentives/keeper/keeper_test.go index 90fe6b6d12e..4d17ebcae9c 100644 --- a/x/incentives/keeper/keeper_test.go +++ b/x/incentives/keeper/keeper_test.go @@ -32,14 +32,40 @@ func TestKeeperTestSuite(t *testing.T) { suite.Run(t, new(KeeperTestSuite)) } +func (s *KeeperTestSuite) SetupGroupGauge(clPoolId uint64, lockOwner sdk.AccAddress, numOfNoLockGauges uint64, numOfLockGauges uint64) []uint64 { + internalGauges := s.setupNoLockInternalGauge(clPoolId, numOfNoLockGauges) + + for i := uint64(1); i <= numOfLockGauges; i++ { + // setup lock + s.LockTokens(lockOwner, sdk.Coins{sdk.NewInt64Coin("lptoken", 10)}, time.Hour*7) + + // create gauge + gaugeID, _, _, _ := s.SetupNewGauge(true, sdk.NewCoins()) + internalGauges = append(internalGauges, gaugeID) + } + + return internalGauges +} + +// setupNoLockInternalGauge create no lock perp internal gauges. +func (s *KeeperTestSuite) setupNoLockInternalGauge(poolId uint64, numberOfExistingGauges uint64) []uint64 { + var internalGauges []uint64 + for i := uint64(1); i <= numberOfExistingGauges; i++ { + internalGauge := s.CreateNoLockExternalGauges(poolId, sdk.NewCoins(), s.TestAccs[1], uint64(1)) + internalGauges = append(internalGauges, internalGauge) + } + + return internalGauges +} + // ValidateDistributedGauge checks that the gauge is updated as expected after distribution func (s *KeeperTestSuite) ValidateDistributedGauge(gaugeID uint64, expectedFilledEpoch uint64, expectedDistributions sdk.Coins) { // Check that filled epcohs is not updated gauge, err := s.App.IncentivesKeeper.GetGaugeByID(s.Ctx, gaugeID) s.Require().NoError(err) s.Require().Equal(expectedFilledEpoch, gauge.FilledEpochs) - // Check that distributed coins is not updated + s.Require().Equal(expectedDistributions, gauge.DistributedCoins) } @@ -48,9 +74,13 @@ func (s *KeeperTestSuite) ValidateNotDistributedGauge(gaugeID uint64) { s.ValidateDistributedGauge(gaugeID, 0, sdk.Coins(nil)) } -func (s *KeeperTestSuite) ValidateIncentiveRecord(poolId uint64, remainingCoin sdk.DecCoin, emissionRate sdk.Dec, incentiveRecord cltypes.IncentiveRecord) { +func (s *KeeperTestSuite) ValidateIncentiveRecord(poolId uint64, remainingCoin sdk.Coin, incentiveRecord cltypes.IncentiveRecord) { + epochInfo := s.App.IncentivesKeeper.GetEpochInfo(s.Ctx) + distributedDecCoin := sdk.NewDecCoinFromCoin(remainingCoin) + emissionRateForPoolClPool := distributedDecCoin.Amount.QuoTruncate(sdk.NewDec(epochInfo.Duration.Milliseconds()).QuoInt(sdk.NewInt(1000))) + s.Require().Equal(poolId, incentiveRecord.PoolId) - s.Require().Equal(emissionRate, incentiveRecord.GetIncentiveRecordBody().EmissionRate) + s.Require().Equal(emissionRateForPoolClPool, incentiveRecord.GetIncentiveRecordBody().EmissionRate) s.Require().Equal(types.DefaultConcentratedUptime, incentiveRecord.MinUptime) - s.Require().Equal(remainingCoin, incentiveRecord.GetIncentiveRecordBody().RemainingCoin) + s.Require().Equal(distributedDecCoin, incentiveRecord.GetIncentiveRecordBody().RemainingCoin) } diff --git a/x/incentives/keeper/store.go b/x/incentives/keeper/store.go index f789bbc1741..e458f6e00c5 100644 --- a/x/incentives/keeper/store.go +++ b/x/incentives/keeper/store.go @@ -2,8 +2,12 @@ package keeper import ( "encoding/json" + "errors" "fmt" + "github.com/gogo/protobuf/proto" + + "github.com/osmosis-labs/osmosis/osmoutils" "github.com/osmosis-labs/osmosis/v17/x/incentives/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -101,3 +105,41 @@ func (k Keeper) deleteGaugeIDForDenom(ctx sdk.Context, ID uint64, denom string) func (k Keeper) addGaugeIDForDenom(ctx sdk.Context, ID uint64, denom string) error { return k.addGaugeRefByKey(ctx, gaugeDenomStoreKey(denom), ID) } + +// SetGroupGauge sets groupGroup for a specific key. +// TODO: explore if we can store this better, this has GroupGaugeId in key and value +func (k Keeper) SetGroupGauge(ctx sdk.Context, groupGauge types.GroupGauge) { + store := ctx.KVStore(k.storeKey) + osmoutils.MustSet(store, types.KeyGroupGaugeForId(groupGauge.GroupGaugeId), &groupGauge) +} + +// GetAllGroupGauges gets all the groupGauges that is in state. +func (k Keeper) GetAllGroupGauges(ctx sdk.Context) ([]types.GroupGauge, error) { + return osmoutils.GatherValuesFromStorePrefix(ctx.KVStore(k.storeKey), types.KeyPrefix(types.GroupGaugePrefix), k.ParseGroupGaugeFromBz) +} + +func (k Keeper) ParseGroupGaugeFromBz(bz []byte) (groupGauge types.GroupGauge, err error) { + if len(bz) == 0 { + return types.GroupGauge{}, errors.New("group gauge not found") + } + err = proto.Unmarshal(bz, &groupGauge) + + return groupGauge, err +} + +// GetGroupGaugeById gets groupGauge struct for a given groupGaugeId. +func (k Keeper) GetGroupGaugeById(ctx sdk.Context, groupGaugeId uint64) (types.GroupGauge, error) { + store := ctx.KVStore(k.storeKey) + key := types.KeyGroupGaugeForId(groupGaugeId) + bz := store.Get(key) + if bz == nil { + return types.GroupGauge{}, nil + } + + var getGroupGauge types.GroupGauge + if err := proto.Unmarshal(bz, &getGroupGauge); err != nil { + return types.GroupGauge{}, nil + } + + return getGroupGauge, nil +} diff --git a/x/incentives/keeper/store_test.go b/x/incentives/keeper/store_test.go index be630ae0a73..cde6c4b82d6 100644 --- a/x/incentives/keeper/store_test.go +++ b/x/incentives/keeper/store_test.go @@ -1,6 +1,12 @@ package keeper_test -import "github.com/stretchr/testify/suite" +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + + "github.com/osmosis-labs/osmosis/v17/x/incentives/types" + lockuptypes "github.com/osmosis-labs/osmosis/v17/x/lockup/types" +) var _ = suite.TestingSuite(nil) @@ -33,3 +39,46 @@ func (s *KeeperTestSuite) TestGaugeReferencesManagement() { gaugeRefs3 := s.App.IncentivesKeeper.GetGaugeRefs(s.Ctx, key2) s.Require().Equal(len(gaugeRefs3), 2) } + +func (s *KeeperTestSuite) TestGetGroupGaugeById() { + tests := map[string]struct { + groupGaugeId uint64 + expectedRecord types.GroupGauge + }{ + "Valid record": { + groupGaugeId: uint64(5), + expectedRecord: types.GroupGauge{ + GroupGaugeId: uint64(5), + InternalIds: []uint64{2, 3, 4}, + SplittingPolicy: types.Evenly, + }, + }, + + "InValid record": { + groupGaugeId: uint64(6), + expectedRecord: types.GroupGauge{}, + }, + } + + for name, test := range tests { + s.Run(name, func() { + s.FundAcc(s.TestAccs[1], sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000)))) // 1,000 osmo + clPool := s.PrepareConcentratedPool() // gaugeid = 1 + + // create 3 internal Gauge + var internalGauges []uint64 + for i := 0; i <= 2; i++ { + internalGauge := s.CreateNoLockExternalGauges(clPool.GetId(), sdk.NewCoins(), s.TestAccs[1], uint64(1)) // gauge id = 2,3,4 + internalGauges = append(internalGauges, internalGauge) + } + + _, err := s.App.IncentivesKeeper.CreateGroupGauge(s.Ctx, sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000))), 1, s.TestAccs[1], internalGauges, lockuptypes.ByGroup, types.Evenly) // gauge id = 5 + s.Require().NoError(err) + + record, err := s.App.IncentivesKeeper.GetGroupGaugeById(s.Ctx, test.groupGaugeId) + s.Require().NoError(err) + + s.Require().Equal(test.expectedRecord, record) + }) + } +} diff --git a/x/incentives/types/gauge.pb.go b/x/incentives/types/gauge.pb.go index c99d323c235..57f9ee718a2 100644 --- a/x/incentives/types/gauge.pb.go +++ b/x/incentives/types/gauge.pb.go @@ -30,6 +30,32 @@ var _ = time.Kitchen // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// SplittingPolicy determines the way we want to split incentives in groupGauges +type SplittingPolicy int32 + +const ( + Volume SplittingPolicy = 0 + Evenly SplittingPolicy = 1 +) + +var SplittingPolicy_name = map[int32]string{ + 0: "Volume", + 1: "Evenly", +} + +var SplittingPolicy_value = map[string]int32{ + "Volume": 0, + "Evenly": 1, +} + +func (x SplittingPolicy) String() string { + return proto.EnumName(SplittingPolicy_name, int32(x)) +} + +func (SplittingPolicy) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_c0304e2bb0159901, []int{0} +} + // Gauge is an object that stores and distributes yields to recipients who // satisfy certain conditions. Currently gauges support conditions around the // duration for which a given denom is locked. @@ -149,6 +175,69 @@ func (m *Gauge) GetDistributedCoins() github_com_cosmos_cosmos_sdk_types.Coins { return nil } +// Gauge is an object that stores GroupGaugeId as well as internalGaugeIds. We +// linked these two together so that we can distribute tokens from groupGauge to +// internalGauges. +type GroupGauge struct { + GroupGaugeId uint64 `protobuf:"varint,1,opt,name=group_gauge_id,json=groupGaugeId,proto3" json:"group_gauge_id,omitempty"` + InternalIds []uint64 `protobuf:"varint,2,rep,packed,name=internal_ids,json=internalIds,proto3" json:"internal_ids,omitempty"` + SplittingPolicy SplittingPolicy `protobuf:"varint,3,opt,name=splitting_policy,json=splittingPolicy,proto3,enum=osmosis.incentives.SplittingPolicy" json:"splitting_policy,omitempty"` +} + +func (m *GroupGauge) Reset() { *m = GroupGauge{} } +func (m *GroupGauge) String() string { return proto.CompactTextString(m) } +func (*GroupGauge) ProtoMessage() {} +func (*GroupGauge) Descriptor() ([]byte, []int) { + return fileDescriptor_c0304e2bb0159901, []int{1} +} +func (m *GroupGauge) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GroupGauge) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GroupGauge.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GroupGauge) XXX_Merge(src proto.Message) { + xxx_messageInfo_GroupGauge.Merge(m, src) +} +func (m *GroupGauge) XXX_Size() int { + return m.Size() +} +func (m *GroupGauge) XXX_DiscardUnknown() { + xxx_messageInfo_GroupGauge.DiscardUnknown(m) +} + +var xxx_messageInfo_GroupGauge proto.InternalMessageInfo + +func (m *GroupGauge) GetGroupGaugeId() uint64 { + if m != nil { + return m.GroupGaugeId + } + return 0 +} + +func (m *GroupGauge) GetInternalIds() []uint64 { + if m != nil { + return m.InternalIds + } + return nil +} + +func (m *GroupGauge) GetSplittingPolicy() SplittingPolicy { + if m != nil { + return m.SplittingPolicy + } + return Volume +} + type LockableDurationsInfo struct { // List of incentivised durations that gauges will pay out to LockableDurations []time.Duration `protobuf:"bytes,1,rep,name=lockable_durations,json=lockableDurations,proto3,stdduration" json:"lockable_durations" yaml:"lockable_durations"` @@ -158,7 +247,7 @@ func (m *LockableDurationsInfo) Reset() { *m = LockableDurationsInfo{} } func (m *LockableDurationsInfo) String() string { return proto.CompactTextString(m) } func (*LockableDurationsInfo) ProtoMessage() {} func (*LockableDurationsInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_c0304e2bb0159901, []int{1} + return fileDescriptor_c0304e2bb0159901, []int{2} } func (m *LockableDurationsInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -195,49 +284,58 @@ func (m *LockableDurationsInfo) GetLockableDurations() []time.Duration { } func init() { + proto.RegisterEnum("osmosis.incentives.SplittingPolicy", SplittingPolicy_name, SplittingPolicy_value) proto.RegisterType((*Gauge)(nil), "osmosis.incentives.Gauge") + proto.RegisterType((*GroupGauge)(nil), "osmosis.incentives.GroupGauge") proto.RegisterType((*LockableDurationsInfo)(nil), "osmosis.incentives.LockableDurationsInfo") } func init() { proto.RegisterFile("osmosis/incentives/gauge.proto", fileDescriptor_c0304e2bb0159901) } var fileDescriptor_c0304e2bb0159901 = []byte{ - // 546 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x93, 0xcf, 0x6e, 0xd3, 0x30, - 0x1c, 0xc7, 0x9b, 0xad, 0x1d, 0xc3, 0xed, 0x10, 0xb5, 0x86, 0x94, 0x56, 0x22, 0x2d, 0x45, 0x48, - 0xbd, 0xcc, 0xa6, 0x43, 0x02, 0x89, 0x63, 0x07, 0x42, 0x93, 0x90, 0x28, 0xd5, 0x0e, 0x88, 0x4b, - 0xe4, 0x24, 0x6e, 0x66, 0x35, 0xc9, 0x2f, 0x8a, 0x9d, 0x6a, 0x7d, 0x03, 0x8e, 0x13, 0x27, 0x9e, - 0x81, 0x27, 0xd9, 0x71, 0x47, 0x4e, 0x1b, 0x6a, 0xdf, 0x80, 0x27, 0x40, 0xb1, 0x13, 0xb5, 0x2a, - 0x57, 0x4e, 0x8e, 0x7f, 0xdf, 0xdf, 0xbf, 0xef, 0x47, 0x0e, 0x72, 0x40, 0xc6, 0x20, 0x85, 0xa4, - 0x22, 0xf1, 0x79, 0xa2, 0xc4, 0x82, 0x4b, 0x1a, 0xb2, 0x3c, 0xe4, 0x24, 0xcd, 0x40, 0x01, 0xc6, - 0xa5, 0x4e, 0x36, 0x7a, 0xf7, 0x38, 0x84, 0x10, 0xb4, 0x4c, 0x8b, 0x2f, 0x93, 0xd9, 0x75, 0x42, - 0x80, 0x30, 0xe2, 0x54, 0xdf, 0xbc, 0x7c, 0x46, 0x83, 0x3c, 0x63, 0x4a, 0x40, 0x52, 0xea, 0xbd, - 0x5d, 0x5d, 0x89, 0x98, 0x4b, 0xc5, 0xe2, 0xb4, 0x6a, 0xe0, 0xeb, 0x59, 0xd4, 0x63, 0x92, 0xd3, - 0xc5, 0xc8, 0xe3, 0x8a, 0x8d, 0xa8, 0x0f, 0xa2, 0x6a, 0xd0, 0xa9, 0x56, 0x8d, 0xc0, 0x9f, 0xe7, - 0xa9, 0x3e, 0x8c, 0x34, 0xf8, 0x5e, 0x47, 0x8d, 0x0f, 0xc5, 0xd6, 0xf8, 0x11, 0xda, 0x13, 0x81, - 0x6d, 0xf5, 0xad, 0x61, 0x7d, 0xba, 0x27, 0x02, 0xfc, 0x0c, 0xb5, 0x84, 0x74, 0x53, 0x9e, 0xa5, - 0x5c, 0xe5, 0x2c, 0xb2, 0xf7, 0xfa, 0xd6, 0xf0, 0x70, 0xda, 0x14, 0x72, 0x52, 0x85, 0xf0, 0x39, - 0x3a, 0x0a, 0x84, 0x54, 0x99, 0xf0, 0x72, 0xc5, 0x5d, 0x05, 0xf6, 0x7e, 0xdf, 0x1a, 0x36, 0x4f, - 0x1d, 0x52, 0x59, 0x37, 0xf3, 0xc8, 0xe7, 0x9c, 0x67, 0xcb, 0x33, 0x48, 0x02, 0x51, 0xb8, 0x1a, - 0xd7, 0x6f, 0xee, 0x7a, 0xb5, 0x69, 0x6b, 0x53, 0x7a, 0x01, 0x98, 0xa1, 0x46, 0xb1, 0xb0, 0xb4, - 0xeb, 0xfd, 0xfd, 0x61, 0xf3, 0xb4, 0x43, 0x8c, 0x25, 0x52, 0x58, 0x22, 0xa5, 0x25, 0x72, 0x06, - 0x22, 0x19, 0xbf, 0x2c, 0xaa, 0x7f, 0xde, 0xf7, 0x86, 0xa1, 0x50, 0x97, 0xb9, 0x47, 0x7c, 0x88, - 0x69, 0xe9, 0xdf, 0x1c, 0x27, 0x32, 0x98, 0x53, 0xb5, 0x4c, 0xb9, 0xd4, 0x05, 0x72, 0x6a, 0x3a, - 0xe3, 0x2f, 0x08, 0x49, 0xc5, 0x32, 0xe5, 0x16, 0xf8, 0xec, 0x86, 0x5e, 0xb5, 0x4b, 0x0c, 0x5b, - 0x52, 0xb1, 0x25, 0x17, 0x15, 0xdb, 0xf1, 0xd3, 0x62, 0xd0, 0x9f, 0xbb, 0x5e, 0x7b, 0xc9, 0xe2, - 0xe8, 0xed, 0x60, 0x53, 0x3b, 0xb8, 0xbe, 0xef, 0x59, 0xd3, 0x87, 0x3a, 0x50, 0xa4, 0x63, 0x8a, - 0x8e, 0x93, 0x3c, 0x76, 0x79, 0x0a, 0xfe, 0xa5, 0x74, 0x53, 0x26, 0x02, 0x17, 0x16, 0x3c, 0xb3, - 0x0f, 0x34, 0xcc, 0x76, 0x92, 0xc7, 0xef, 0xb5, 0x34, 0x61, 0x22, 0xf8, 0xb4, 0xe0, 0x19, 0x7e, - 0x8e, 0x8e, 0x66, 0x22, 0x8a, 0x78, 0x50, 0xd6, 0xd8, 0x0f, 0x74, 0x66, 0xcb, 0x04, 0x4d, 0x32, - 0xbe, 0x42, 0xed, 0x0d, 0xa2, 0xc0, 0x35, 0x78, 0x0e, 0xff, 0x3f, 0x9e, 0xc7, 0x5b, 0x53, 0x74, - 0x64, 0xf0, 0xcd, 0x42, 0x4f, 0x3e, 0x82, 0x3f, 0x67, 0x5e, 0xc4, 0xdf, 0x95, 0x6f, 0x51, 0x9e, - 0x27, 0x33, 0xc0, 0x80, 0x70, 0x54, 0x0a, 0x6e, 0xf5, 0x4a, 0xa5, 0x6d, 0x95, 0x4b, 0xed, 0xb2, - 0xac, 0x6a, 0xc7, 0x2f, 0x4a, 0x94, 0x1d, 0x83, 0xf2, 0xdf, 0x16, 0x83, 0x1f, 0x05, 0xd2, 0x76, - 0xb4, 0x3b, 0x74, 0x3c, 0xb9, 0x59, 0x39, 0xd6, 0xed, 0xca, 0xb1, 0x7e, 0xaf, 0x1c, 0xeb, 0x7a, - 0xed, 0xd4, 0x6e, 0xd7, 0x4e, 0xed, 0xd7, 0xda, 0xa9, 0x7d, 0x7d, 0xbd, 0x65, 0xb0, 0x7c, 0x6f, - 0x27, 0x11, 0xf3, 0x64, 0x75, 0xa1, 0x8b, 0xd1, 0x1b, 0x7a, 0xb5, 0xfd, 0x77, 0x6a, 0xd3, 0xde, - 0x81, 0x5e, 0xef, 0xd5, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2c, 0xc2, 0x67, 0xb9, 0xc0, 0x03, - 0x00, 0x00, + // 662 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x4f, 0x4f, 0xdb, 0x3e, + 0x18, 0x6e, 0x4a, 0xe1, 0xc7, 0xcf, 0x2d, 0xd0, 0x5a, 0x4c, 0x0a, 0x95, 0x96, 0x76, 0x65, 0x93, + 0xaa, 0x49, 0xd8, 0x83, 0x49, 0x9b, 0xb4, 0x63, 0x19, 0x42, 0x95, 0xa6, 0xad, 0xeb, 0xd0, 0x34, + 0xed, 0x12, 0x39, 0x89, 0x09, 0x16, 0x4e, 0x1c, 0xc5, 0x4e, 0x45, 0xbf, 0x01, 0x47, 0xb4, 0xd3, + 0xee, 0xd3, 0x2e, 0xfb, 0x24, 0x1c, 0x39, 0xee, 0x04, 0x13, 0x7c, 0x83, 0x7d, 0x82, 0x29, 0x76, + 0x42, 0x59, 0xb9, 0xee, 0x14, 0xfb, 0x7d, 0xde, 0x7f, 0xcf, 0xf3, 0xbe, 0x0e, 0x70, 0x84, 0x8c, + 0x84, 0x64, 0x12, 0xb3, 0xd8, 0xa7, 0xb1, 0x62, 0x13, 0x2a, 0x71, 0x48, 0xb2, 0x90, 0xa2, 0x24, + 0x15, 0x4a, 0x40, 0x58, 0xe0, 0x68, 0x86, 0xb7, 0xd7, 0x43, 0x11, 0x0a, 0x0d, 0xe3, 0xfc, 0x64, + 0x3c, 0xdb, 0x4e, 0x28, 0x44, 0xc8, 0x29, 0xd6, 0x37, 0x2f, 0x3b, 0xc4, 0x41, 0x96, 0x12, 0xc5, + 0x44, 0x5c, 0xe0, 0x9d, 0x79, 0x5c, 0xb1, 0x88, 0x4a, 0x45, 0xa2, 0xa4, 0x4c, 0xe0, 0xeb, 0x5a, + 0xd8, 0x23, 0x92, 0xe2, 0xc9, 0xb6, 0x47, 0x15, 0xd9, 0xc6, 0xbe, 0x60, 0x65, 0x82, 0x8d, 0xb2, + 0x55, 0x2e, 0xfc, 0xe3, 0x2c, 0xd1, 0x1f, 0x03, 0xf5, 0xbe, 0xd4, 0xc0, 0xe2, 0x7e, 0xde, 0x35, + 0x5c, 0x05, 0x55, 0x16, 0xd8, 0x56, 0xd7, 0xea, 0xd7, 0xc6, 0x55, 0x16, 0xc0, 0x47, 0xa0, 0xc1, + 0xa4, 0x9b, 0xd0, 0x34, 0xa1, 0x2a, 0x23, 0xdc, 0xae, 0x76, 0xad, 0xfe, 0xf2, 0xb8, 0xce, 0xe4, + 0xa8, 0x34, 0xc1, 0x21, 0x58, 0x09, 0x98, 0x54, 0x29, 0xf3, 0x32, 0x45, 0x5d, 0x25, 0xec, 0x85, + 0xae, 0xd5, 0xaf, 0xef, 0x38, 0xa8, 0xa4, 0x6e, 0xea, 0xa1, 0xf7, 0x19, 0x4d, 0xa7, 0xbb, 0x22, + 0x0e, 0x58, 0xce, 0x6a, 0x50, 0x3b, 0xbf, 0xec, 0x54, 0xc6, 0x8d, 0x59, 0xe8, 0x81, 0x80, 0x04, + 0x2c, 0xe6, 0x0d, 0x4b, 0xbb, 0xd6, 0x5d, 0xe8, 0xd7, 0x77, 0x36, 0x90, 0xa1, 0x84, 0x72, 0x4a, + 0xa8, 0xa0, 0x84, 0x76, 0x05, 0x8b, 0x07, 0xcf, 0xf2, 0xe8, 0x1f, 0x57, 0x9d, 0x7e, 0xc8, 0xd4, + 0x51, 0xe6, 0x21, 0x5f, 0x44, 0xb8, 0xe0, 0x6f, 0x3e, 0x5b, 0x32, 0x38, 0xc6, 0x6a, 0x9a, 0x50, + 0xa9, 0x03, 0xe4, 0xd8, 0x64, 0x86, 0x9f, 0x00, 0x90, 0x8a, 0xa4, 0xca, 0xcd, 0xe5, 0xb3, 0x17, + 0x75, 0xab, 0x6d, 0x64, 0xb4, 0x45, 0xa5, 0xb6, 0xe8, 0xa0, 0xd4, 0x76, 0xf0, 0x30, 0x2f, 0xf4, + 0xfb, 0xb2, 0xd3, 0x9a, 0x92, 0x88, 0xbf, 0xea, 0xcd, 0x62, 0x7b, 0x67, 0x57, 0x1d, 0x6b, 0xfc, + 0xbf, 0x36, 0xe4, 0xee, 0x10, 0x83, 0xf5, 0x38, 0x8b, 0x5c, 0x9a, 0x08, 0xff, 0x48, 0xba, 0x09, + 0x61, 0x81, 0x2b, 0x26, 0x34, 0xb5, 0x97, 0xb4, 0x98, 0xad, 0x38, 0x8b, 0xf6, 0x34, 0x34, 0x22, + 0x2c, 0x78, 0x37, 0xa1, 0x29, 0xdc, 0x04, 0x2b, 0x87, 0x8c, 0x73, 0x1a, 0x14, 0x31, 0xf6, 0x7f, + 0xda, 0xb3, 0x61, 0x8c, 0xc6, 0x19, 0x9e, 0x80, 0xd6, 0x4c, 0xa2, 0xc0, 0x35, 0xf2, 0x2c, 0xff, + 0x7b, 0x79, 0x9a, 0x77, 0xaa, 0x68, 0x4b, 0xef, 0xbb, 0x05, 0xc0, 0x7e, 0x2a, 0xb2, 0xc4, 0x6c, + 0xc6, 0x63, 0xb0, 0x1a, 0xe6, 0x37, 0x57, 0xaf, 0xb7, 0x7b, 0xbb, 0x25, 0x8d, 0xf0, 0xd6, 0x67, + 0x68, 0xf6, 0x25, 0x56, 0x34, 0x8d, 0x09, 0x77, 0x59, 0x20, 0xed, 0x6a, 0x77, 0xa1, 0x5f, 0x1b, + 0xd7, 0x4b, 0xdb, 0x30, 0x90, 0xf0, 0x2d, 0x68, 0xca, 0x84, 0x33, 0xa5, 0x58, 0x1c, 0xba, 0x89, + 0xe0, 0xcc, 0x9f, 0xea, 0x95, 0x59, 0xdd, 0xd9, 0x44, 0xf7, 0x5f, 0x0b, 0xfa, 0x50, 0xfa, 0x8e, + 0xb4, 0xeb, 0x78, 0x4d, 0xfe, 0x6d, 0xe8, 0x9d, 0x5a, 0xe0, 0xc1, 0x1b, 0xe1, 0x1f, 0x13, 0x8f, + 0xd3, 0xd7, 0xc5, 0x9b, 0x91, 0xc3, 0xf8, 0x50, 0x40, 0x01, 0x20, 0x2f, 0x00, 0xb7, 0x7c, 0x4d, + 0xd2, 0xb6, 0x0a, 0xf1, 0xe6, 0x67, 0x5e, 0xc6, 0x0e, 0x9e, 0x14, 0x23, 0xdf, 0x30, 0x23, 0xbf, + 0x9f, 0xa2, 0xf7, 0x35, 0x1f, 0x7d, 0x8b, 0xcf, 0x17, 0x7d, 0x8a, 0xc1, 0xda, 0x5c, 0xbb, 0x10, + 0x80, 0xa5, 0x8f, 0x82, 0x67, 0x11, 0x6d, 0x56, 0xf2, 0xf3, 0xde, 0x84, 0xc6, 0x7c, 0xda, 0xb4, + 0xda, 0xb5, 0xd3, 0x6f, 0x4e, 0x65, 0x30, 0x3a, 0xbf, 0x76, 0xac, 0x8b, 0x6b, 0xc7, 0xfa, 0x75, + 0xed, 0x58, 0x67, 0x37, 0x4e, 0xe5, 0xe2, 0xc6, 0xa9, 0xfc, 0xbc, 0x71, 0x2a, 0x9f, 0x5f, 0xdc, + 0x99, 0x5c, 0xa1, 0xca, 0x16, 0x27, 0x9e, 0x2c, 0x2f, 0x78, 0xb2, 0xfd, 0x12, 0x9f, 0xdc, 0xfd, + 0xed, 0xe8, 0x69, 0x7a, 0x4b, 0x9a, 0xcf, 0xf3, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x40, + 0x27, 0xeb, 0x99, 0x04, 0x00, 0x00, } func (m *Gauge) Marshal() (dAtA []byte, err error) { @@ -334,6 +432,57 @@ func (m *Gauge) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *GroupGauge) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GroupGauge) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GroupGauge) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SplittingPolicy != 0 { + i = encodeVarintGauge(dAtA, i, uint64(m.SplittingPolicy)) + i-- + dAtA[i] = 0x18 + } + if len(m.InternalIds) > 0 { + dAtA4 := make([]byte, len(m.InternalIds)*10) + var j3 int + for _, num := range m.InternalIds { + for num >= 1<<7 { + dAtA4[j3] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j3++ + } + dAtA4[j3] = uint8(num) + j3++ + } + i -= j3 + copy(dAtA[i:], dAtA4[:j3]) + i = encodeVarintGauge(dAtA, i, uint64(j3)) + i-- + dAtA[i] = 0x12 + } + if m.GroupGaugeId != 0 { + i = encodeVarintGauge(dAtA, i, uint64(m.GroupGaugeId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func (m *LockableDurationsInfo) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -417,6 +566,28 @@ func (m *Gauge) Size() (n int) { return n } +func (m *GroupGauge) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.GroupGaugeId != 0 { + n += 1 + sovGauge(uint64(m.GroupGaugeId)) + } + if len(m.InternalIds) > 0 { + l = 0 + for _, e := range m.InternalIds { + l += sovGauge(uint64(e)) + } + n += 1 + sovGauge(uint64(l)) + l + } + if m.SplittingPolicy != 0 { + n += 1 + sovGauge(uint64(m.SplittingPolicy)) + } + return n +} + func (m *LockableDurationsInfo) Size() (n int) { if m == nil { return 0 @@ -699,6 +870,170 @@ func (m *Gauge) Unmarshal(dAtA []byte) error { } return nil } +func (m *GroupGauge) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGauge + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GroupGauge: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GroupGauge: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GroupGaugeId", wireType) + } + m.GroupGaugeId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGauge + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GroupGaugeId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGauge + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.InternalIds = append(m.InternalIds, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGauge + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthGauge + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthGauge + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.InternalIds) == 0 { + m.InternalIds = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGauge + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.InternalIds = append(m.InternalIds, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field InternalIds", wireType) + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SplittingPolicy", wireType) + } + m.SplittingPolicy = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGauge + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SplittingPolicy |= SplittingPolicy(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipGauge(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGauge + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *LockableDurationsInfo) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/incentives/types/keys.go b/x/incentives/types/keys.go index c3bbf64d5e9..cfc1e41a279 100644 --- a/x/incentives/types/keys.go +++ b/x/incentives/types/keys.go @@ -50,6 +50,8 @@ var ( NoLockInternalPrefix = "no-lock/i/" NoLockExternalPrefix = "no-lock/e/" + + GroupGaugePrefix = "group_gauge" ) func KeyPrefix(p string) []byte { @@ -65,3 +67,8 @@ func NoLockExternalGaugeDenom(poolId uint64) string { func NoLockInternalGaugeDenom(poolId uint64) string { return fmt.Sprintf("%s%d", NoLockInternalPrefix, poolId) } + +// KeyGroupGaugeForId returns key for a given groupGaugeId. +func KeyGroupGaugeForId(groupGaugeId uint64) []byte { + return []byte(fmt.Sprintf("%s%s%d%s", GroupGaugePrefix, "|", groupGaugeId, "|")) +} diff --git a/x/lockup/keeper/lock_test.go b/x/lockup/keeper/lock_test.go index c161cddc3c2..ab74a88aa51 100644 --- a/x/lockup/keeper/lock_test.go +++ b/x/lockup/keeper/lock_test.go @@ -1306,7 +1306,7 @@ func (s *KeeperTestSuite) TestSlashTokensFromLockByIDSendUnderlyingAndBurn() { // Create a cl pool and a locked full range position clPool := s.PrepareConcentratedPool() clPoolId := clPool.GetId() - positionID, _, _, liquidity, concentratedLockId, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPoolId, addr, tc.positionCoins, time.Hour) + positionData, concentratedLockId, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPoolId, addr, tc.positionCoins, time.Hour) s.Require().NoError(err) // Refetch the cl pool post full range position creation @@ -1317,7 +1317,7 @@ func (s *KeeperTestSuite) TestSlashTokensFromLockByIDSendUnderlyingAndBurn() { // Check the supply of the cl asset before we slash it is equal to the liquidity created clAssetSupplyPreSlash := s.App.BankKeeper.GetSupply(s.Ctx, clPoolPositionDenom) - s.Require().Equal(liquidity.TruncateInt().String(), clAssetSupplyPreSlash.Amount.String()) + s.Require().Equal(positionData.Liquidity.TruncateInt().String(), clAssetSupplyPreSlash.Amount.String()) // Store the cl pool balance before the slash clPoolBalancePreSlash := s.App.BankKeeper.GetAllBalances(s.Ctx, clPool.GetAddress()) @@ -1327,15 +1327,15 @@ func (s *KeeperTestSuite) TestSlashTokensFromLockByIDSendUnderlyingAndBurn() { Denom: clPoolPositionDenom, Duration: time.Second, }) - s.Require().Equal(liquidity.TruncateInt64(), acc.Int64()) + s.Require().Equal(positionData.Liquidity.TruncateInt64(), acc.Int64()) // The lockup module account balance before the slash should match the liquidity added to the lock lockupModuleBalancePreSlash := s.App.LockupKeeper.GetModuleBalance(s.Ctx) - s.Require().Equal(sdk.NewCoins(sdk.NewCoin(clPoolPositionDenom, liquidity.TruncateInt())), lockupModuleBalancePreSlash) + s.Require().Equal(sdk.NewCoins(sdk.NewCoin(clPoolPositionDenom, positionData.Liquidity.TruncateInt())), lockupModuleBalancePreSlash) // Slash the cl shares and the underlying assets // Figure out the underlying assets from the liquidity slash - position, err := s.App.ConcentratedLiquidityKeeper.GetPosition(s.Ctx, positionID) + position, err := s.App.ConcentratedLiquidityKeeper.GetPosition(s.Ctx, positionData.ID) s.Require().NoError(err) concentratedPool, err := s.App.ConcentratedLiquidityKeeper.GetConcentratedPoolById(s.Ctx, position.PoolId) diff --git a/x/lockup/types/lock.pb.go b/x/lockup/types/lock.pb.go index a6dd473677d..81ec7791b87 100644 --- a/x/lockup/types/lock.pb.go +++ b/x/lockup/types/lock.pb.go @@ -37,18 +37,21 @@ const ( ByDuration LockQueryType = 0 ByTime LockQueryType = 1 NoLock LockQueryType = 2 + ByGroup LockQueryType = 3 ) var LockQueryType_name = map[int32]string{ 0: "ByDuration", 1: "ByTime", 2: "NoLock", + 3: "ByGroup", } var LockQueryType_value = map[string]int32{ "ByDuration": 0, "ByTime": 1, "NoLock": 2, + "ByGroup": 3, } func (x LockQueryType) String() string { @@ -331,48 +334,49 @@ func init() { func init() { proto.RegisterFile("osmosis/lockup/lock.proto", fileDescriptor_7e9d7527a237b489) } var fileDescriptor_7e9d7527a237b489 = []byte{ - // 649 bytes of a gzipped FileDescriptorProto + // 659 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x54, 0x3f, 0x6f, 0xd3, 0x40, - 0x1c, 0x8d, 0xf3, 0xa7, 0xb4, 0x57, 0x9a, 0x46, 0xa7, 0x22, 0xd2, 0x00, 0x76, 0xe4, 0x01, 0x45, - 0xa8, 0xb5, 0x49, 0x19, 0x90, 0x90, 0x18, 0x70, 0xc3, 0x50, 0xa9, 0x42, 0x60, 0x2a, 0x86, 0x2e, - 0x96, 0xed, 0x3b, 0xdc, 0x53, 0x6d, 0x9f, 0xf1, 0xd9, 0x2d, 0xfe, 0x06, 0x8c, 0x1d, 0x41, 0x62, - 0x63, 0xe3, 0x5b, 0xb0, 0x75, 0xec, 0xc8, 0x94, 0xa2, 0x76, 0x63, 0xec, 0x27, 0x40, 0x77, 0x67, - 0x27, 0x69, 0x51, 0xa5, 0x0e, 0x30, 0xd9, 0x77, 0xef, 0xf7, 0x7b, 0xf7, 0xf3, 0x7b, 0xef, 0x0c, - 0x56, 0x29, 0x8b, 0x28, 0x23, 0xcc, 0x0c, 0xa9, 0xbf, 0x9f, 0x27, 0xe2, 0x61, 0x24, 0x29, 0xcd, - 0x28, 0x6c, 0x97, 0x90, 0x21, 0xa1, 0xde, 0x4a, 0x40, 0x03, 0x2a, 0x20, 0x93, 0xbf, 0xc9, 0xaa, - 0x9e, 0x1a, 0x50, 0x1a, 0x84, 0xd8, 0x14, 0x2b, 0x2f, 0x7f, 0x6f, 0xa2, 0x3c, 0x75, 0x33, 0x42, - 0xe3, 0x12, 0xd7, 0xae, 0xe2, 0x19, 0x89, 0x30, 0xcb, 0xdc, 0x28, 0xa9, 0x08, 0x7c, 0x71, 0x8e, - 0xe9, 0xb9, 0x0c, 0x9b, 0x07, 0x43, 0x0f, 0x67, 0xee, 0xd0, 0xf4, 0x29, 0x29, 0x09, 0xf4, 0x1f, - 0x0d, 0x00, 0x5e, 0xe3, 0x94, 0x50, 0xb4, 0x4d, 0xfd, 0x7d, 0xd8, 0x06, 0xf5, 0xad, 0x51, 0x57, - 0xe9, 0x2b, 0x83, 0xa6, 0x5d, 0xdf, 0x1a, 0xc1, 0x87, 0xa0, 0x45, 0x0f, 0x63, 0x9c, 0x76, 0xeb, - 0x7d, 0x65, 0xb0, 0x60, 0x75, 0x2e, 0xc6, 0xda, 0xed, 0xc2, 0x8d, 0xc2, 0x67, 0xba, 0xd8, 0xd6, - 0x6d, 0x09, 0xc3, 0x3d, 0x30, 0x5f, 0x4d, 0xd6, 0x6d, 0xf4, 0x95, 0xc1, 0xe2, 0xc6, 0xaa, 0x21, - 0x47, 0x33, 0xaa, 0xd1, 0x8c, 0x51, 0x59, 0x60, 0x0d, 0x8f, 0xc7, 0x5a, 0xed, 0xf7, 0x58, 0x83, - 0x55, 0xcb, 0x1a, 0x8d, 0x48, 0x86, 0xa3, 0x24, 0x2b, 0x2e, 0xc6, 0xda, 0xb2, 0xe4, 0xaf, 0x30, - 0xfd, 0xf3, 0xa9, 0xa6, 0xd8, 0x13, 0x76, 0x68, 0x83, 0x79, 0x1c, 0x23, 0x87, 0x7f, 0x67, 0xb7, - 0x29, 0x4e, 0xea, 0xfd, 0x75, 0xd2, 0x4e, 0x25, 0x82, 0x75, 0x8f, 0x1f, 0x35, 0x25, 0xad, 0x3a, - 0xf5, 0x23, 0x4e, 0x7a, 0x0b, 0xc7, 0x88, 0x97, 0x42, 0x17, 0xb4, 0xb8, 0x24, 0xac, 0xdb, 0xea, - 0x37, 0xc4, 0xe8, 0x52, 0x34, 0x83, 0x8b, 0x66, 0x94, 0xa2, 0x19, 0x9b, 0x94, 0xc4, 0xd6, 0x63, - 0xce, 0xf7, 0xfd, 0x54, 0x1b, 0x04, 0x24, 0xdb, 0xcb, 0x3d, 0xc3, 0xa7, 0x91, 0x59, 0x2a, 0x2c, - 0x1f, 0xeb, 0x0c, 0xed, 0x9b, 0x59, 0x91, 0x60, 0x26, 0x1a, 0x98, 0x2d, 0x99, 0xe1, 0x2e, 0xb8, - 0x9b, 0xe2, 0x43, 0x37, 0x45, 0x4e, 0x8a, 0x7d, 0x4c, 0x0e, 0x70, 0xea, 0xb8, 0x08, 0xa5, 0x98, - 0xb1, 0xee, 0x9c, 0x90, 0x56, 0xbf, 0x18, 0x6b, 0xaa, 0x9c, 0xf2, 0x9a, 0x42, 0xdd, 0xbe, 0x23, - 0x11, 0xbb, 0x04, 0x5e, 0x94, 0xfb, 0x5f, 0xea, 0xa0, 0xfd, 0x26, 0xc7, 0x69, 0xb1, 0x49, 0x63, - 0x44, 0x84, 0x4a, 0x2f, 0xc1, 0x32, 0xcf, 0x95, 0xf3, 0x81, 0x6f, 0x3b, 0x7c, 0x1e, 0x61, 0x6a, - 0x7b, 0xe3, 0x81, 0x71, 0x39, 0x77, 0x06, 0xb7, 0x5d, 0x34, 0xef, 0x14, 0x09, 0xb6, 0x97, 0xc2, - 0xd9, 0x25, 0x5c, 0x01, 0x2d, 0x84, 0x63, 0x1a, 0x49, 0xfb, 0x6d, 0xb9, 0xe0, 0x16, 0xdc, 0xdc, - 0xec, 0x2b, 0x0e, 0x5c, 0x67, 0xeb, 0x3b, 0xb0, 0x30, 0x89, 0xee, 0x0d, 0x7c, 0xbd, 0x5f, 0xb2, - 0x76, 0x24, 0xeb, 0xa4, 0x55, 0x1a, 0x3b, 0xa5, 0xd2, 0xbf, 0xd6, 0xc1, 0xd2, 0xdb, 0x22, 0xce, - 0xf6, 0x70, 0x46, 0x7c, 0x11, 0xf1, 0x35, 0x00, 0xf3, 0x18, 0xe1, 0x34, 0x2c, 0x48, 0x1c, 0x38, - 0x42, 0x25, 0x82, 0xca, 0xc8, 0x77, 0xa6, 0x08, 0xaf, 0xdd, 0x42, 0x50, 0x03, 0x8b, 0x8c, 0xb7, - 0x3b, 0xb3, 0x3a, 0x00, 0xb1, 0x35, 0xaa, 0xc4, 0x98, 0xe4, 0xb1, 0xf1, 0x8f, 0xf2, 0x38, 0x7b, - 0x9b, 0x9a, 0xff, 0xf3, 0x36, 0x3d, 0x7a, 0x0e, 0x96, 0x2e, 0x05, 0x00, 0xb6, 0x01, 0xb0, 0x8a, - 0x8a, 0xbb, 0x53, 0x83, 0x00, 0xcc, 0x59, 0x05, 0x1f, 0xaa, 0xa3, 0xf0, 0xf7, 0x57, 0x94, 0x97, - 0x77, 0xea, 0xbd, 0xe6, 0xa7, 0x6f, 0x6a, 0xcd, 0xda, 0x3e, 0x3e, 0x53, 0x95, 0x93, 0x33, 0x55, - 0xf9, 0x75, 0xa6, 0x2a, 0x47, 0xe7, 0x6a, 0xed, 0xe4, 0x5c, 0xad, 0xfd, 0x3c, 0x57, 0x6b, 0xbb, - 0x1b, 0x33, 0x17, 0xa4, 0x4c, 0xdc, 0x7a, 0xe8, 0x7a, 0xac, 0x5a, 0x98, 0x07, 0xc3, 0xa7, 0xe6, - 0xc7, 0xea, 0xbf, 0x28, 0x2e, 0x8c, 0x37, 0x27, 0x3e, 0xee, 0xc9, 0x9f, 0x00, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x9a, 0x18, 0xcb, 0x36, 0x05, 0x00, 0x00, + 0x1c, 0x8d, 0xf3, 0xa7, 0x7f, 0xae, 0x34, 0xb5, 0x4e, 0x45, 0xa4, 0x01, 0xec, 0xc8, 0x03, 0x8a, + 0x50, 0x6b, 0x93, 0x32, 0x20, 0xb1, 0xe1, 0x06, 0xa1, 0xa2, 0x0a, 0x81, 0xa9, 0x18, 0xba, 0x58, + 0x8e, 0xef, 0x48, 0x4f, 0x8d, 0x7d, 0xe6, 0xce, 0x6e, 0xf1, 0x37, 0x60, 0xec, 0x08, 0x12, 0x1b, + 0x1b, 0xdf, 0x82, 0xad, 0x63, 0x47, 0xa6, 0x14, 0xb5, 0x1b, 0x63, 0x3f, 0x01, 0xba, 0x3b, 0x3b, + 0x4d, 0x8b, 0x2a, 0x75, 0x80, 0xc9, 0xbe, 0x7b, 0xbf, 0xdf, 0xbb, 0x9f, 0xdf, 0x7b, 0x67, 0xb0, + 0x42, 0x79, 0x44, 0x39, 0xe1, 0xce, 0x88, 0x86, 0x7b, 0x59, 0x22, 0x1f, 0x76, 0xc2, 0x68, 0x4a, + 0x61, 0xb3, 0x80, 0x6c, 0x05, 0xb5, 0x97, 0x87, 0x74, 0x48, 0x25, 0xe4, 0x88, 0x37, 0x55, 0xd5, + 0x36, 0x86, 0x94, 0x0e, 0x47, 0xd8, 0x91, 0xab, 0x41, 0xf6, 0xde, 0x41, 0x19, 0x0b, 0x52, 0x42, + 0xe3, 0x02, 0x37, 0xaf, 0xe2, 0x29, 0x89, 0x30, 0x4f, 0x83, 0x28, 0x29, 0x09, 0x42, 0x79, 0x8e, + 0x33, 0x08, 0x38, 0x76, 0xf6, 0x7b, 0x03, 0x9c, 0x06, 0x3d, 0x27, 0xa4, 0xa4, 0x20, 0xb0, 0x7e, + 0xd4, 0x00, 0x78, 0x8d, 0x19, 0xa1, 0x68, 0x8b, 0x86, 0x7b, 0xb0, 0x09, 0xaa, 0x9b, 0xfd, 0x96, + 0xd6, 0xd1, 0xba, 0x75, 0xaf, 0xba, 0xd9, 0x87, 0x0f, 0x40, 0x83, 0x1e, 0xc4, 0x98, 0xb5, 0xaa, + 0x1d, 0xad, 0x3b, 0xef, 0xea, 0xe7, 0x63, 0xf3, 0x56, 0x1e, 0x44, 0xa3, 0xa7, 0x96, 0xdc, 0xb6, + 0x3c, 0x05, 0xc3, 0x5d, 0x30, 0x57, 0x4e, 0xd6, 0xaa, 0x75, 0xb4, 0xee, 0xc2, 0xfa, 0x8a, 0xad, + 0x46, 0xb3, 0xcb, 0xd1, 0xec, 0x7e, 0x51, 0xe0, 0xf6, 0x8e, 0xc6, 0x66, 0xe5, 0xf7, 0xd8, 0x84, + 0x65, 0xcb, 0x2a, 0x8d, 0x48, 0x8a, 0xa3, 0x24, 0xcd, 0xcf, 0xc7, 0xe6, 0x92, 0xe2, 0x2f, 0x31, + 0xeb, 0xf3, 0x89, 0xa9, 0x79, 0x13, 0x76, 0xe8, 0x81, 0x39, 0x1c, 0x23, 0x5f, 0x7c, 0x67, 0xab, + 0x2e, 0x4f, 0x6a, 0xff, 0x75, 0xd2, 0x76, 0x29, 0x82, 0x7b, 0x57, 0x1c, 0x75, 0x41, 0x5a, 0x76, + 0x5a, 0x87, 0x82, 0x74, 0x16, 0xc7, 0x48, 0x94, 0xc2, 0x00, 0x34, 0x84, 0x24, 0xbc, 0xd5, 0xe8, + 0xd4, 0xe4, 0xe8, 0x4a, 0x34, 0x5b, 0x88, 0x66, 0x17, 0xa2, 0xd9, 0x1b, 0x94, 0xc4, 0xee, 0x23, + 0xc1, 0xf7, 0xfd, 0xc4, 0xec, 0x0e, 0x49, 0xba, 0x9b, 0x0d, 0xec, 0x90, 0x46, 0x4e, 0xa1, 0xb0, + 0x7a, 0xac, 0x71, 0xb4, 0xe7, 0xa4, 0x79, 0x82, 0xb9, 0x6c, 0xe0, 0x9e, 0x62, 0x86, 0x3b, 0xe0, + 0x0e, 0xc3, 0x07, 0x01, 0x43, 0x3e, 0xc3, 0x21, 0x26, 0xfb, 0x98, 0xf9, 0x01, 0x42, 0x0c, 0x73, + 0xde, 0x9a, 0x91, 0xd2, 0x5a, 0xe7, 0x63, 0xd3, 0x50, 0x53, 0x5e, 0x53, 0x68, 0x79, 0xb7, 0x15, + 0xe2, 0x15, 0xc0, 0xb3, 0x62, 0xff, 0x4b, 0x15, 0x34, 0xdf, 0x64, 0x98, 0xe5, 0x1b, 0x34, 0x46, + 0x44, 0xaa, 0xf4, 0x1c, 0x2c, 0x89, 0x5c, 0xf9, 0x1f, 0xc4, 0xb6, 0x2f, 0xe6, 0x91, 0xa6, 0x36, + 0xd7, 0xef, 0xdb, 0x97, 0x73, 0x67, 0x0b, 0xdb, 0x65, 0xf3, 0x76, 0x9e, 0x60, 0x6f, 0x71, 0x34, + 0xbd, 0x84, 0xcb, 0xa0, 0x81, 0x70, 0x4c, 0x23, 0x65, 0xbf, 0xa7, 0x16, 0xc2, 0x82, 0x9b, 0x9b, + 0x7d, 0xc5, 0x81, 0xeb, 0x6c, 0x7d, 0x07, 0xe6, 0x27, 0xd1, 0xbd, 0x81, 0xaf, 0xf7, 0x0a, 0x56, + 0x5d, 0xb1, 0x4e, 0x5a, 0x95, 0xb1, 0x17, 0x54, 0xd6, 0xd7, 0x2a, 0x58, 0x7c, 0x9b, 0xc7, 0xe9, + 0x2e, 0x4e, 0x49, 0x28, 0x23, 0xbe, 0x0a, 0x60, 0x16, 0x23, 0xcc, 0x46, 0x39, 0x89, 0x87, 0xbe, + 0x54, 0x89, 0xa0, 0x22, 0xf2, 0xfa, 0x05, 0x22, 0x6a, 0x37, 0x11, 0x34, 0xc1, 0x02, 0x17, 0xed, + 0xfe, 0xb4, 0x0e, 0x40, 0x6e, 0xf5, 0x4b, 0x31, 0x26, 0x79, 0xac, 0xfd, 0xa3, 0x3c, 0x4e, 0xdf, + 0xa6, 0xfa, 0xff, 0xbc, 0x4d, 0x0f, 0x5f, 0x82, 0xc5, 0x4b, 0x01, 0x80, 0x4d, 0x00, 0xdc, 0xbc, + 0xe4, 0xd6, 0x2b, 0x10, 0x80, 0x19, 0x37, 0x17, 0x43, 0xe9, 0x9a, 0x78, 0x7f, 0x45, 0x45, 0xb9, + 0x5e, 0x85, 0x0b, 0x60, 0xd6, 0xcd, 0x5f, 0x30, 0x9a, 0x25, 0x7a, 0xad, 0x5d, 0xff, 0xf4, 0xcd, + 0xa8, 0xb8, 0x5b, 0x47, 0xa7, 0x86, 0x76, 0x7c, 0x6a, 0x68, 0xbf, 0x4e, 0x0d, 0xed, 0xf0, 0xcc, + 0xa8, 0x1c, 0x9f, 0x19, 0x95, 0x9f, 0x67, 0x46, 0x65, 0x67, 0x7d, 0xea, 0xb6, 0x14, 0xf1, 0x5b, + 0x1b, 0x05, 0x03, 0x5e, 0x2e, 0x9c, 0xfd, 0xde, 0x13, 0xe7, 0x63, 0xf9, 0x93, 0x94, 0xb7, 0x67, + 0x30, 0x23, 0xbf, 0xf4, 0xf1, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc9, 0x87, 0x80, 0xb1, 0x43, + 0x05, 0x00, 0x00, } func (m *PeriodLock) Marshal() (dAtA []byte, err error) { diff --git a/x/pool-incentives/keeper/keeper.go b/x/pool-incentives/keeper/keeper.go index a84c1f5fe38..ebfc443d1c6 100644 --- a/x/pool-incentives/keeper/keeper.go +++ b/x/pool-incentives/keeper/keeper.go @@ -185,13 +185,14 @@ func (k Keeper) GetPoolGaugeId(ctx sdk.Context, poolId uint64, lockableDuration key := types.GetPoolGaugeIdInternalStoreKey(poolId, lockableDuration) store := ctx.KVStore(k.storeKey) - bz := store.Get(key) - if len(bz) == 0 { + if !store.Has(key) { return 0, types.NoGaugeAssociatedWithPoolError{PoolId: poolId, Duration: lockableDuration} } - return sdk.BigEndianToUint64(bz), nil + bz := store.Get(key) + gaugeId := sdk.BigEndianToUint64(bz) + return gaugeId, nil } // GetNoLockGaugeIdsFromPool returns all the NoLock gauge ids associated with the pool id. diff --git a/x/poolmanager/create_pool.go b/x/poolmanager/create_pool.go index 49a6159284a..e2da8d9bb39 100644 --- a/x/poolmanager/create_pool.go +++ b/x/poolmanager/create_pool.go @@ -12,7 +12,7 @@ import ( ) // validateCreatedPool checks that the pool was created with the correct pool ID and address. -func (k Keeper) validateCreatedPool(ctx sdk.Context, poolId uint64, pool types.PoolI) error { +func (k Keeper) validateCreatedPool(poolId uint64, pool types.PoolI) error { if pool.GetId() != poolId { return types.IncorrectPoolIdError{ExpectedPoolId: poolId, ActualPoolId: pool.GetId()} } @@ -115,7 +115,7 @@ func (k Keeper) createPoolZeroLiquidityNoCreationFee(ctx sdk.Context, msg types. k.SetPoolRoute(ctx, poolId, msg.GetPoolType()) // Validates the pool address and pool ID stored match what was expected. - if err := k.validateCreatedPool(ctx, poolId, pool); err != nil { + if err := k.validateCreatedPool(poolId, pool); err != nil { return nil, err } diff --git a/x/poolmanager/export_test.go b/x/poolmanager/export_test.go index 2b22169c302..7809023a3d1 100644 --- a/x/poolmanager/export_test.go +++ b/x/poolmanager/export_test.go @@ -39,7 +39,7 @@ func (k Keeper) GetAllPoolRoutes(ctx sdk.Context) []types.ModuleRoute { } func (k Keeper) ValidateCreatedPool(ctx sdk.Context, poolId uint64, pool types.PoolI) error { - return k.validateCreatedPool(ctx, poolId, pool) + return k.validateCreatedPool(poolId, pool) } func (k Keeper) IsOsmoRoutedMultihop(ctx sdk.Context, route types.MultihopRoute, inDenom, outDenom string) (isRouted bool) { @@ -62,3 +62,7 @@ func (k Keeper) CreateOsmoMultihopExpectedSwapOuts( ) ([]sdk.Int, error) { return k.createOsmoMultihopExpectedSwapOuts(ctx, route, tokenOut, cumulativeRouteSwapFee, sumOfSwapFees) } + +func (k Keeper) TrackVolume(ctx sdk.Context, poolId uint64, volumeGenerated sdk.Coin) { + k.trackVolume(ctx, poolId, volumeGenerated) +} diff --git a/x/poolmanager/keeper.go b/x/poolmanager/keeper.go index ec647b2d5d4..aa0dc0ae723 100644 --- a/x/poolmanager/keeper.go +++ b/x/poolmanager/keeper.go @@ -20,6 +20,8 @@ type Keeper struct { bankKeeper types.BankI accountKeeper types.AccountI communityPoolKeeper types.CommunityPoolI + stakingKeeper types.StakingKeeper + protorevKeeper types.ProtorevKeeper // routes is a map to get the pool module by id. routes map[types.PoolType]types.PoolModuleI @@ -33,7 +35,7 @@ type Keeper struct { paramSpace paramtypes.Subspace } -func NewKeeper(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, gammKeeper types.PoolModuleI, concentratedKeeper types.PoolModuleI, cosmwasmpoolKeeper types.PoolModuleI, bankKeeper types.BankI, accountKeeper types.AccountI, communityPoolKeeper types.CommunityPoolI) *Keeper { +func NewKeeper(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, gammKeeper types.PoolModuleI, concentratedKeeper types.PoolModuleI, cosmwasmpoolKeeper types.PoolModuleI, bankKeeper types.BankI, accountKeeper types.AccountI, communityPoolKeeper types.CommunityPoolI, stakingKeeper types.StakingKeeper, protorevKeeper types.ProtorevKeeper) *Keeper { // set KeyTable if it has not already been set if !paramSpace.HasKeyTable() { paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) @@ -61,6 +63,8 @@ func NewKeeper(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, gammKeeper communityPoolKeeper: communityPoolKeeper, routes: routesMap, poolModules: routesList, + stakingKeeper: stakingKeeper, + protorevKeeper: protorevKeeper, } } @@ -117,3 +121,13 @@ func (k Keeper) SetNextPoolId(ctx sdk.Context, poolId uint64) { func (k *Keeper) SetPoolIncentivesKeeper(poolIncentivesKeeper types.PoolIncentivesKeeperI) { k.poolIncentivesKeeper = poolIncentivesKeeper } + +// SetStakingKeeper sets staking keeper +func (k *Keeper) SetStakingKeeper(stakingKeeper types.StakingKeeper) { + k.stakingKeeper = stakingKeeper +} + +// SetProtorevKeeper sets protorev keeper +func (k *Keeper) SetProtorevKeeper(protorevKeeper types.ProtorevKeeper) { + k.protorevKeeper = protorevKeeper +} diff --git a/x/poolmanager/keeper_test.go b/x/poolmanager/keeper_test.go index f6d0fad7ad6..04d08c83be9 100644 --- a/x/poolmanager/keeper_test.go +++ b/x/poolmanager/keeper_test.go @@ -37,6 +37,11 @@ func TestKeeperTestSuite(t *testing.T) { func (s *KeeperTestSuite) SetupTest() { s.Setup() + + // Set the bond denom to be uosmo to make volume tracking tests more readable. + skParams := s.App.StakingKeeper.GetParams(s.Ctx) + skParams.BondDenom = "uosmo" + s.App.StakingKeeper.SetParams(s.Ctx, skParams) } // createBalancerPoolsFromCoinsWithSpreadFactor creates balancer pools from given sets of coins and respective spread factors. diff --git a/x/poolmanager/router.go b/x/poolmanager/router.go index e6e5587f889..4a94670ba13 100644 --- a/x/poolmanager/router.go +++ b/x/poolmanager/router.go @@ -97,6 +97,9 @@ func (k Keeper) RouteExactAmountIn( return sdk.Int{}, err } + // Track volume for volume-splitting incentives + k.trackVolume(ctx, pool.GetId(), tokenIn) + // Chain output of current pool as the input for the next routed pool tokenIn = sdk.NewCoin(routeStep.TokenOutDenom, tokenOutAmount) } @@ -377,16 +380,19 @@ func (k Keeper) RouteExactAmountOut(ctx sdk.Context, spreadFactor = routeSpreadFactor.Mul((spreadFactor.Quo(sumOfSpreadFactors))) } - _tokenInAmount, swapErr := swapModule.SwapExactAmountOut(ctx, sender, pool, routeStep.TokenInDenom, insExpected[i], _tokenOut, spreadFactor) + curTokenInAmount, swapErr := swapModule.SwapExactAmountOut(ctx, sender, pool, routeStep.TokenInDenom, insExpected[i], _tokenOut, spreadFactor) if swapErr != nil { return sdk.Int{}, swapErr } + // Track volume for volume-splitting incentives + k.trackVolume(ctx, pool.GetId(), sdk.NewCoin(routeStep.TokenInDenom, curTokenInAmount)) + // Sets the final amount of tokens that need to be input into the first pool. Even though this is the final return value for the // whole method and will not change after the first iteration, we still iterate through the rest of the pools to execute their respective // swaps. if i == 0 { - tokenInAmount = _tokenInAmount + tokenInAmount = curTokenInAmount } } @@ -741,3 +747,93 @@ func (k Keeper) TotalLiquidity(ctx sdk.Context) (sdk.Coins, error) { totalLiquidity := totalGammLiquidity.Add(totalConcentratedLiquidity...).Add(totalCosmwasmLiquidity...) return totalLiquidity, nil } + +// nolint: unused +// trackVolume converts the input token into OSMO units and adds it to the global tracked volume for the given pool ID. +// Fails quietly if an OSMO paired pool cannot be found, although this should only happen in rare scenarios where OSMO is +// removed as a base denom from the protorev module (which this function relies on). +// +// CONTRACT: `volumeGenerated` corresponds to one of the denoms in the pool +// CONTRACT: pool with `poolId` exists +func (k Keeper) trackVolume(ctx sdk.Context, poolId uint64, volumeGenerated sdk.Coin) { + // If the denom is already denominated in uosmo, we can just use it directly + OSMO := k.stakingKeeper.BondDenom(ctx) + if volumeGenerated.Denom == OSMO { + k.addVolume(ctx, poolId, volumeGenerated) + return + } + + // Get the most liquid OSMO-paired pool with `volumeGenerated`'s denom using `GetPoolForDenomPair` + osmoPairedPoolId, err := k.protorevKeeper.GetPoolForDenomPair(ctx, OSMO, volumeGenerated.Denom) + + // If no pool is found, fail quietly. + // + // This is a rare scenario that should only happen if OSMO-paired pools are all removed from the protorev module. + // Since this removal scenario is all-or-nothing, this is functionally equiavalent to freezing the tracked volume amounts + // where they were prior to the disabling, which seems an appropriate response. + // + // This branch would also get triggered in the case where there is a token that has no OSMO-paired pool on the entire chain. + // We simply do not track volume in these cases. Importantly, volume splitting gauge logic should prevent a gauge from being + // created for such a pool that includes such a token, although it is okay to no-op in these cases regardless. + if err != nil { + return + } + + // Since we want to ultimately multiply the volume by this spot price, we want to quote OSMO in terms of the input token. + // This is so that once we multiply the volume by the spot price, we get the volume in units of OSMO. + osmoPerInputToken, err := k.RouteCalculateSpotPrice(ctx, osmoPairedPoolId, OSMO, volumeGenerated.Denom) + + // We expect that if a pool is found, there should always be an available spot price as well. + // That being said, if there is an error finding the spot price, we fail quietly and leave tracked volume unchanged. + // This is because we do not want to escalate an issue with finding spot price to locking all swaps involving the given asset. + if err != nil { + return + } + + // Multiply `volumeGenerated.Amount.ToDec()` by this spot price. + // While rounding does not particularly matter here, we round down to ensure that we do not overcount volume. + volumeInOsmo := volumeGenerated.Amount.ToDec().Mul(osmoPerInputToken).TruncateInt() + + // Add this new volume to the global tracked volume for the pool ID + k.addVolume(ctx, poolId, sdk.NewCoin(OSMO, volumeInOsmo)) +} + +// nolint: unused +// addVolume adds the given volume to the global tracked volume for the given pool ID. +func (k Keeper) addVolume(ctx sdk.Context, poolId uint64, volumeGenerated sdk.Coin) { + // Get the current volume for the pool ID + currentTotalVolume := k.GetTotalVolumeForPool(ctx, poolId) + + // Add newly generated volume to existing volume and set updated volume in state + newTotalVolume := currentTotalVolume.Add(volumeGenerated) + k.setVolume(ctx, poolId, newTotalVolume) +} + +// nolint: unused +// setVolume sets the given volume to the global tracked volume for the given pool ID. +func (k Keeper) setVolume(ctx sdk.Context, poolId uint64, totalVolume sdk.Coins) { + storedVolume := types.TrackedVolume{Amount: totalVolume} + osmoutils.MustSet(ctx.KVStore(k.storeKey), types.KeyPoolVolume(poolId), &storedVolume) +} + +// GetTotalVolumeForPool gets the total OSMO-denominated historical volume for a given pool ID. +func (k Keeper) GetTotalVolumeForPool(ctx sdk.Context, poolId uint64) sdk.Coins { + var currentTrackedVolume types.TrackedVolume + volumeFound, err := osmoutils.Get(ctx.KVStore(k.storeKey), types.KeyPoolVolume(poolId), ¤tTrackedVolume) + if err != nil { + // We can only encounter an error if a database or serialization errors occurs, so we panic here. + // Normally this would be handled by `osmoutils.MustGet`, but since we want to specifically use `osmoutils.Get`, + // we also have to manually panic here. + panic(err) + } + + // If no volume was found, we treat the existing volume as 0. + // While we can technically require volume to exist, we would need to store empty coins in state for each pool (past and present), + // which is a high storage cost to pay for a weak guardrail. + currentTotalVolume := sdk.NewCoins() + if volumeFound { + currentTotalVolume = currentTrackedVolume.Amount + } + + return currentTotalVolume +} diff --git a/x/poolmanager/router_test.go b/x/poolmanager/router_test.go index 7a84cb0921e..709256fc169 100644 --- a/x/poolmanager/router_test.go +++ b/x/poolmanager/router_test.go @@ -2734,3 +2734,252 @@ func (s *KeeperTestSuite) TestCreateMultihopExpectedSwapOuts() { }) } } + +// runMultipleTrackVolumes runs TrackVolume on the same pool multiple times +func (s *KeeperTestSuite) runMultipleTrackVolumes(poolId uint64, volume sdk.Coin, times int64) { + for i := 0; i < int(times); i++ { + s.App.PoolManagerKeeper.TrackVolume(s.Ctx, poolId, volume) + } +} + +// Testing strategy: +// 1. If applicable, create an OSMO-paired pool +// 2. Set OSMO-paired pool as canonical for that denom pair in state +// 3. Run `trackVolume` on test input amount (cases include both OSMO and non-OSMO volumes) +// 4. Assert correct amount was added to pool volume +func (s *KeeperTestSuite) TestTrackVolume() { + hundred := sdk.NewInt(100) + hundredFoo := sdk.NewCoin(foo, hundred) + hundredUosmo := sdk.NewCoin(uosmo, hundred) + oneRun := int64(1) + threeRuns := int64(3) + + tests := map[string]struct { + generatedVolume sdk.Coin + timesRun int64 + osmoPairedPoolType types.PoolType + osmoPairedPoolCoins sdk.Coins + + expectedVolume sdk.Int + }{ + "Happy path: volume denominated in OSMO": { + generatedVolume: hundredUosmo, + timesRun: oneRun, + + expectedVolume: hundred.MulRaw(oneRun), + }, + + // --- Pricing against balancer pool --- + + "Non-OSMO volume priced with balancer pool": { + generatedVolume: hundredFoo, + timesRun: oneRun, + osmoPairedPoolType: types.Balancer, + // Spot price = 1 + osmoPairedPoolCoins: fooUosmoCoins, + + expectedVolume: hundred.MulRaw(oneRun), + }, + "Non-OSMO volume priced with balancer pool, multiple runs": { + generatedVolume: hundredFoo, + timesRun: threeRuns, + osmoPairedPoolType: types.Balancer, + // Spot price = 1 + osmoPairedPoolCoins: fooUosmoCoins, + + expectedVolume: hundred.MulRaw(threeRuns), + }, + "Non-OSMO volume priced with balancer pool, large spot price": { + generatedVolume: hundredFoo, + timesRun: oneRun, + osmoPairedPoolType: types.Balancer, + // 100 foo corresponds to 1000 osmo (spot price = 10) + osmoPairedPoolCoins: sdk.NewCoins( + sdk.NewCoin(foo, sdk.NewInt(100)), + sdk.NewCoin(uosmo, sdk.NewInt(1000)), + ), + + expectedVolume: sdk.NewInt(10).Mul(hundred).MulRaw(oneRun), + }, + "Non-OSMO volume priced with balancer pool, small spot price": { + generatedVolume: hundredFoo, + timesRun: oneRun, + osmoPairedPoolType: types.Balancer, + // 100 foo corresponds to 10 osmo (spot price = 0.1) + osmoPairedPoolCoins: sdk.NewCoins( + sdk.NewCoin(foo, sdk.NewInt(100)), + sdk.NewCoin(uosmo, sdk.NewInt(10)), + ), + + expectedVolume: hundred.MulRaw(oneRun).Quo(sdk.NewInt(10)), + }, + "Non-OSMO volume priced with balancer pool, large spot price, multiple runs": { + generatedVolume: hundredFoo, + timesRun: threeRuns, + osmoPairedPoolType: types.Balancer, + // 100 foo corresponds to 1000 osmo (spot price = 10) + osmoPairedPoolCoins: sdk.NewCoins( + sdk.NewCoin(foo, sdk.NewInt(100)), + sdk.NewCoin(uosmo, sdk.NewInt(1000)), + ), + + expectedVolume: sdk.NewInt(10).Mul(hundred).MulRaw(threeRuns), + }, + "Non-OSMO volume priced with balancer pool, small spot price, multiple runs": { + generatedVolume: hundredFoo, + timesRun: threeRuns, + osmoPairedPoolType: types.Balancer, + // 100 foo corresponds to 10 osmo (spot price = 0.1) + osmoPairedPoolCoins: sdk.NewCoins( + sdk.NewCoin(foo, sdk.NewInt(100)), + sdk.NewCoin(uosmo, sdk.NewInt(10)), + ), + + expectedVolume: hundred.MulRaw(threeRuns).Quo(sdk.NewInt(10)), + }, + + // --- Pricing against CL pool --- + + "Non-OSMO volume priced with concentrated pool, multiple runs": { + generatedVolume: hundredFoo, + timesRun: threeRuns, + osmoPairedPoolType: types.Concentrated, + // Spot price = 1 + osmoPairedPoolCoins: fooUosmoCoins, + + expectedVolume: hundred.MulRaw(threeRuns), + }, + "Non-OSMO volume priced with concentrated pool, large spot price": { + generatedVolume: hundredFoo, + timesRun: oneRun, + osmoPairedPoolType: types.Concentrated, + // 100 foo corresponds to 1000 osmo (spot price = 10) + osmoPairedPoolCoins: sdk.NewCoins( + sdk.NewCoin(foo, sdk.NewInt(100)), + sdk.NewCoin(uosmo, sdk.NewInt(1000)), + ), + + expectedVolume: sdk.NewInt(10).Mul(hundred).MulRaw(oneRun), + }, + "Non-OSMO volume priced with concentrated pool, small spot price": { + generatedVolume: hundredFoo, + timesRun: oneRun, + osmoPairedPoolType: types.Concentrated, + // 100 foo corresponds to 10 osmo (spot price = 0.1) + osmoPairedPoolCoins: sdk.NewCoins( + sdk.NewCoin(foo, sdk.NewInt(100)), + sdk.NewCoin(uosmo, sdk.NewInt(10)), + ), + + expectedVolume: hundred.MulRaw(oneRun).Quo(sdk.NewInt(10)), + }, + "Non-OSMO volume priced with concentrated pool, large spot price, multiple runs": { + generatedVolume: hundredFoo, + timesRun: threeRuns, + osmoPairedPoolType: types.Concentrated, + // 100 foo corresponds to 1000 osmo (spot price = 10) + osmoPairedPoolCoins: sdk.NewCoins( + sdk.NewCoin(foo, sdk.NewInt(100)), + sdk.NewCoin(uosmo, sdk.NewInt(1000)), + ), + + expectedVolume: sdk.NewInt(10).Mul(hundred).MulRaw(threeRuns), + }, + "Non-OSMO volume priced with concentrated pool, small spot price, multiple runs": { + generatedVolume: hundredFoo, + timesRun: threeRuns, + osmoPairedPoolType: types.Concentrated, + // 100 foo corresponds to 10 osmo (spot price = 0.1) + osmoPairedPoolCoins: sdk.NewCoins( + sdk.NewCoin(foo, sdk.NewInt(100)), + sdk.NewCoin(uosmo, sdk.NewInt(10)), + ), + + expectedVolume: hundred.MulRaw(threeRuns).Quo(sdk.NewInt(10)), + }, + + // --- Pricing against cosmwasm pool --- + + "Non-OSMO volume priced with CosmWasm pool, multiple runs": { + generatedVolume: hundredFoo, + timesRun: threeRuns, + osmoPairedPoolType: types.CosmWasm, + // Spot price = 1 since our test CW pool is a 1:1 transmuter + osmoPairedPoolCoins: fooUosmoCoins, + + expectedVolume: hundred.MulRaw(threeRuns), + }, + + // --- Edge cases --- + + "OSMO denominated volume, no volume added": { + generatedVolume: sdk.NewCoin(uosmo, sdk.NewInt(0)), + timesRun: oneRun, + osmoPairedPoolType: types.Balancer, + osmoPairedPoolCoins: fooUosmoCoins, + + expectedVolume: sdk.NewInt(0), + }, + "Non-OSMO volume priced with balancer pool, no volume added": { + generatedVolume: sdk.NewCoin(foo, sdk.NewInt(0)), + timesRun: oneRun, + osmoPairedPoolType: types.Balancer, + osmoPairedPoolCoins: fooUosmoCoins, + + expectedVolume: sdk.NewInt(0), + }, + "Added volume truncated to zero (no volume added)": { + generatedVolume: sdk.NewCoin(foo, sdk.NewInt(1)), + timesRun: oneRun, + osmoPairedPoolType: types.Balancer, + // 100 foo corresponds to 10 osmo (spot price = 0.1) + osmoPairedPoolCoins: sdk.NewCoins( + sdk.NewCoin(foo, sdk.NewInt(100)), + sdk.NewCoin(uosmo, sdk.NewInt(10)), + ), + + expectedVolume: sdk.NewInt(0), + }, + "Non-OSMO denominated volume, no OSMO-paired pool": { + generatedVolume: hundredFoo, + timesRun: oneRun, + + // Note that we expect this to fail quietly and track zero volume, + // as described in the function spec/comments. + expectedVolume: sdk.NewInt(0), + }, + } + + for name, tc := range tests { + s.Run(name, func() { + s.SetupTest() + + // --- Setup --- + + // Create target pool to track volume for. + // Note that the actual contents or type of this pool do not matter for this test as we just need the ID. + targetPoolId := s.PrepareBalancerPool() + + // If applicable, create an OSMO-paired pool and set it in protorev + if tc.osmoPairedPoolCoins != nil { + osmoPairedPoolId := s.CreatePoolFromTypeWithCoins(tc.osmoPairedPoolType, tc.osmoPairedPoolCoins) + s.App.ProtoRevKeeper.SetPoolForDenomPair(s.Ctx, uosmo, foo, osmoPairedPoolId) + } + + // --- System under test --- + + // Run TrackVolume the specified number of times. Note that this function fails quietly in all error cases. + s.runMultipleTrackVolumes(targetPoolId, tc.generatedVolume, tc.timesRun) + + // --- Assertions --- + + // Assert that the correct amount of volume was added to the pool tracker + + // Note that the units should always be in OSMO, even if the input volume was in another token. + // + // We wrap with sdk.NewCoins() to sanitize the outputs for comparison in case they are empty. + totalVolume := s.App.PoolManagerKeeper.GetTotalVolumeForPool(s.Ctx, targetPoolId) + s.Require().Equal(sdk.NewCoins(sdk.NewCoin(uosmo, tc.expectedVolume)), sdk.NewCoins(totalVolume...)) + }) + } +} diff --git a/x/poolmanager/types/expected_keepers.go b/x/poolmanager/types/expected_keepers.go new file mode 100644 index 00000000000..7321b618cb7 --- /dev/null +++ b/x/poolmanager/types/expected_keepers.go @@ -0,0 +1,11 @@ +package types + +import sdk "github.com/cosmos/cosmos-sdk/types" + +type StakingKeeper interface { + BondDenom(ctx sdk.Context) string +} + +type ProtorevKeeper interface { + GetPoolForDenomPair(ctx sdk.Context, baseDenom, denomToMatch string) (uint64, error) +} diff --git a/x/poolmanager/types/keys.go b/x/poolmanager/types/keys.go index 5f470da6af8..fb4cb963fa4 100644 --- a/x/poolmanager/types/keys.go +++ b/x/poolmanager/types/keys.go @@ -7,7 +7,8 @@ import ( ) const ( - ModuleName = "poolmanager" + ModuleName = "poolmanager" + KeySeparator = "|" StoreKey = ModuleName @@ -20,6 +21,9 @@ var ( // SwapModuleRouterPrefix defines prefix to store pool id to swap module mappings. SwapModuleRouterPrefix = []byte{0x02} + + // KeyPoolVolumePrefix defines prefix to store pool volume. + KeyPoolVolumePrefix = []byte{0x03} ) // ModuleRouteToBytes serializes moduleRoute to bytes. @@ -37,3 +41,8 @@ func ParseModuleRouteFromBz(bz []byte) (ModuleRoute, error) { } return moduleRoute, err } + +// KeyPoolVolume returns the key for the pool volume corresponding to the given poolId. +func KeyPoolVolume(poolId uint64) []byte { + return []byte(fmt.Sprintf("%s%s%d%s", KeyPoolVolumePrefix, KeySeparator, poolId, KeySeparator)) +} diff --git a/x/poolmanager/types/tracked_volume.pb.go b/x/poolmanager/types/tracked_volume.pb.go new file mode 100644 index 00000000000..5a3e1680c35 --- /dev/null +++ b/x/poolmanager/types/tracked_volume.pb.go @@ -0,0 +1,336 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/poolmanager/v1beta1/tracked_volume.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type TrackedVolume struct { + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` +} + +func (m *TrackedVolume) Reset() { *m = TrackedVolume{} } +func (m *TrackedVolume) String() string { return proto.CompactTextString(m) } +func (*TrackedVolume) ProtoMessage() {} +func (*TrackedVolume) Descriptor() ([]byte, []int) { + return fileDescriptor_0a2e3e91de3baf1a, []int{0} +} +func (m *TrackedVolume) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TrackedVolume) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TrackedVolume.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TrackedVolume) XXX_Merge(src proto.Message) { + xxx_messageInfo_TrackedVolume.Merge(m, src) +} +func (m *TrackedVolume) XXX_Size() int { + return m.Size() +} +func (m *TrackedVolume) XXX_DiscardUnknown() { + xxx_messageInfo_TrackedVolume.DiscardUnknown(m) +} + +var xxx_messageInfo_TrackedVolume proto.InternalMessageInfo + +func (m *TrackedVolume) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Amount + } + return nil +} + +func init() { + proto.RegisterType((*TrackedVolume)(nil), "osmosis.poolmanager.v1beta1.TrackedVolume") +} + +func init() { + proto.RegisterFile("osmosis/poolmanager/v1beta1/tracked_volume.proto", fileDescriptor_0a2e3e91de3baf1a) +} + +var fileDescriptor_0a2e3e91de3baf1a = []byte{ + // 255 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0xc8, 0x2f, 0xce, 0xcd, + 0x2f, 0xce, 0x2c, 0xd6, 0x2f, 0xc8, 0xcf, 0xcf, 0xc9, 0x4d, 0xcc, 0x4b, 0x4c, 0x4f, 0x2d, 0xd2, + 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x2f, 0x29, 0x4a, 0x4c, 0xce, 0x4e, 0x4d, 0x89, + 0x2f, 0xcb, 0xcf, 0x29, 0xcd, 0x4d, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x86, 0xea, + 0xd0, 0x43, 0xd2, 0xa1, 0x07, 0xd5, 0x21, 0x25, 0x92, 0x9e, 0x9f, 0x9e, 0x0f, 0x56, 0xa7, 0x0f, + 0x62, 0x41, 0xb4, 0x48, 0xc9, 0x25, 0x83, 0xf5, 0xe8, 0x27, 0x25, 0x16, 0xa7, 0xc2, 0x0d, 0x4f, + 0xce, 0xcf, 0xcc, 0x83, 0xc8, 0x2b, 0x95, 0x70, 0xf1, 0x86, 0x40, 0xac, 0x0a, 0x03, 0xdb, 0x24, + 0x94, 0xcc, 0xc5, 0x96, 0x98, 0x9b, 0x5f, 0x9a, 0x57, 0x22, 0xc1, 0xa8, 0xc0, 0xac, 0xc1, 0x6d, + 0x24, 0xa9, 0x07, 0x31, 0x41, 0x0f, 0x64, 0x02, 0xcc, 0x32, 0x3d, 0xe7, 0xfc, 0xcc, 0x3c, 0x27, + 0x83, 0x13, 0xf7, 0xe4, 0x19, 0x56, 0xdd, 0x97, 0xd7, 0x48, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, + 0x4b, 0xce, 0xcf, 0xd5, 0x87, 0x5a, 0x07, 0xa1, 0x74, 0x8b, 0x53, 0xb2, 0xf5, 0x4b, 0x2a, 0x0b, + 0x52, 0x8b, 0xc1, 0x1a, 0x8a, 0x83, 0xa0, 0x46, 0x3b, 0x05, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, + 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, + 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x39, 0x92, 0x59, 0x50, 0xdf, 0xea, 0xe6, 0x24, 0x26, 0x15, 0xc3, + 0x38, 0xfa, 0x65, 0x86, 0xe6, 0xfa, 0x15, 0x28, 0x41, 0x06, 0xb6, 0x20, 0x89, 0x0d, 0xec, 0x1f, + 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2c, 0x40, 0xcc, 0x4f, 0x56, 0x01, 0x00, 0x00, +} + +func (m *TrackedVolume) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TrackedVolume) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TrackedVolume) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTrackedVolume(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintTrackedVolume(dAtA []byte, offset int, v uint64) int { + offset -= sovTrackedVolume(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *TrackedVolume) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovTrackedVolume(uint64(l)) + } + } + return n +} + +func sovTrackedVolume(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTrackedVolume(x uint64) (n int) { + return sovTrackedVolume(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *TrackedVolume) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTrackedVolume + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TrackedVolume: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TrackedVolume: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTrackedVolume + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTrackedVolume + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTrackedVolume + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Amount = append(m.Amount, types.Coin{}) + if err := m.Amount[len(m.Amount)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTrackedVolume(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTrackedVolume + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTrackedVolume(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTrackedVolume + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTrackedVolume + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTrackedVolume + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTrackedVolume + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTrackedVolume + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTrackedVolume + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTrackedVolume = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTrackedVolume = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTrackedVolume = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/protorev/client/cli/query.go b/x/protorev/client/cli/query.go index afded878afd..b32b4c0d3c4 100644 --- a/x/protorev/client/cli/query.go +++ b/x/protorev/client/cli/query.go @@ -28,7 +28,7 @@ func NewCmdQuery() *cobra.Command { osmocli.AddQueryCmd(cmd, types.NewQueryClient, NewQueryMaxPoolPointsPerBlockCmd) osmocli.AddQueryCmd(cmd, types.NewQueryClient, NewQueryBaseDenomsCmd) osmocli.AddQueryCmd(cmd, types.NewQueryClient, NewQueryEnabledCmd) - osmocli.AddQueryCmd(cmd, types.NewQueryClient, NewQueryPoolWeightsCmd) + osmocli.AddQueryCmd(cmd, types.NewQueryClient, NewQueryInfoByPoolTypeCmd) osmocli.AddQueryCmd(cmd, types.NewQueryClient, NewQueryPoolCmd) return cmd @@ -141,12 +141,12 @@ func NewQueryEnabledCmd() (*osmocli.QueryDescriptor, *types.QueryGetProtoRevEnab }, &types.QueryGetProtoRevEnabledRequest{} } -// NewQueryPoolWeightsCmd returns the command to query the pool weights of protorev -func NewQueryPoolWeightsCmd() (*osmocli.QueryDescriptor, *types.QueryGetProtoRevPoolWeightsRequest) { +// NewQueryInfoByPoolTypeCmd returns the command to query the pool type info of protorev +func NewQueryInfoByPoolTypeCmd() (*osmocli.QueryDescriptor, *types.QueryGetProtoRevInfoByPoolTypeRequest) { return &osmocli.QueryDescriptor{ - Use: "pool-weights", - Short: "Query the pool weights used to determine how computationally expensive a route is", - }, &types.QueryGetProtoRevPoolWeightsRequest{} + Use: "info-by-pool-type", + Short: "Query the pool info used to determine how computationally expensive a route is", + }, &types.QueryGetProtoRevInfoByPoolTypeRequest{} } // NewQueryPoolCmd returns the command to query the pool id for a given denom pair stored via the highest liquidity method in ProtoRev @@ -158,6 +158,8 @@ func NewQueryPoolCmd() (*osmocli.QueryDescriptor, *types.QueryGetProtoRevPoolReq } // convert a string array "[1,2,3]" to []uint64 +// +//nolint:unparam func parseRoute(arg string, _ *pflag.FlagSet) (any, osmocli.FieldReadLocation, error) { var route []uint64 err := json.Unmarshal([]byte(arg), &route) diff --git a/x/protorev/client/cli/tx.go b/x/protorev/client/cli/tx.go index ffe479b749a..4d02d54e36c 100644 --- a/x/protorev/client/cli/tx.go +++ b/x/protorev/client/cli/tx.go @@ -29,7 +29,7 @@ func NewCmdTx() *cobra.Command { osmocli.AddTxCmd(txCmd, CmdSetMaxPoolPointsPerBlock) txCmd.AddCommand( CmdSetDeveloperHotRoutes().BuildCommandCustomFn(), - CmdSetPoolWeights().BuildCommandCustomFn(), + CmdSetInfoByPoolType().BuildCommandCustomFn(), CmdSetBaseDenoms().BuildCommandCustomFn(), CmdSetProtoRevAdminAccountProposal(), CmdSetProtoRevEnabledProposal(), @@ -141,22 +141,35 @@ func CmdSetMaxPoolPointsPerBlock() (*osmocli.TxCliDesc, *types.MsgSetMaxPoolPoin }, &types.MsgSetMaxPoolPointsPerBlock{} } -// CmdSetPoolWeights implements the command to set the pool weights used to estimate execution costs -func CmdSetPoolWeights() *osmocli.TxCliDesc { +// CmdSetInfoByPoolType implements the command to set the pool information used throughout the module +func CmdSetInfoByPoolType() *osmocli.TxCliDesc { desc := osmocli.TxCliDesc{ - Use: "set-pool-weights [path/to/routes.json]", - Short: "set the protorev pool weights", - Long: `Must provide a json file with all the pool weights that will be set. + Use: "set-info-by-pool-type [path/to/pool_info.json]", + Short: "set the protorev pool type info", + Long: `Must provide a json file with all the pool info that will be set. This does NOT set info for a single pool type. + All information must be provided across all pool types in the json file. Sample json file: { - "stable_weight" : 1, - "balancer_weight" : 1, - "concentrated_weight" : 1 + "stable" : { + "weight" : 1, + }, + "concentrated" : { + "weight" : 1, + "max_ticks_crossed": 10, + }, + "balancer" : { + "weight" : 1, + }, + "cosmwasm" : { + "weight_maps" : [ + {"contract_address" : "cosmos123...", "weight" : 1} + ], + }, } `, - Example: fmt.Sprintf(`$ %s tx protorev set-pool-weights weights.json --from mykey`, version.AppName), + Example: fmt.Sprintf(`$ %s tx protorev set-info-by-pool-type pool_info.json --from mykey`, version.AppName), NumArgs: 1, - ParseAndBuildMsg: BuildSetPoolWeightsMsg, + ParseAndBuildMsg: BuildSetInfoByPoolTypeMsg, } return &desc diff --git a/x/protorev/client/cli/utils.go b/x/protorev/client/cli/utils.go index 689c5682b04..bdbc01fef7d 100644 --- a/x/protorev/client/cli/utils.go +++ b/x/protorev/client/cli/utils.go @@ -122,38 +122,76 @@ func BuildSetHotRoutesMsg(clientCtx client.Context, args []string, fs *flag.Flag }, nil } -// ------------ types/functions to handle a SetPoolWeights CLI TX ------------ // -type createPoolWeightsInput types.PoolWeights +// ------------ types/functions to handle a SetInfoByPoolType CLI TX ------------ // +type InfoByPoolTypeInput struct { + Stable StablePoolInfoInput `json:"stable"` + Balancer BalancerPoolInfoInput `json:"balancer"` + Concentrated ConcentratedPoolInfoInput `json:"concentrated"` + Cosmwasm CosmwasmPoolInfoInput `json:"cosmwasm"` +} + +type StablePoolInfoInput struct { + Weight uint64 `json:"weight"` +} + +type BalancerPoolInfoInput struct { + Weight uint64 `json:"weight"` +} + +type ConcentratedPoolInfoInput struct { + Weight uint64 `json:"weight"` + MaxTicksCrossed uint64 `json:"max_ticks_crossed"` +} -type XCreatePoolWeightsInputs createPoolWeightsInput +type CosmwasmPoolInfoInput struct { + WeightMap map[string]uint64 `json:"weight_map"` +} +type createInfoByPoolTypeInput types.InfoByPoolType -type XCreatePoolWeightsExceptions struct { - XCreatePoolWeightsInputs +type XCreateInfoByPoolTypeInputs createInfoByPoolTypeInput + +type XCreateInfoByPoolTypeExceptions struct { + XCreateInfoByPoolTypeInputs Other *string // Other won't raise an error } // UnmarshalJSON should error if there are fields unexpected. -func (release *createPoolWeightsInput) UnmarshalJSON(data []byte) error { - var createPoolWeightsE XCreatePoolWeightsExceptions +func (release *createInfoByPoolTypeInput) UnmarshalJSON(data []byte) error { + var createInfoByPoolTypeE XCreateInfoByPoolTypeExceptions dec := json.NewDecoder(bytes.NewReader(data)) dec.DisallowUnknownFields() // Force - if err := dec.Decode(&createPoolWeightsE); err != nil { + if err := dec.Decode(&createInfoByPoolTypeE); err != nil { return err } - *release = createPoolWeightsInput(createPoolWeightsE.XCreatePoolWeightsInputs) + *release = createInfoByPoolTypeInput(createInfoByPoolTypeE.XCreateInfoByPoolTypeInputs) return nil } -// BuildSetPoolWeightsMsg builds a MsgSetPoolWeights from the provided json file -func BuildSetPoolWeightsMsg(clientCtx client.Context, args []string, fs *flag.FlagSet) (sdk.Msg, error) { +// createInfoByPoolTypeInput converts the input to the types.InfoByPoolType type +func (release *createInfoByPoolTypeInput) convertToInfoByPoolType() types.InfoByPoolType { + if release == nil { + return types.InfoByPoolType{} + } + + infoByPoolType := types.InfoByPoolType{} + infoByPoolType.Stable = release.Stable + infoByPoolType.Balancer = release.Balancer + infoByPoolType.Concentrated = release.Concentrated + infoByPoolType.Cosmwasm = release.Cosmwasm + + return infoByPoolType +} + +// BuildSetInfoByPoolTypeMsg builds a MsgSetInfoByPoolType from the provided json file +func BuildSetInfoByPoolTypeMsg(clientCtx client.Context, args []string, fs *flag.FlagSet) (sdk.Msg, error) { if len(args) == 0 { return nil, fmt.Errorf("must provide a json file") } // Read the json file - input := &createPoolWeightsInput{} + input := &createInfoByPoolTypeInput{} path := args[0] contents, err := os.ReadFile(path) if err != nil { @@ -167,9 +205,9 @@ func BuildSetPoolWeightsMsg(clientCtx client.Context, args []string, fs *flag.Fl // Build the msg admin := clientCtx.GetFromAddress().String() - return &types.MsgSetPoolWeights{ - Admin: admin, - PoolWeights: types.PoolWeights(*input), + return &types.MsgSetInfoByPoolType{ + Admin: admin, + InfoByPoolType: input.convertToInfoByPoolType(), }, nil } diff --git a/x/protorev/keeper/genesis.go b/x/protorev/keeper/genesis.go index 6f88098aa66..cd4621741f3 100644 --- a/x/protorev/keeper/genesis.go +++ b/x/protorev/keeper/genesis.go @@ -77,9 +77,8 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState types.GenesisState) { // Set the number of pool points that have been consumed in the current block. k.SetPointCountForBlock(ctx, genState.PointCountForBlock) - // Configure the pool weights for genesis. This roughly correlates to the ms of execution time - // by pool type. - k.SetPoolWeights(ctx, genState.PoolWeights) + // Configure the pool info for genesis. + k.SetInfoByPoolType(ctx, genState.InfoByPoolType) // Set the profits that have been collected by Protorev. for _, coin := range genState.Profits { @@ -99,7 +98,7 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { genesis.Params = k.GetParams(ctx) // Export the pool weights. - genesis.PoolWeights = k.GetPoolWeights(ctx) + genesis.InfoByPoolType = k.GetInfoByPoolType(ctx) // Export the token pair arb routes (hot routes). routes, err := k.GetAllTokenPairArbRoutes(ctx) diff --git a/x/protorev/keeper/genesis_test.go b/x/protorev/keeper/genesis_test.go index d68f647f2c1..3ddba2cfd88 100644 --- a/x/protorev/keeper/genesis_test.go +++ b/x/protorev/keeper/genesis_test.go @@ -23,8 +23,8 @@ func (s *KeeperTestSuite) TestInitGenesis() { params := s.App.ProtoRevKeeper.GetParams(s.Ctx) s.Require().Equal(params, exportedGenesis.Params) - poolWeights := s.App.ProtoRevKeeper.GetPoolWeights(s.Ctx) - s.Require().Equal(poolWeights, exportedGenesis.PoolWeights) + poolInfo := s.App.ProtoRevKeeper.GetInfoByPoolType(s.Ctx) + s.Require().Equal(poolInfo, exportedGenesis.InfoByPoolType) daysSinceGenesis, err := s.App.ProtoRevKeeper.GetDaysSinceModuleGenesis(s.Ctx) s.Require().NoError(err) diff --git a/x/protorev/keeper/grpc_query.go b/x/protorev/keeper/grpc_query.go index 49f23b4f461..6bf05c7f54a 100644 --- a/x/protorev/keeper/grpc_query.go +++ b/x/protorev/keeper/grpc_query.go @@ -169,15 +169,15 @@ func (q Querier) GetProtoRevDeveloperAccount(c context.Context, req *types.Query return &types.QueryGetProtoRevDeveloperAccountResponse{DeveloperAccount: developerAccount.String()}, nil } -// GetProtoRevPoolWeights queries the weights of each pool type that is being used for arbitrage -func (q Querier) GetProtoRevPoolWeights(c context.Context, req *types.QueryGetProtoRevPoolWeightsRequest) (*types.QueryGetProtoRevPoolWeightsResponse, error) { +// GetProtoRevInfoByPoolType queries information pertaining to each pool type the module is using for arbitrage +func (q Querier) GetProtoRevInfoByPoolType(c context.Context, req *types.QueryGetProtoRevInfoByPoolTypeRequest) (*types.QueryGetProtoRevInfoByPoolTypeResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "invalid request") } ctx := sdk.UnwrapSDKContext(c) - poolWeights := q.Keeper.GetPoolWeights(ctx) + infoByPoolType := q.Keeper.GetInfoByPoolType(ctx) - return &types.QueryGetProtoRevPoolWeightsResponse{PoolWeights: poolWeights}, nil + return &types.QueryGetProtoRevInfoByPoolTypeResponse{InfoByPoolType: infoByPoolType}, nil } // GetProtoRevPoolPointsPerTx queries the maximum number of pool points that can be consumed per transaction diff --git a/x/protorev/keeper/grpc_query_test.go b/x/protorev/keeper/grpc_query_test.go index de1f85d350c..11b8620dc54 100644 --- a/x/protorev/keeper/grpc_query_test.go +++ b/x/protorev/keeper/grpc_query_test.go @@ -281,20 +281,23 @@ func (s *KeeperTestSuite) TestGetProtoRevDeveloperAccount() { s.Require().Equal(developerAccount.String(), res.DeveloperAccount) } -// TestGetProtoRevPoolWeights tests the query to retrieve the pool weights -func (s *KeeperTestSuite) TestGetProtoRevPoolWeights() { +// TestGetProtoRevInfoByPoolType tests the query to retrieve the pool info +func (s *KeeperTestSuite) TestGetProtoRevInfoByPoolType() { // Set the pool weights - poolWeights := types.PoolWeights{ - StableWeight: 5, - BalancerWeight: 1, - ConcentratedWeight: 3, + poolInfo := types.InfoByPoolType{ + Stable: types.StablePoolInfo{Weight: 1}, + Balancer: types.BalancerPoolInfo{Weight: 1}, + Concentrated: types.ConcentratedPoolInfo{Weight: 1, MaxTicksCrossed: 1}, + Cosmwasm: types.CosmwasmPoolInfo{WeightMaps: []types.WeightMap{ + {ContractAddress: "test", Weight: 1}, + }}, } - s.App.AppKeepers.ProtoRevKeeper.SetPoolWeights(s.Ctx, poolWeights) + s.App.AppKeepers.ProtoRevKeeper.SetInfoByPoolType(s.Ctx, poolInfo) - req := &types.QueryGetProtoRevPoolWeightsRequest{} - res, err := s.queryClient.GetProtoRevPoolWeights(sdk.WrapSDKContext(s.Ctx), req) + req := &types.QueryGetProtoRevInfoByPoolTypeRequest{} + res, err := s.queryClient.GetProtoRevInfoByPoolType(sdk.WrapSDKContext(s.Ctx), req) s.Require().NoError(err) - s.Require().Equal(poolWeights, res.PoolWeights) + s.Require().Equal(poolInfo, res.InfoByPoolType) } // TestGetProtoRevMaxPoolPointsPerTx tests the query to retrieve the max pool points per tx diff --git a/x/protorev/keeper/keeper_test.go b/x/protorev/keeper/keeper_test.go index e4d43658e01..170e729d535 100644 --- a/x/protorev/keeper/keeper_test.go +++ b/x/protorev/keeper/keeper_test.go @@ -78,13 +78,6 @@ func (s *KeeperTestSuite) SetupTest() { panic(err) } - poolWeights := types.PoolWeights{ - StableWeight: 5, // it takes around 5 ms to simulate and execute a stable swap - BalancerWeight: 2, // it takes around 2 ms to simulate and execute a balancer swap - ConcentratedWeight: 2, // it takes around 2 ms to simulate and execute a concentrated swap - } - s.App.ProtoRevKeeper.SetPoolWeights(s.Ctx, poolWeights) - // Configure the initial base denoms used for cyclic route building baseDenomPriorities := []types.BaseDenom{ { @@ -142,6 +135,7 @@ func (s *KeeperTestSuite) SetupTest() { sdk.NewCoin("gamm/pool/1", sdk.NewInt(9000000000000000000)), sdk.NewCoin(apptesting.DefaultTransmuterDenomA, sdk.NewInt(9000000000000000000)), sdk.NewCoin(apptesting.DefaultTransmuterDenomB, sdk.NewInt(9000000000000000000)), + sdk.NewCoin("stake", sdk.NewInt(9000000000000000000)), ) s.fundAllAccountsWith() s.Commit() @@ -911,10 +905,105 @@ func (s *KeeperTestSuite) setUpPools() { // Create a cosmwasm pool for testing // Pool 51 - s.PrepareCosmWasmPool() + cwPool := s.PrepareCosmWasmPool() - // Create a concentrated liquidity pool for range testing + // Add the new cosmwasm pool to the pool info + poolInfo := types.DefaultPoolTypeInfo + poolInfo.Cosmwasm.WeightMaps = []types.WeightMap{ + { + ContractAddress: cwPool.GetContractAddress(), + Weight: 4, + }, + } + s.App.ProtoRevKeeper.SetInfoByPoolType(s.Ctx, poolInfo) + + // Create a duplicate pool for testing // Pool 52 + s.createGAMMPool( + []balancer.PoolAsset{ + { + Token: sdk.NewCoin("Atom", sdk.NewInt(10000)), + Weight: sdk.NewInt(1), + }, + { + Token: sdk.NewCoin(types.OsmosisDenomination, sdk.NewInt(10000)), + Weight: sdk.NewInt(1), + }, + }, + sdk.NewDecWithPrec(2, 3), + sdk.NewDecWithPrec(0, 2), + ) + + // Create a duplicate pool for testing + // Pool 53 + s.createGAMMPool( + []balancer.PoolAsset{ + { + Token: sdk.NewCoin("usdc", sdk.NewInt(10000)), + Weight: sdk.NewInt(1), + }, + { + Token: sdk.NewCoin(types.OsmosisDenomination, sdk.NewInt(10000)), + Weight: sdk.NewInt(1), + }, + }, + sdk.NewDecWithPrec(2, 3), + sdk.NewDecWithPrec(0, 2), + ) + + // Create a duplicate pool for testing + // Pool 54 + s.createGAMMPool( + []balancer.PoolAsset{ + { + Token: sdk.NewCoin("stake", sdk.NewInt(10000)), + Weight: sdk.NewInt(1), + }, + { + Token: sdk.NewCoin(types.OsmosisDenomination, sdk.NewInt(10000)), + Weight: sdk.NewInt(1), + }, + }, + sdk.NewDecWithPrec(2, 3), + sdk.NewDecWithPrec(0, 2), + ) + + // Create a duplicate pool for testing + // Pool 55 + s.createGAMMPool( + []balancer.PoolAsset{ + { + Token: sdk.NewCoin("stake", sdk.NewInt(100000000)), + Weight: sdk.NewInt(1), + }, + { + Token: sdk.NewCoin(types.OsmosisDenomination, sdk.NewInt(1000000000)), + Weight: sdk.NewInt(1), + }, + }, + sdk.NewDecWithPrec(2, 3), + sdk.NewDecWithPrec(0, 2), + ) + + // Create a duplicate pool for testing + // Pool 56 + s.createGAMMPool( + []balancer.PoolAsset{ + { + Token: sdk.NewCoin("bitcoin", sdk.NewInt(100)), + Weight: sdk.NewInt(1), + }, + { + Token: sdk.NewCoin("Atom", sdk.NewInt(100)), + Weight: sdk.NewInt(1), + }, + }, + sdk.NewDecWithPrec(2, 3), + sdk.NewDecWithPrec(0, 2), + ) + + // Create a concentrated liquidity pool for range testing + // Pool 58 // Create the CL pool clPool := s.PrepareCustomConcentratedPool(s.TestAccs[0], "epochTwo", "uosmo", apptesting.DefaultTickSpacing, sdk.ZeroDec()) fundCoins := sdk.NewCoins(sdk.NewCoin("epochTwo", sdk.NewInt(10_000_000_000_000)), sdk.NewCoin("uosmo", sdk.NewInt(10_000_000_000_000))) @@ -922,7 +1011,7 @@ func (s *KeeperTestSuite) setUpPools() { s.CreateFullRangePosition(clPool, fundCoins) // Create a concentrated liquidity pool for range testing - // Pool 53 + // Pool 59 // Create the CL pool clPool = s.PrepareCustomConcentratedPool(s.TestAccs[0], "epochTwo", "uosmo", apptesting.DefaultTickSpacing, sdk.ZeroDec()) fundCoins = sdk.NewCoins(sdk.NewCoin("epochTwo", sdk.NewInt(2_000_000_000)), sdk.NewCoin("uosmo", sdk.NewInt(1_000_000_000))) @@ -948,9 +1037,9 @@ func (s *KeeperTestSuite) CreateCLPoolAndArbRouteWith_28000_Ticks() { upperTick := int64(100) for i := int64(0); i < 14000; i++ { - _, _, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, clPool.GetId(), s.TestAccs[2], tokensProvided, amount0Min, amount1Min, lowerTick-(100*i), upperTick-(100*i)) + _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, clPool.GetId(), s.TestAccs[2], tokensProvided, amount0Min, amount1Min, lowerTick-(100*i), upperTick-(100*i)) s.Require().NoError(err) - _, _, _, _, _, _, err = s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, clPool.GetId(), s.TestAccs[2], tokensProvided, amount0Min, amount1Min, lowerTick+(100*i), upperTick+(100*i)) + _, err = s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, clPool.GetId(), s.TestAccs[2], tokensProvided, amount0Min, amount1Min, lowerTick+(100*i), upperTick+(100*i)) s.Require().NoError(err) } diff --git a/x/protorev/keeper/msg_server.go b/x/protorev/keeper/msg_server.go index 04ece97a5b5..d8a2fa53e26 100644 --- a/x/protorev/keeper/msg_server.go +++ b/x/protorev/keeper/msg_server.go @@ -114,10 +114,9 @@ func (m MsgServer) SetMaxPoolPointsPerBlock(c context.Context, msg *types.MsgSet return &types.MsgSetMaxPoolPointsPerBlockResponse{}, nil } -// SetPoolWeights sets the weights corresponding to each pool type. This distinction is necessary because the -// pool types have different execution times. Each weight roughly corresponds to the amount of time (in ms) it takes -// to simulate and execute a trade. -func (m MsgServer) SetPoolWeights(c context.Context, msg *types.MsgSetPoolWeights) (*types.MsgSetPoolWeightsResponse, error) { +// SetInfoByPoolType sets the execution time/gas consumption parameters corresponding to each pool type. +// This distinction is necessary because the pool types have different execution times / gas consumption. +func (m MsgServer) SetInfoByPoolType(c context.Context, msg *types.MsgSetInfoByPoolType) (*types.MsgSetInfoByPoolTypeResponse, error) { ctx := sdk.UnwrapSDKContext(c) // Ensure the account has the admin role and can make the tx @@ -125,9 +124,9 @@ func (m MsgServer) SetPoolWeights(c context.Context, msg *types.MsgSetPoolWeight return nil, err } - m.k.SetPoolWeights(ctx, msg.PoolWeights) + m.k.SetInfoByPoolType(ctx, msg.InfoByPoolType) - return &types.MsgSetPoolWeightsResponse{}, nil + return &types.MsgSetInfoByPoolTypeResponse{}, nil } // SetBaseDenoms sets the base denoms that will be used to generate cyclic arbitrage routes diff --git a/x/protorev/keeper/msg_server_test.go b/x/protorev/keeper/msg_server_test.go index 9ca1a591079..103a751e87d 100644 --- a/x/protorev/keeper/msg_server_test.go +++ b/x/protorev/keeper/msg_server_test.go @@ -465,23 +465,23 @@ func (s *KeeperTestSuite) TestMsgSetMaxPoolPointsPerBlock() { } } -// TestMsgSetPoolWeights tests the MsgSetPoolWeights message. -func (s *KeeperTestSuite) TestMsgSetPoolWeights() { +// TestMsgSetPoolTypeInfo tests the MsgSetInfoByPoolType message. +func (s *KeeperTestSuite) TestMsgSetPoolTypeInfo() { cases := []struct { description string admin string - poolWeights types.PoolWeights + poolInfo types.InfoByPoolType passValidateBasic bool pass bool }{ { "Invalid message (invalid admin)", "admin", - types.PoolWeights{ - StableWeight: 1, - BalancerWeight: 2, - ConcentratedWeight: 3, - CosmwasmWeight: 4, + types.InfoByPoolType{ + Stable: types.StablePoolInfo{Weight: 1}, + Balancer: types.BalancerPoolInfo{Weight: 1}, + Concentrated: types.ConcentratedPoolInfo{Weight: 1, MaxTicksCrossed: 1}, + Cosmwasm: types.CosmwasmPoolInfo{WeightMaps: nil}, }, false, false, @@ -489,11 +489,11 @@ func (s *KeeperTestSuite) TestMsgSetPoolWeights() { { "Invalid message (invalid pool weight)", s.adminAccount.String(), - types.PoolWeights{ - StableWeight: 0, - BalancerWeight: 2, - ConcentratedWeight: 1, - CosmwasmWeight: 4, + types.InfoByPoolType{ + Stable: types.StablePoolInfo{Weight: 0}, + Balancer: types.BalancerPoolInfo{Weight: 1}, + Concentrated: types.ConcentratedPoolInfo{Weight: 1, MaxTicksCrossed: 1}, + Cosmwasm: types.CosmwasmPoolInfo{WeightMaps: nil}, }, false, false, @@ -501,9 +501,10 @@ func (s *KeeperTestSuite) TestMsgSetPoolWeights() { { "Invalid message (unset pool weight)", s.adminAccount.String(), - types.PoolWeights{ - StableWeight: 1, - CosmwasmWeight: 4, + types.InfoByPoolType{ + Stable: types.StablePoolInfo{Weight: 1}, + Concentrated: types.ConcentratedPoolInfo{Weight: 1, MaxTicksCrossed: 1}, + Cosmwasm: types.CosmwasmPoolInfo{WeightMaps: nil}, }, false, false, @@ -511,11 +512,11 @@ func (s *KeeperTestSuite) TestMsgSetPoolWeights() { { "Invalid message (wrong admin)", apptesting.CreateRandomAccounts(1)[0].String(), - types.PoolWeights{ - StableWeight: 1, - BalancerWeight: 2, - ConcentratedWeight: 3, - CosmwasmWeight: 4, + types.InfoByPoolType{ + Stable: types.StablePoolInfo{Weight: 1}, + Balancer: types.BalancerPoolInfo{Weight: 1}, + Concentrated: types.ConcentratedPoolInfo{Weight: 1, MaxTicksCrossed: 1}, + Cosmwasm: types.CosmwasmPoolInfo{WeightMaps: nil}, }, true, false, @@ -523,11 +524,11 @@ func (s *KeeperTestSuite) TestMsgSetPoolWeights() { { "Valid message (correct admin)", s.adminAccount.String(), - types.PoolWeights{ - StableWeight: 1, - BalancerWeight: 2, - ConcentratedWeight: 3, - CosmwasmWeight: 4, + types.InfoByPoolType{ + Stable: types.StablePoolInfo{Weight: 1}, + Balancer: types.BalancerPoolInfo{Weight: 1}, + Concentrated: types.ConcentratedPoolInfo{Weight: 1, MaxTicksCrossed: 1}, + Cosmwasm: types.CosmwasmPoolInfo{WeightMaps: nil}, }, true, true, @@ -536,7 +537,7 @@ func (s *KeeperTestSuite) TestMsgSetPoolWeights() { for _, testCase := range cases { s.Run(testCase.description, func() { - msg := types.NewMsgSetPoolWeights(testCase.admin, testCase.poolWeights) + msg := types.NewMsgSetPoolTypeInfo(testCase.admin, testCase.poolInfo) err := msg.ValidateBasic() if testCase.passValidateBasic { @@ -548,14 +549,14 @@ func (s *KeeperTestSuite) TestMsgSetPoolWeights() { server := keeper.NewMsgServer(*s.App.AppKeepers.ProtoRevKeeper) wrappedCtx := sdk.WrapSDKContext(s.Ctx) - response, err := server.SetPoolWeights(wrappedCtx, msg) + response, err := server.SetInfoByPoolType(wrappedCtx, msg) if testCase.pass { s.Require().NoError(err) - s.Require().Equal(response, &types.MsgSetPoolWeightsResponse{}) + s.Require().Equal(response, &types.MsgSetInfoByPoolTypeResponse{}) - poolWeights := s.App.AppKeepers.ProtoRevKeeper.GetPoolWeights(s.Ctx) + poolWeights := s.App.AppKeepers.ProtoRevKeeper.GetInfoByPoolType(s.Ctx) s.Require().NoError(err) - s.Require().Equal(testCase.poolWeights, poolWeights) + s.Require().Equal(testCase.poolInfo, poolWeights) } else { s.Require().Error(err) } diff --git a/x/protorev/keeper/posthandler_test.go b/x/protorev/keeper/posthandler_test.go index c0d3bc0e261..c2f43c9b198 100644 --- a/x/protorev/keeper/posthandler_test.go +++ b/x/protorev/keeper/posthandler_test.go @@ -220,7 +220,7 @@ func (s *KeeperTestSuite) TestAnteHandle() { expectPass: true, }, { - name: "Two Pool Arb Route - Hot Route Build", + name: "Two Pool Arb Route", params: param{ trades: []types.Trade{ { @@ -235,16 +235,12 @@ func (s *KeeperTestSuite) TestAnteHandle() { Denom: "Atom", Amount: sdk.NewInt(15_767_231), }, - { - Denom: "ibc/0CD3A0285E1341859B5E86B6AB7682F023D03E97607CCC1DC95706411D866DF7", - Amount: sdk.NewInt(218_149_058), - }, { Denom: types.OsmosisDenomination, - Amount: sdk.NewInt(56_609_900), + Amount: sdk.NewInt(256_086_256), }, }, - expectedPoolPoints: 33, + expectedPoolPoints: 41, }, expectPass: true, }, @@ -264,16 +260,12 @@ func (s *KeeperTestSuite) TestAnteHandle() { Denom: "Atom", Amount: sdk.NewInt(15_767_231), }, - { - Denom: "ibc/0CD3A0285E1341859B5E86B6AB7682F023D03E97607CCC1DC95706411D866DF7", - Amount: sdk.NewInt(218_149_058), - }, { Denom: types.OsmosisDenomination, - Amount: sdk.NewInt(56_609_900), + Amount: sdk.NewInt(256_086_256), }, }, - expectedPoolPoints: 33, + expectedPoolPoints: 41, }, expectPass: true, }, @@ -293,16 +285,12 @@ func (s *KeeperTestSuite) TestAnteHandle() { Denom: "Atom", Amount: sdk.NewInt(15_767_231), }, - { - Denom: "ibc/0CD3A0285E1341859B5E86B6AB7682F023D03E97607CCC1DC95706411D866DF7", - Amount: sdk.NewInt(218_149_058), - }, { Denom: types.OsmosisDenomination, - Amount: sdk.NewInt(56_609_900), + Amount: sdk.NewInt(256_086_256), }, }, - expectedPoolPoints: 33, + expectedPoolPoints: 41, }, expectPass: true, }, @@ -322,16 +310,12 @@ func (s *KeeperTestSuite) TestAnteHandle() { Denom: "Atom", Amount: sdk.NewInt(15_767_231), }, - { - Denom: "ibc/0CD3A0285E1341859B5E86B6AB7682F023D03E97607CCC1DC95706411D866DF7", - Amount: sdk.NewInt(218_149_058), - }, { Denom: types.OsmosisDenomination, - Amount: sdk.NewInt(56_609_900), + Amount: sdk.NewInt(256_086_256), }, }, - expectedPoolPoints: 33, + expectedPoolPoints: 41, }, expectPass: true, }, @@ -342,7 +326,6 @@ func (s *KeeperTestSuite) TestAnteHandle() { s.Require().NoError(err) err = s.App.ProtoRevKeeper.SetMaxPointsPerBlock(s.Ctx, 100) s.Require().NoError(err) - s.App.ProtoRevKeeper.SetPoolWeights(s.Ctx, types.PoolWeights{StableWeight: 5, BalancerWeight: 2, ConcentratedWeight: 2}) for _, tc := range tests { s.Run(tc.name, func() { @@ -704,7 +687,6 @@ func setUpBenchmarkSuite(msgs []sdk.Msg) (*KeeperTestSuite, authsigning.Tx, sdk. s.Ctx = s.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) err := s.App.ProtoRevKeeper.SetMaxPointsPerTx(s.Ctx, 40) s.Require().NoError(err) - s.App.ProtoRevKeeper.SetPoolWeights(s.Ctx, types.PoolWeights{StableWeight: 5, BalancerWeight: 2, ConcentratedWeight: 2}) // Init a new account and fund it with tokens for gas fees priv0, _, addr0 := testdata.KeyTestPubAddr() diff --git a/x/protorev/keeper/protorev.go b/x/protorev/keeper/protorev.go index fac66ce78fe..531dec98313 100644 --- a/x/protorev/keeper/protorev.go +++ b/x/protorev/keeper/protorev.go @@ -118,7 +118,7 @@ func (k Keeper) DeleteBaseDenoms(ctx sdk.Context) { k.DeleteAllEntriesForKeyPrefix(ctx, types.KeyPrefixBaseDenoms) } -// GetPoolForDenomPair returns the id of the highest liquidty pool between the base denom and the denom to match +// GetPoolForDenomPair returns the id of the highest liquidity pool between the base denom and the denom to match func (k Keeper) GetPoolForDenomPair(ctx sdk.Context, baseDenom, denomToMatch string) (uint64, error) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixDenomPairToPool) key := types.GetKeyPrefixDenomPairToPool(baseDenom, denomToMatch) @@ -448,17 +448,17 @@ func (k Keeper) SetMaxPointsPerBlock(ctx sdk.Context, maxPoints uint64) error { return nil } -// GetPoolWeights retrieves the weights of different pool types. The weight of a pool type roughly -// corresponds to the amount of time it will take to simulate and execute a swap on that pool type (in ms). -func (k Keeper) GetPoolWeights(ctx sdk.Context) types.PoolWeights { - store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixPoolWeights) - poolWeights := &types.PoolWeights{} - osmoutils.MustGet(store, types.KeyPrefixPoolWeights, poolWeights) +// GetInfoByPoolType retrieves the metadata about the different pool types. This is used to determine the execution costs of +// different pool types when calculating the optimal route (in terms of time and gas consumption). +func (k Keeper) GetInfoByPoolType(ctx sdk.Context) types.InfoByPoolType { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixInfoByPoolType) + poolWeights := &types.InfoByPoolType{} + osmoutils.MustGet(store, types.KeyPrefixInfoByPoolType, poolWeights) return *poolWeights } -// SetPoolWeights sets the weights of different pool types. -func (k Keeper) SetPoolWeights(ctx sdk.Context, poolWeights types.PoolWeights) { - store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixPoolWeights) - osmoutils.MustSet(store, types.KeyPrefixPoolWeights, &poolWeights) +// SetInfoByPoolType sets the pool type information. +func (k Keeper) SetInfoByPoolType(ctx sdk.Context, poolWeights types.InfoByPoolType) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixInfoByPoolType) + osmoutils.MustSet(store, types.KeyPrefixInfoByPoolType, &poolWeights) } diff --git a/x/protorev/keeper/protorev_test.go b/x/protorev/keeper/protorev_test.go index d2cae88e96f..5168e2ac7cf 100644 --- a/x/protorev/keeper/protorev_test.go +++ b/x/protorev/keeper/protorev_test.go @@ -305,21 +305,20 @@ func (s *KeeperTestSuite) TestGetMaxPointsPerBlock() { s.Require().Error(err) } -// TestGetPoolWeights tests the GetPoolWeights and SetPoolWeights functions. -func (s *KeeperTestSuite) TestGetPoolWeights() { - // Should be initialized on genesis - poolWeights := s.App.ProtoRevKeeper.GetPoolWeights(s.Ctx) - s.Require().Equal(types.PoolWeights{StableWeight: 5, BalancerWeight: 2, ConcentratedWeight: 2}, poolWeights) - - // Should be able to set the PoolWeights - newRouteWeights := types.PoolWeights{ - StableWeight: 10, - BalancerWeight: 2, - ConcentratedWeight: 22, +// TestGetInfoByPoolType tests the GetInfoByPoolType and SetInfoByPoolType functions. +func (s *KeeperTestSuite) TestGetInfoByPoolType() { + // Should be able to set the InfoByPoolType + newRouteWeights := types.DefaultPoolTypeInfo + newRouteWeights.Balancer.Weight = 100 + newRouteWeights.Cosmwasm.WeightMaps = []types.WeightMap{ + { + ContractAddress: "contractAddress", + Weight: 1, + }, } - s.App.ProtoRevKeeper.SetPoolWeights(s.Ctx, newRouteWeights) + s.App.ProtoRevKeeper.SetInfoByPoolType(s.Ctx, newRouteWeights) - poolWeights = s.App.ProtoRevKeeper.GetPoolWeights(s.Ctx) + poolWeights := s.App.ProtoRevKeeper.GetInfoByPoolType(s.Ctx) s.Require().Equal(newRouteWeights, poolWeights) } diff --git a/x/protorev/keeper/rebalance.go b/x/protorev/keeper/rebalance.go index f1df9655f2f..084fabcc375 100644 --- a/x/protorev/keeper/rebalance.go +++ b/x/protorev/keeper/rebalance.go @@ -207,6 +207,8 @@ func (k Keeper) CalculateUpperBoundForSearch( ) (sdk.Int, error) { var intermidiateCoin sdk.Coin + poolInfo := k.GetInfoByPoolType(ctx) + // Iterate through all CL pools and determine the maximal amount of input that can be used // respecting the max ticks moved. for index := route.Route.Length() - 1; index >= 0; index-- { @@ -230,7 +232,7 @@ func (k Keeper) CalculateUpperBoundForSearch( ctx, pool.GetId(), tokenInDenom, - types.MaxTicksCrossed, + poolInfo.Concentrated.MaxTicksCrossed, ) if err != nil { return sdk.ZeroInt(), err @@ -247,14 +249,14 @@ func (k Keeper) CalculateUpperBoundForSearch( // In the scenario where there are multiple CL pools in a route, we select the one that inputs // the smaller amount to ensure we do not overstep the max ticks moved. - intermidiateCoin, err = k.executeSafeSwap(ctx, pool.GetId(), intermidiateCoin, tokenInDenom, route.StepSize) + intermidiateCoin, err = k.executeSafeSwap(ctx, pool.GetId(), intermidiateCoin, tokenInDenom) if err != nil { return sdk.ZeroInt(), err } case !intermidiateCoin.IsNil(): // If we have already seen a CL pool in the route, then simply propagate the intermediate coin up // the route. - intermidiateCoin, err = k.executeSafeSwap(ctx, pool.GetId(), intermidiateCoin, tokenInDenom, route.StepSize) + intermidiateCoin, err = k.executeSafeSwap(ctx, pool.GetId(), intermidiateCoin, tokenInDenom) if err != nil { return sdk.ZeroInt(), err } @@ -282,7 +284,6 @@ func (k Keeper) executeSafeSwap( poolID uint64, outputCoin sdk.Coin, tokenInDenom string, - stepSize sdk.Int, ) (sdk.Coin, error) { liquidity, err := k.poolmanagerKeeper.GetTotalPoolLiquidity(ctx, poolID) if err != nil { diff --git a/x/protorev/keeper/rebalance_test.go b/x/protorev/keeper/rebalance_test.go index 1acd4ffb703..e0bf7735252 100644 --- a/x/protorev/keeper/rebalance_test.go +++ b/x/protorev/keeper/rebalance_test.go @@ -181,11 +181,11 @@ var clPoolRouteExtended = poolmanagertypes.SwapAmountInRoutes{ // Tests multiple CL pools in the same route var clPoolRouteMulti = poolmanagertypes.SwapAmountInRoutes{ poolmanagertypes.SwapAmountInRoute{ - PoolId: 52, + PoolId: 57, TokenOutDenom: "uosmo", }, poolmanagertypes.SwapAmountInRoute{ - PoolId: 53, + PoolId: 58, TokenOutDenom: "epochTwo", }, } @@ -197,7 +197,7 @@ var clPoolRoute = poolmanagertypes.SwapAmountInRoutes{ TokenOutDenom: "uosmo", }, poolmanagertypes.SwapAmountInRoute{ - PoolId: 52, + PoolId: 57, TokenOutDenom: "epochTwo", }, } diff --git a/x/protorev/keeper/routes.go b/x/protorev/keeper/routes.go index 404b6576ee8..c0f8c3c9fdc 100644 --- a/x/protorev/keeper/routes.go +++ b/x/protorev/keeper/routes.go @@ -102,6 +102,10 @@ func (k Keeper) BuildHighestLiquidityRoutes(ctx sdk.Context, tokenIn, tokenOut s if newRoute, err := k.BuildHighestLiquidityRoute(ctx, baseDenom, tokenIn, tokenOut, poolId); err == nil { routes = append(routes, newRoute) } + + if newRoute, err := k.BuildTwoPoolRoute(ctx, baseDenom, tokenIn, tokenOut, poolId); err == nil { + routes = append(routes, newRoute) + } } return routes, nil @@ -149,11 +153,75 @@ func (k Keeper) BuildHighestLiquidityRoute(ctx sdk.Context, swapDenom types.Base }, nil } +// BuildTwoPoolRoute will attempt to create a two pool route that will rebalance pools that are paired +// with the base denom. This is useful for pools that contain the same assets but are imbalanced. +func (k Keeper) BuildTwoPoolRoute( + ctx sdk.Context, + baseDenom types.BaseDenom, + tokenInDenom, tokenOutDenom string, + poolId uint64, +) (RouteMetaData, error) { + if baseDenom.Denom != tokenInDenom && baseDenom.Denom != tokenOutDenom { + return RouteMetaData{}, fmt.Errorf("base denom (%s) must be either tokenIn (%s) or tokenOut (%s)", baseDenom.Denom, tokenInDenom, tokenOutDenom) + } + + var ( + pool1, pool2 uint64 + ) + + // In the case where the base denom is the swap out, the base denom becomes more ~expensive~ on the current pool id + // and potentially cheaper on the highest liquidity pool. So we swap first on the current pool id and then on the + // highest liquidity pool. + if tokenOutDenom == baseDenom.Denom { + highestLiquidityPool, err := k.GetPoolForDenomPair(ctx, baseDenom.Denom, tokenInDenom) + if err != nil { + return RouteMetaData{}, err + } + + pool1, pool2 = poolId, highestLiquidityPool + tokenOutDenom = tokenInDenom + } else { + highestLiquidityPool, err := k.GetPoolForDenomPair(ctx, baseDenom.Denom, tokenOutDenom) + if err != nil { + return RouteMetaData{}, err + } + + pool1, pool2 = highestLiquidityPool, poolId + } + + if pool1 == pool2 { + return RouteMetaData{}, fmt.Errorf("cannot be trading on the same pool twice") + } + + newRoute := poolmanagertypes.SwapAmountInRoutes{ + poolmanagertypes.SwapAmountInRoute{ + TokenOutDenom: tokenOutDenom, + PoolId: pool1, + }, + poolmanagertypes.SwapAmountInRoute{ + TokenOutDenom: baseDenom.Denom, + PoolId: pool2, + }, + } + + // Check that the route is valid and update the number of pool points that this route will consume when simulating and executing trades + routePoolPoints, err := k.CalculateRoutePoolPoints(ctx, newRoute) + if err != nil { + return RouteMetaData{}, err + } + + return RouteMetaData{ + Route: newRoute, + PoolPoints: routePoolPoints, + StepSize: baseDenom.StepSize, + }, nil +} + // CalculateRoutePoolPoints calculates the number of pool points that will be consumed by a route when simulating and executing trades. This // is only added to the global pool point counter if the route simulated is minimally profitable i.e. it will make a profit. func (k Keeper) CalculateRoutePoolPoints(ctx sdk.Context, route poolmanagertypes.SwapAmountInRoutes) (uint64, error) { // Calculate the number of pool points this route will consume - poolWeights := k.GetPoolWeights(ctx) + infoByPoolType := k.GetInfoByPoolType(ctx) totalWeight := uint64(0) for _, poolId := range route.PoolIds() { @@ -169,13 +237,25 @@ func (k Keeper) CalculateRoutePoolPoints(ctx sdk.Context, route poolmanagertypes switch pool.GetType() { case poolmanagertypes.Balancer: - totalWeight += poolWeights.BalancerWeight + totalWeight += infoByPoolType.Balancer.Weight case poolmanagertypes.Stableswap: - totalWeight += poolWeights.StableWeight + totalWeight += infoByPoolType.Stable.Weight case poolmanagertypes.Concentrated: - totalWeight += poolWeights.ConcentratedWeight + totalWeight += infoByPoolType.Concentrated.Weight case poolmanagertypes.CosmWasm: - totalWeight += poolWeights.CosmwasmWeight + weight, ok := uint64(0), false + for _, weightMap := range infoByPoolType.Cosmwasm.WeightMaps { + if weightMap.ContractAddress == pool.GetAddress().String() { + weight = weightMap.Weight + ok = true + break + } + } + if !ok { + return 0, fmt.Errorf("cosmwasm pool %d does not have a weight", poolId) + } + + totalWeight += weight default: return 0, fmt.Errorf("invalid pool type") } diff --git a/x/protorev/keeper/routes_test.go b/x/protorev/keeper/routes_test.go index 70ac96276a0..6e7b9676720 100644 --- a/x/protorev/keeper/routes_test.go +++ b/x/protorev/keeper/routes_test.go @@ -44,13 +44,17 @@ func (s *KeeperTestSuite) TestBuildRoutes() { description: "Route exists for swap in Bitcoin and swap out Atom", inputDenom: "bitcoin", outputDenom: "Atom", - poolID: 4, + poolID: 55, expectedRoutes: [][]TestRoute{ { {PoolId: 25, InputDenom: types.OsmosisDenomination, OutputDenom: "Atom"}, - {PoolId: 4, InputDenom: "Atom", OutputDenom: "bitcoin"}, + {PoolId: 55, InputDenom: "Atom", OutputDenom: "bitcoin"}, {PoolId: 10, InputDenom: "bitcoin", OutputDenom: types.OsmosisDenomination}, }, + { + {PoolId: 55, InputDenom: "Atom", OutputDenom: "bitcoin"}, + {PoolId: 4, InputDenom: "bitcoin", OutputDenom: "Atom"}, + }, }, }, { @@ -91,6 +95,22 @@ func (s *KeeperTestSuite) TestBuildRoutes() { }, }, }, + { + description: "Two Pool Route exists for (osmo, atom)", + inputDenom: "Atom", + outputDenom: types.OsmosisDenomination, + poolID: 51, + expectedRoutes: [][]TestRoute{ + { + {PoolId: 51, InputDenom: types.OsmosisDenomination, OutputDenom: "Atom"}, + {PoolId: 25, InputDenom: "Atom", OutputDenom: types.OsmosisDenomination}, + }, + { + {PoolId: 25, InputDenom: "Atom", OutputDenom: types.OsmosisDenomination}, + {PoolId: 51, InputDenom: types.OsmosisDenomination, OutputDenom: "Atom"}, + }, + }, + }, } for _, tc := range cases { @@ -101,6 +121,7 @@ func (s *KeeperTestSuite) TestBuildRoutes() { for routeIndex, route := range routes { for tradeIndex, poolID := range route.Route.PoolIds() { s.Require().Equal(tc.expectedRoutes[routeIndex][tradeIndex].PoolId, poolID) + s.Require().Equal(tc.expectedRoutes[routeIndex][tradeIndex].OutputDenom, route.Route[tradeIndex].TokenOutDenom) } } }) @@ -199,11 +220,7 @@ func (s *KeeperTestSuite) TestBuildHighestLiquidityRoute() { for _, tc := range cases { s.Run(tc.description, func() { - s.App.ProtoRevKeeper.SetPoolWeights(s.Ctx, types.PoolWeights{ - StableWeight: 5, - BalancerWeight: 2, - ConcentratedWeight: 2, - }) + s.App.ProtoRevKeeper.SetInfoByPoolType(s.Ctx, types.DefaultPoolTypeInfo) baseDenom := types.BaseDenom{ Denom: tc.swapDenom, @@ -225,6 +242,99 @@ func (s *KeeperTestSuite) TestBuildHighestLiquidityRoute() { } } +// TestBuildTwoPoolRoute tests the BuildTwoPoolRoute function +func (s *KeeperTestSuite) TestBuildTwoPoolRoute() { + cases := []struct { + description string + swapDenom types.BaseDenom + tokenIn string + tokenOut string + poolId uint64 + expectedRoute []TestRoute + hasRoute bool + }{ + { + description: "two pool route can be created with base as token out", + swapDenom: types.BaseDenom{ + Denom: types.OsmosisDenomination, + StepSize: sdk.NewInt(1_000_000), + }, + tokenIn: "stake", + tokenOut: types.OsmosisDenomination, + poolId: 54, + expectedRoute: []TestRoute{ + {PoolId: 54, InputDenom: types.OsmosisDenomination, OutputDenom: "stake"}, + {PoolId: 55, InputDenom: "stake", OutputDenom: types.OsmosisDenomination}, + }, + hasRoute: true, + }, + { + description: "two pool route can be created with base as token in", + swapDenom: types.BaseDenom{ + Denom: types.OsmosisDenomination, + StepSize: sdk.NewInt(1_000_000), + }, + tokenIn: types.OsmosisDenomination, + tokenOut: "stake", + poolId: 54, + expectedRoute: []TestRoute{ + {PoolId: 55, InputDenom: types.OsmosisDenomination, OutputDenom: "stake"}, + {PoolId: 54, InputDenom: "stake", OutputDenom: types.OsmosisDenomination}, + }, + hasRoute: true, + }, + { + description: "two pool route where swap is on the highest liquidity pool", + swapDenom: types.BaseDenom{ + Denom: types.OsmosisDenomination, + StepSize: sdk.NewInt(1_000_000), + }, + tokenIn: "stake", + tokenOut: types.OsmosisDenomination, + poolId: 55, + expectedRoute: []TestRoute{}, + hasRoute: false, + }, + { + description: "trade executes on pool not tracked by the module", + swapDenom: types.BaseDenom{ + Denom: types.OsmosisDenomination, + StepSize: sdk.NewInt(1_000_000), + }, + tokenIn: "stake", + tokenOut: types.OsmosisDenomination, + poolId: 1000000, + expectedRoute: []TestRoute{}, + hasRoute: false, + }, + } + + for _, tc := range cases { + s.Run(tc.description, func() { + routeMetaData, err := s.App.ProtoRevKeeper.BuildTwoPoolRoute( + s.Ctx, + tc.swapDenom, + tc.tokenIn, + tc.tokenOut, + tc.poolId, + ) + + if tc.hasRoute { + s.Require().NoError(err) + s.Require().Equal(len(tc.expectedRoute), len(routeMetaData.Route.PoolIds())) + + for index, trade := range tc.expectedRoute { + s.Require().Equal(trade.PoolId, routeMetaData.Route[index].PoolId) + s.Require().Equal(trade.OutputDenom, routeMetaData.Route[index].TokenOutDenom) + } + } else { + s.Require().Equal(len(tc.expectedRoute), len(routeMetaData.Route.PoolIds())) + s.Require().Error(err) + } + }) + } +} + // TestBuildHotRoutes tests the BuildHotRoutes function func (s *KeeperTestSuite) TestBuildHotRoutes() { cases := []struct { @@ -274,11 +384,7 @@ func (s *KeeperTestSuite) TestBuildHotRoutes() { for _, tc := range cases { s.Run(tc.description, func() { - s.App.ProtoRevKeeper.SetPoolWeights(s.Ctx, types.PoolWeights{ - StableWeight: 5, - BalancerWeight: 2, - ConcentratedWeight: 2, - }) + s.App.ProtoRevKeeper.SetInfoByPoolType(s.Ctx, types.DefaultPoolTypeInfo) routes, err := s.App.ProtoRevKeeper.BuildHotRoutes(s.Ctx, tc.swapIn, tc.swapOut, tc.poolId) @@ -332,13 +438,13 @@ func (s *KeeperTestSuite) TestCalculateRoutePoolPoints() { { description: "Valid route containing only stable swap pools", route: []poolmanagertypes.SwapAmountInRoute{{PoolId: 40, TokenOutDenom: ""}, {PoolId: 40, TokenOutDenom: ""}, {PoolId: 40, TokenOutDenom: ""}}, - expectedRoutePoolPoints: 9, + expectedRoutePoolPoints: 15, expectedPass: true, }, { description: "Valid route with more than 3 hops", route: []poolmanagertypes.SwapAmountInRoute{{PoolId: 40, TokenOutDenom: ""}, {PoolId: 40, TokenOutDenom: ""}, {PoolId: 40, TokenOutDenom: ""}, {PoolId: 1, TokenOutDenom: ""}}, - expectedRoutePoolPoints: 11, + expectedRoutePoolPoints: 17, expectedPass: true, }, { @@ -356,7 +462,7 @@ func (s *KeeperTestSuite) TestCalculateRoutePoolPoints() { { description: "Valid route with cw pool, balancer, stable swap and cl pool", route: []poolmanagertypes.SwapAmountInRoute{{PoolId: 1, TokenOutDenom: ""}, {PoolId: 51, TokenOutDenom: ""}, {PoolId: 40, TokenOutDenom: ""}, {PoolId: 50, TokenOutDenom: ""}}, - expectedRoutePoolPoints: 10, + expectedRoutePoolPoints: 18, expectedPass: true, }, } @@ -364,12 +470,8 @@ func (s *KeeperTestSuite) TestCalculateRoutePoolPoints() { for _, tc := range cases { s.Run(tc.description, func() { s.SetupTest() - s.App.ProtoRevKeeper.SetPoolWeights(s.Ctx, types.PoolWeights{ - StableWeight: 3, - BalancerWeight: 2, - ConcentratedWeight: 1, - CosmwasmWeight: 4, - }) + s.Require().NoError(s.App.ProtoRevKeeper.SetMaxPointsPerTx(s.Ctx, 25)) + s.Require().NoError(s.App.ProtoRevKeeper.SetMaxPointsPerBlock(s.Ctx, 100)) routePoolPoints, err := s.App.ProtoRevKeeper.CalculateRoutePoolPoints(s.Ctx, tc.route) if tc.expectedPass { diff --git a/x/protorev/types/codec.go b/x/protorev/types/codec.go index ea8308a2e43..c5bf7fe4c5b 100644 --- a/x/protorev/types/codec.go +++ b/x/protorev/types/codec.go @@ -20,7 +20,7 @@ const ( setDeveloperAccount = "osmosis/MsgSetDeveloperAccount" setMaxPoolPointsPerTx = "osmosis/MsgSetMaxPoolPointsPerTx" setMaxPoolPointsPerBlock = "osmosis/MsgSetMaxPoolPointsPerBlock" - setPoolWeights = "osmosis/MsgSetPoolWeights" + setInfoByPoolType = "osmosis/MsgSetInfoByPoolType" setBaseDenoms = "osmosis/MsgSetBaseDenoms" // proposals @@ -40,7 +40,7 @@ func RegisterCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgSetDeveloperAccount{}, setDeveloperAccount, nil) cdc.RegisterConcrete(&MsgSetMaxPoolPointsPerTx{}, setMaxPoolPointsPerTx, nil) cdc.RegisterConcrete(&MsgSetMaxPoolPointsPerBlock{}, setMaxPoolPointsPerBlock, nil) - cdc.RegisterConcrete(&MsgSetPoolWeights{}, setPoolWeights, nil) + cdc.RegisterConcrete(&MsgSetInfoByPoolType{}, setInfoByPoolType, nil) cdc.RegisterConcrete(&MsgSetBaseDenoms{}, setBaseDenoms, nil) // proposals @@ -55,7 +55,7 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { &MsgSetDeveloperAccount{}, &MsgSetMaxPoolPointsPerTx{}, &MsgSetMaxPoolPointsPerBlock{}, - &MsgSetPoolWeights{}, + &MsgSetInfoByPoolType{}, &MsgSetBaseDenoms{}, ) diff --git a/x/protorev/types/constants.go b/x/protorev/types/constants.go index ffdc56c2a7f..23c0cbc7772 100644 --- a/x/protorev/types/constants.go +++ b/x/protorev/types/constants.go @@ -27,9 +27,8 @@ const MaxPoolPointsPerTx uint64 = 50 // to the maximum execution time (in ms) of protorev per block const MaxPoolPointsPerBlock uint64 = 200 -// Max number of ticks we can move in a concentrated pool swap. This will be parameterized in a -// follow up PR. -const MaxTicksCrossed uint64 = 5 +// Max number of ticks we can move in a concentrated pool swap. +const MaxTicksCrossed uint64 = 10 // ---------------- Module Profit Splitting Constants ---------------- // diff --git a/x/protorev/types/genesis.go b/x/protorev/types/genesis.go index cb7466f5ce0..aa6672a2fe5 100644 --- a/x/protorev/types/genesis.go +++ b/x/protorev/types/genesis.go @@ -16,14 +16,18 @@ var ( StepSize: sdk.NewInt(1_000_000), }, } - DefaultPoolWeights = PoolWeights{ - StableWeight: 5, // it takes around 5 ms to simulate and execute a stable swap - BalancerWeight: 2, // it takes around 2 ms to simulate and execute a balancer swap - ConcentratedWeight: 2, // it takes around 2 ms to simulate and execute a concentrated swap - - // TODO: This is a temporary weight until we can get a more accurate weight for cosmwasm swaps - // ref: https://github.com/osmosis-labs/osmosis/issues/5858 - CosmwasmWeight: 5, // it takes around 5 ms to simulate and execute a cosmwasm swap + DefaultPoolTypeInfo = InfoByPoolType{ + Balancer: BalancerPoolInfo{ + Weight: 2, // it takes around 2 ms to simulate and execute a balancer swap + }, + Stable: StablePoolInfo{ + Weight: 5, // it takes around 5 ms to simulate and execute a stable swap + }, + Concentrated: ConcentratedPoolInfo{ + Weight: 7, // it takes around 7 ms to simulate and execute a concentrated swap + MaxTicksCrossed: 5, + }, + Cosmwasm: CosmwasmPoolInfo{}, } DefaultDaysSinceModuleGenesis = uint64(0) DefaultDeveloperFees = []sdk.Coin{} @@ -41,7 +45,7 @@ func DefaultGenesis() *GenesisState { Params: DefaultParams(), TokenPairArbRoutes: DefaultTokenPairArbRoutes, BaseDenoms: DefaultBaseDenoms, - PoolWeights: DefaultPoolWeights, + InfoByPoolType: DefaultPoolTypeInfo, DaysSinceModuleGenesis: DefaultDaysSinceModuleGenesis, DeveloperFees: DefaultDeveloperFees, DeveloperAddress: DefaultDeveloperAddress, @@ -65,8 +69,8 @@ func (gs GenesisState) Validate() error { return err } - // Validate the pool weights - if err := gs.PoolWeights.Validate(); err != nil { + // Validate the pool type information + if err := gs.InfoByPoolType.Validate(); err != nil { return err } diff --git a/x/protorev/types/genesis.pb.go b/x/protorev/types/genesis.pb.go index b6f6fded674..a3e31189ba1 100644 --- a/x/protorev/types/genesis.pb.go +++ b/x/protorev/types/genesis.pb.go @@ -36,6 +36,9 @@ type GenesisState struct { BaseDenoms []BaseDenom `protobuf:"bytes,3,rep,name=base_denoms,json=baseDenoms,proto3" json:"base_denoms" yaml:"base_denoms"` // The pool weights that are being used to calculate the weight (compute cost) // of each route. + // + // DEPRECATED: This field is deprecated and will be removed in the next + // release. It is replaced by the `info_by_pool_type` field. PoolWeights PoolWeights `protobuf:"bytes,4,opt,name=pool_weights,json=poolWeights,proto3" json:"pool_weights" yaml:"pool_weights"` // The number of days since module genesis. DaysSinceModuleGenesis uint64 `protobuf:"varint,5,opt,name=days_since_module_genesis,json=daysSinceModuleGenesis,proto3" json:"days_since_module_genesis,omitempty" yaml:"days_since_module_genesis"` @@ -55,6 +58,9 @@ type GenesisState struct { PointCountForBlock uint64 `protobuf:"varint,11,opt,name=point_count_for_block,json=pointCountForBlock,proto3" json:"point_count_for_block,omitempty" yaml:"point_count_for_block"` // All of the profits that have been accumulated by the module. Profits []types.Coin `protobuf:"bytes,12,rep,name=profits,proto3" json:"profits" yaml:"profits"` + // Information that is used to estimate execution time / gas + // consumption of a swap on a given pool type. + InfoByPoolType InfoByPoolType `protobuf:"bytes,13,opt,name=info_by_pool_type,json=infoByPoolType,proto3" json:"info_by_pool_type" yaml:"info_by_pool_type"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -174,6 +180,13 @@ func (m *GenesisState) GetProfits() []types.Coin { return nil } +func (m *GenesisState) GetInfoByPoolType() InfoByPoolType { + if m != nil { + return m.InfoByPoolType + } + return InfoByPoolType{} +} + func init() { proto.RegisterType((*GenesisState)(nil), "osmosis.protorev.v1beta1.GenesisState") } @@ -183,50 +196,53 @@ func init() { } var fileDescriptor_3c77fc2da5752af2 = []byte{ - // 688 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x94, 0xcf, 0x6e, 0xd3, 0x4a, - 0x14, 0xc6, 0xe3, 0xdb, 0xde, 0xfe, 0x99, 0xf4, 0x56, 0xb7, 0xd3, 0x9b, 0xca, 0xc9, 0xa5, 0x8e, - 0x31, 0x2d, 0x64, 0x41, 0x6d, 0xb5, 0x20, 0x21, 0xb1, 0x40, 0xaa, 0x8b, 0x0a, 0x12, 0xa2, 0x8a, - 0xa6, 0x45, 0x48, 0x20, 0x31, 0x8c, 0x93, 0x69, 0x6a, 0xd5, 0xf6, 0x58, 0x9e, 0x49, 0x48, 0x1f, - 0x80, 0x3d, 0x0f, 0xc3, 0x43, 0x74, 0x59, 0xb1, 0x62, 0x15, 0xa1, 0x76, 0xc1, 0x3e, 0x4f, 0x80, - 0x3c, 0x33, 0x49, 0x4a, 0x89, 0x61, 0x97, 0x39, 0xe7, 0x77, 0xbe, 0xef, 0x9c, 0x99, 0xe3, 0x80, - 0xbb, 0x8c, 0xc7, 0x8c, 0x87, 0xdc, 0x4b, 0x33, 0x26, 0x58, 0x46, 0x7b, 0x5e, 0x6f, 0x3b, 0xa0, - 0x82, 0x6c, 0x7b, 0x1d, 0x9a, 0x50, 0x1e, 0x72, 0x57, 0x26, 0xa0, 0xa9, 0x39, 0x77, 0xc4, 0xb9, - 0x9a, 0xab, 0xfd, 0xd7, 0x61, 0x1d, 0x26, 0xa3, 0x5e, 0xfe, 0x4b, 0x01, 0xb5, 0x7b, 0x85, 0xba, - 0x63, 0x01, 0x05, 0x6e, 0x16, 0x83, 0x24, 0x23, 0xb1, 0x36, 0xac, 0x55, 0x5b, 0x92, 0xc3, 0xca, - 0x48, 0x1d, 0x74, 0xca, 0x52, 0x27, 0x2f, 0x20, 0x9c, 0x8e, 0x8b, 0x5b, 0x2c, 0x4c, 0x54, 0xde, - 0xf9, 0xbe, 0x00, 0x96, 0x9e, 0xa9, 0x61, 0x0e, 0x05, 0x11, 0x14, 0x3e, 0x01, 0x73, 0x4a, 0xdb, - 0x34, 0x6c, 0xa3, 0x51, 0xde, 0xb1, 0xdd, 0xa2, 0xe1, 0xdc, 0xa6, 0xe4, 0xfc, 0xd9, 0xf3, 0x41, - 0xbd, 0x84, 0x74, 0x15, 0xfc, 0x68, 0x80, 0x8a, 0x60, 0xa7, 0x34, 0xc1, 0x29, 0x09, 0x33, 0x4c, - 0xb2, 0x00, 0x67, 0xac, 0x2b, 0x28, 0x37, 0xff, 0xb2, 0x67, 0x1a, 0xe5, 0x9d, 0xfb, 0xc5, 0x7a, - 0x47, 0x79, 0x59, 0x93, 0x84, 0xd9, 0x6e, 0x16, 0x20, 0x59, 0xe3, 0x6f, 0xe4, 0xda, 0xc3, 0x41, - 0xfd, 0xd6, 0x19, 0x89, 0xa3, 0xc7, 0xce, 0x54, 0x61, 0x07, 0x41, 0xf1, 0x4b, 0x25, 0x7c, 0x0f, - 0xca, 0xf9, 0xcc, 0xb8, 0x4d, 0x13, 0x16, 0x73, 0x73, 0x46, 0x9a, 0xdf, 0x29, 0x36, 0xf7, 0x09, - 0xa7, 0x4f, 0x73, 0xd6, 0xaf, 0x69, 0x4f, 0xa8, 0x3c, 0xaf, 0xa9, 0x38, 0x08, 0x04, 0x23, 0x8c, - 0x43, 0x0a, 0x96, 0x52, 0xc6, 0x22, 0xfc, 0x81, 0x86, 0x9d, 0x13, 0xc1, 0xcd, 0x59, 0x79, 0x5f, - 0x9b, 0xbf, 0xb9, 0x2f, 0xc6, 0xa2, 0xd7, 0x0a, 0xf6, 0xff, 0xd7, 0x26, 0xab, 0xca, 0xe4, 0xba, - 0x90, 0x83, 0xca, 0xe9, 0x84, 0x84, 0x18, 0x54, 0xdb, 0xe4, 0x8c, 0x63, 0x1e, 0x26, 0x2d, 0x8a, - 0x63, 0xd6, 0xee, 0x46, 0x14, 0xeb, 0xfd, 0x33, 0xff, 0xb6, 0x8d, 0xc6, 0xac, 0xbf, 0x31, 0x1c, - 0xd4, 0x6d, 0x25, 0x54, 0x88, 0x3a, 0x68, 0x2d, 0xcf, 0x1d, 0xe6, 0xa9, 0x97, 0x32, 0xa3, 0x9f, - 0x1d, 0x62, 0xb0, 0xdc, 0xa6, 0x3d, 0x1a, 0xb1, 0x94, 0x66, 0xf8, 0x98, 0x52, 0x6e, 0xce, 0xc9, - 0xcb, 0xaa, 0xba, 0x7a, 0x93, 0xf2, 0x99, 0xc7, 0x43, 0xec, 0xb1, 0x30, 0xf1, 0xd7, 0x75, 0xf7, - 0x15, 0x6d, 0xfa, 0x53, 0xb9, 0x83, 0xfe, 0x19, 0x07, 0xf6, 0x29, 0xe5, 0xf0, 0x00, 0xac, 0x46, - 0x44, 0x50, 0x2e, 0x70, 0x10, 0xb1, 0xd6, 0x29, 0x3e, 0x91, 0x93, 0x99, 0xf3, 0xb2, 0x77, 0x6b, - 0x38, 0xa8, 0xd7, 0x94, 0xcc, 0x14, 0xc8, 0x41, 0x2b, 0x2a, 0xea, 0xe7, 0xc1, 0xe7, 0x32, 0x06, - 0xdf, 0x82, 0x95, 0x89, 0x23, 0x69, 0xb7, 0x33, 0xca, 0xb9, 0xb9, 0x60, 0x1b, 0x8d, 0x45, 0xdf, - 0x1d, 0x0e, 0xea, 0xe6, 0xcd, 0xa6, 0x34, 0xe2, 0x7c, 0xf9, 0xbc, 0xb5, 0xac, 0x47, 0xda, 0x55, - 0x21, 0xf4, 0xef, 0x98, 0xd2, 0x11, 0xf8, 0x0e, 0x54, 0x63, 0xd2, 0xc7, 0xf2, 0x41, 0x52, 0x16, - 0x26, 0x82, 0xe3, 0x5c, 0x43, 0x36, 0x65, 0x2e, 0xde, 0xbc, 0xee, 0x42, 0xd4, 0x41, 0x95, 0x98, - 0xf4, 0xf3, 0x17, 0x6f, 0xca, 0x4c, 0x93, 0x66, 0x72, 0x04, 0xf8, 0x0a, 0xac, 0x4d, 0x2b, 0x12, - 0x7d, 0x13, 0x48, 0xf1, 0xdb, 0xc3, 0x41, 0x7d, 0xbd, 0x58, 0x5c, 0xf4, 0x1d, 0x04, 0x6f, 0x2a, - 0x1f, 0xf5, 0xe1, 0x21, 0xa8, 0x48, 0x0a, 0xb7, 0x58, 0x37, 0x11, 0xf8, 0x98, 0x8d, 0x5a, 0x2e, - 0x4b, 0x55, 0x7b, 0xf2, 0x0d, 0x4d, 0xc5, 0x1c, 0x04, 0x65, 0x7c, 0x2f, 0x0f, 0xef, 0x33, 0xdd, - 0xeb, 0x0b, 0x30, 0x9f, 0x66, 0xec, 0x38, 0x14, 0xdc, 0x5c, 0xfa, 0xd3, 0x4a, 0xac, 0xe9, 0x95, - 0x58, 0xd6, 0x2e, 0xaa, 0xce, 0x41, 0x23, 0x05, 0xff, 0xe0, 0xfc, 0xd2, 0x32, 0x2e, 0x2e, 0x2d, - 0xe3, 0xdb, 0xa5, 0x65, 0x7c, 0xba, 0xb2, 0x4a, 0x17, 0x57, 0x56, 0xe9, 0xeb, 0x95, 0x55, 0x7a, - 0xf3, 0xb0, 0x13, 0x8a, 0x93, 0x6e, 0xe0, 0xb6, 0x58, 0xec, 0xe9, 0x8f, 0x67, 0x2b, 0x22, 0x01, - 0x1f, 0x1d, 0xbc, 0xde, 0xf6, 0x23, 0xaf, 0x3f, 0xf9, 0x0f, 0x14, 0x67, 0x29, 0xe5, 0xc1, 0x9c, - 0x3c, 0x3f, 0xf8, 0x11, 0x00, 0x00, 0xff, 0xff, 0xf2, 0x2a, 0x10, 0x89, 0xa5, 0x05, 0x00, 0x00, + // 728 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x94, 0xcd, 0x6e, 0xd3, 0x4a, + 0x14, 0xc7, 0xe3, 0xdb, 0xde, 0xf6, 0x76, 0xd2, 0x46, 0xb7, 0xd3, 0x9b, 0xca, 0xc9, 0xa5, 0x8e, + 0x31, 0x2d, 0x64, 0x41, 0x6d, 0xb5, 0x20, 0x21, 0xb1, 0x40, 0xaa, 0x8b, 0x0a, 0x08, 0x51, 0x45, + 0x6e, 0x11, 0x12, 0x48, 0x0c, 0xe3, 0x64, 0x92, 0x5a, 0xb5, 0x3d, 0x96, 0x67, 0x12, 0x92, 0x07, + 0x60, 0xcf, 0xc3, 0xf0, 0x10, 0x5d, 0x56, 0xac, 0x58, 0x45, 0xa8, 0x7d, 0x83, 0x3c, 0x01, 0xf2, + 0xcc, 0x24, 0xe9, 0x47, 0x0c, 0x3b, 0xcf, 0x39, 0xbf, 0xf3, 0xff, 0x9f, 0x33, 0x1f, 0x06, 0xf7, + 0x29, 0x8b, 0x28, 0x0b, 0x98, 0x93, 0xa4, 0x94, 0xd3, 0x94, 0xf4, 0x9c, 0xde, 0x8e, 0x4f, 0x38, + 0xde, 0x71, 0x3a, 0x24, 0x26, 0x2c, 0x60, 0xb6, 0x48, 0x40, 0x5d, 0x71, 0xf6, 0x98, 0xb3, 0x15, + 0x57, 0xfd, 0xaf, 0x43, 0x3b, 0x54, 0x44, 0x9d, 0xec, 0x4b, 0x02, 0xd5, 0x07, 0xb9, 0xba, 0x13, + 0x01, 0x09, 0x6e, 0xe5, 0x83, 0x38, 0xc5, 0x91, 0x32, 0xac, 0x56, 0x9a, 0x82, 0x43, 0xd2, 0x48, + 0x2e, 0x54, 0xca, 0x90, 0x2b, 0xc7, 0xc7, 0x8c, 0x4c, 0x8a, 0x9b, 0x34, 0x88, 0x65, 0xde, 0x1a, + 0x2e, 0x81, 0xe5, 0x17, 0x72, 0x98, 0x23, 0x8e, 0x39, 0x81, 0xcf, 0xc0, 0x82, 0xd4, 0xd6, 0x35, + 0x53, 0xab, 0x17, 0x77, 0x4d, 0x3b, 0x6f, 0x38, 0xbb, 0x21, 0x38, 0x77, 0xfe, 0x6c, 0x58, 0x2b, + 0x78, 0xaa, 0x0a, 0x7e, 0xd1, 0x40, 0x99, 0xd3, 0x53, 0x12, 0xa3, 0x04, 0x07, 0x29, 0xc2, 0xa9, + 0x8f, 0x52, 0xda, 0xe5, 0x84, 0xe9, 0x7f, 0x99, 0x73, 0xf5, 0xe2, 0xee, 0xc3, 0x7c, 0xbd, 0xe3, + 0xac, 0xac, 0x81, 0x83, 0x74, 0x2f, 0xf5, 0x3d, 0x51, 0xe3, 0x6e, 0x66, 0xda, 0xa3, 0x61, 0xed, + 0xce, 0x00, 0x47, 0xe1, 0x53, 0x6b, 0xa6, 0xb0, 0xe5, 0x41, 0x7e, 0xab, 0x12, 0x7e, 0x02, 0xc5, + 0x6c, 0x66, 0xd4, 0x22, 0x31, 0x8d, 0x98, 0x3e, 0x27, 0xcc, 0xef, 0xe5, 0x9b, 0xbb, 0x98, 0x91, + 0xe7, 0x19, 0xeb, 0x56, 0x95, 0x27, 0x94, 0x9e, 0x57, 0x54, 0x2c, 0x0f, 0xf8, 0x63, 0x8c, 0x41, + 0x02, 0x96, 0x13, 0x4a, 0x43, 0xf4, 0x99, 0x04, 0x9d, 0x13, 0xce, 0xf4, 0x79, 0xb1, 0x5f, 0x5b, + 0xbf, 0xd9, 0x2f, 0x4a, 0xc3, 0x77, 0x12, 0x76, 0xff, 0x57, 0x26, 0x6b, 0xd2, 0xe4, 0xaa, 0x90, + 0xe5, 0x15, 0x93, 0x29, 0x09, 0x11, 0xa8, 0xb4, 0xf0, 0x80, 0x21, 0x16, 0xc4, 0x4d, 0x82, 0x22, + 0xda, 0xea, 0x86, 0x04, 0xa9, 0xfb, 0xa7, 0xff, 0x6d, 0x6a, 0xf5, 0x79, 0x77, 0x73, 0x34, 0xac, + 0x99, 0x52, 0x28, 0x17, 0xb5, 0xbc, 0xf5, 0x2c, 0x77, 0x94, 0xa5, 0xde, 0x88, 0x8c, 0x3a, 0x76, + 0x88, 0x40, 0xa9, 0x45, 0x7a, 0x24, 0xa4, 0x09, 0x49, 0x51, 0x9b, 0x10, 0xa6, 0x2f, 0x88, 0xcd, + 0xaa, 0xd8, 0xea, 0x26, 0x65, 0x33, 0x4f, 0x86, 0xd8, 0xa7, 0x41, 0xec, 0x6e, 0xa8, 0xee, 0xcb, + 0xca, 0xf4, 0x5a, 0xb9, 0xe5, 0xad, 0x4c, 0x02, 0x07, 0x84, 0x30, 0x78, 0x08, 0xd6, 0x42, 0xcc, + 0x09, 0xe3, 0xc8, 0x0f, 0x69, 0xf3, 0x14, 0x9d, 0x88, 0xc9, 0xf4, 0x45, 0xd1, 0xbb, 0x31, 0x1a, + 0xd6, 0xaa, 0x52, 0x66, 0x06, 0x64, 0x79, 0xab, 0x32, 0xea, 0x66, 0xc1, 0x97, 0x22, 0x06, 0x3f, + 0x80, 0xd5, 0xa9, 0x23, 0x6e, 0xb5, 0x52, 0xc2, 0x98, 0xfe, 0x8f, 0xa9, 0xd5, 0x97, 0x5c, 0x7b, + 0x34, 0xac, 0xe9, 0x37, 0x9b, 0x52, 0x88, 0xf5, 0xfd, 0xdb, 0x76, 0x49, 0x8d, 0xb4, 0x27, 0x43, + 0xde, 0xbf, 0x13, 0x4a, 0x45, 0xe0, 0x47, 0x50, 0x89, 0x70, 0x1f, 0x89, 0x03, 0x49, 0x68, 0x10, + 0x73, 0x86, 0x32, 0x0d, 0xd1, 0x94, 0xbe, 0x74, 0x73, 0xbb, 0x73, 0x51, 0xcb, 0x2b, 0x47, 0xb8, + 0x9f, 0x9d, 0x78, 0x43, 0x64, 0x1a, 0x24, 0x15, 0x23, 0xc0, 0xb7, 0x60, 0x7d, 0x56, 0x11, 0xef, + 0xeb, 0x40, 0x88, 0xdf, 0x1d, 0x0d, 0x6b, 0x1b, 0xf9, 0xe2, 0xbc, 0x6f, 0x79, 0xf0, 0xa6, 0xf2, + 0x71, 0x1f, 0x1e, 0x81, 0xb2, 0xa0, 0x50, 0x93, 0x76, 0x63, 0x8e, 0xda, 0x74, 0xdc, 0x72, 0x51, + 0xa8, 0x9a, 0xd3, 0x37, 0x34, 0x13, 0xb3, 0x3c, 0x28, 0xe2, 0xfb, 0x59, 0xf8, 0x80, 0xaa, 0x5e, + 0x5f, 0x83, 0xc5, 0x24, 0xa5, 0xed, 0x80, 0x33, 0x7d, 0xf9, 0x4f, 0x57, 0x62, 0x5d, 0x5d, 0x89, + 0x92, 0x72, 0x91, 0x75, 0x96, 0x37, 0x56, 0x80, 0x5d, 0xb0, 0x1a, 0xc4, 0x6d, 0x8a, 0xfc, 0x81, + 0x1c, 0x8a, 0x0f, 0x12, 0xa2, 0xaf, 0x88, 0x37, 0x53, 0xcf, 0x7f, 0x33, 0xaf, 0xe2, 0x36, 0x75, + 0x07, 0xd9, 0xb4, 0xc7, 0x83, 0x84, 0xb8, 0xa6, 0x72, 0x51, 0x67, 0x7c, 0x4b, 0xd0, 0xf2, 0x4a, + 0xc1, 0xf5, 0x8a, 0xc3, 0xb3, 0x0b, 0x43, 0x3b, 0xbf, 0x30, 0xb4, 0x9f, 0x17, 0x86, 0xf6, 0xf5, + 0xd2, 0x28, 0x9c, 0x5f, 0x1a, 0x85, 0x1f, 0x97, 0x46, 0xe1, 0xfd, 0xe3, 0x4e, 0xc0, 0x4f, 0xba, + 0xbe, 0xdd, 0xa4, 0x91, 0xa3, 0xfc, 0xb7, 0x43, 0xec, 0xb3, 0xf1, 0xc2, 0xe9, 0xed, 0x3c, 0x71, + 0xfa, 0xd3, 0x5f, 0x6f, 0xa6, 0xcf, 0xfc, 0x05, 0xb1, 0x7e, 0xf4, 0x2b, 0x00, 0x00, 0xff, 0xff, + 0x04, 0xad, 0x0a, 0x11, 0x1c, 0x06, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -249,6 +265,16 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size, err := m.InfoByPoolType.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6a if len(m.Profits) > 0 { for iNdEx := len(m.Profits) - 1; iNdEx >= 0; iNdEx-- { { @@ -424,6 +450,8 @@ func (m *GenesisState) Size() (n int) { n += 1 + l + sovGenesis(uint64(l)) } } + l = m.InfoByPoolType.Size() + n += 1 + l + sovGenesis(uint64(l)) return n } @@ -791,6 +819,39 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InfoByPoolType", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.InfoByPoolType.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) diff --git a/x/protorev/types/keys.go b/x/protorev/types/keys.go index 86aeb24733a..cb5c906f289 100644 --- a/x/protorev/types/keys.go +++ b/x/protorev/types/keys.go @@ -34,7 +34,7 @@ const ( prefixMaxPoolPointsPerBlock prefixPoolPointCountForBlock prefixLatestBlockHeight - prefixPoolWeights + prefixInfoByPoolType prefixSwapsToBackrun ) @@ -84,8 +84,8 @@ var ( // KeyPrefixLatestBlockHeight is the prefix for store that keeps track of the latest recorded block height KeyPrefixLatestBlockHeight = []byte{prefixLatestBlockHeight} - // KeyPrefixPoolWeights is the prefix for store that keeps track of the weights for different pool types - KeyPrefixPoolWeights = []byte{prefixPoolWeights} + // KeyPrefixInfoByPoolType is the prefix for store that keeps track of the pool type info + KeyPrefixInfoByPoolType = []byte{prefixInfoByPoolType} // KeyPrefixSwapsToBackrun is the prefix for store that keeps track of the swaps that need to be backrun for a given tx KeyPrefixSwapsToBackrun = []byte{prefixSwapsToBackrun} diff --git a/x/protorev/types/msg.go b/x/protorev/types/msg.go index 1210405a36c..26cd32a2a6a 100644 --- a/x/protorev/types/msg.go +++ b/x/protorev/types/msg.go @@ -10,7 +10,7 @@ var ( _ sdk.Msg = &MsgSetDeveloperAccount{} _ sdk.Msg = &MsgSetMaxPoolPointsPerTx{} _ sdk.Msg = &MsgSetMaxPoolPointsPerBlock{} - _ sdk.Msg = &MsgSetPoolWeights{} + _ sdk.Msg = &MsgSetInfoByPoolType{} _ sdk.Msg = &MsgSetBaseDenoms{} ) @@ -19,7 +19,7 @@ const ( TypeMsgSetDeveloperAccount = "set_developer_account" TypeMsgSetMaxPoolPointsPerTx = "set_max_pool_points_per_tx" TypeMsgSetMaxPoolPointsPerBlock = "set_max_pool_points_per_block" - TypeMsgSetPoolWeights = "set_pool_weights" + TypeMsgSetPoolTypeInfo = "set_info_by_pool_type" TypeMsgSetBaseDenoms = "set_base_denoms" ) @@ -205,33 +205,33 @@ func (msg MsgSetMaxPoolPointsPerBlock) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{addr} } -// ---------------------- Interface for MsgSetPoolWeights ---------------------- // -// NewMsgSetPoolWeights creates a new MsgSetPoolWeights instance -func NewMsgSetPoolWeights(admin string, poolWeights PoolWeights) *MsgSetPoolWeights { - return &MsgSetPoolWeights{ - Admin: admin, - PoolWeights: poolWeights, +// ---------------------- Interface for MsgSetInfoByPoolType ---------------------- // +// NewMsgSetPoolTypeInfo creates a new MsgSetInfoByPoolType instance +func NewMsgSetPoolTypeInfo(admin string, infoByPoolType InfoByPoolType) *MsgSetInfoByPoolType { + return &MsgSetInfoByPoolType{ + Admin: admin, + InfoByPoolType: infoByPoolType, } } // Route returns the name of the module -func (msg MsgSetPoolWeights) Route() string { +func (msg MsgSetInfoByPoolType) Route() string { return RouterKey } // Type returns the type of the message -func (msg MsgSetPoolWeights) Type() string { - return TypeMsgSetPoolWeights +func (msg MsgSetInfoByPoolType) Type() string { + return TypeMsgSetPoolTypeInfo } -// ValidateBasic validates the MsgSetPoolWeights -func (msg MsgSetPoolWeights) ValidateBasic() error { +// ValidateBasic validates the MsgSetInfoByPoolType +func (msg MsgSetInfoByPoolType) ValidateBasic() error { // Account must be a valid bech32 address if _, err := sdk.AccAddressFromBech32(msg.Admin); err != nil { return errorsmod.Wrap(err, "invalid admin address (must be bech32)") } - if err := msg.PoolWeights.Validate(); err != nil { + if err := msg.InfoByPoolType.Validate(); err != nil { return err } @@ -239,12 +239,12 @@ func (msg MsgSetPoolWeights) ValidateBasic() error { } // GetSignBytes encodes the message for signing -func (msg MsgSetPoolWeights) GetSignBytes() []byte { +func (msg MsgSetInfoByPoolType) GetSignBytes() []byte { return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) } // GetSigners defines whose signature is required -func (msg MsgSetPoolWeights) GetSigners() []sdk.AccAddress { +func (msg MsgSetInfoByPoolType) GetSigners() []sdk.AccAddress { addr := sdk.MustAccAddressFromBech32(msg.Admin) return []sdk.AccAddress{addr} } diff --git a/x/protorev/types/msg_test.go b/x/protorev/types/msg_test.go index 32dcdc40371..93115703be0 100644 --- a/x/protorev/types/msg_test.go +++ b/x/protorev/types/msg_test.go @@ -506,43 +506,79 @@ func TestMsgSetMaxPoolPointsPerBlock(t *testing.T) { } } -func TestMsgSetPoolWeights(t *testing.T) { +func TestMsgSetPoolTypeInfo(t *testing.T) { cases := []struct { - description string - admin string - poolWeights types.PoolWeights - pass bool + description string + admin string + infoByPoolType types.InfoByPoolType + pass bool }{ { "Invalid message (invalid admin)", "admin", - types.PoolWeights{ - BalancerWeight: 1, - StableWeight: 1, - ConcentratedWeight: 1, - CosmwasmWeight: 1, + types.InfoByPoolType{ + Balancer: types.BalancerPoolInfo{Weight: 1}, + Stable: types.StablePoolInfo{Weight: 1}, + Concentrated: types.ConcentratedPoolInfo{Weight: 1, MaxTicksCrossed: 1}, + Cosmwasm: types.CosmwasmPoolInfo{}, }, false, }, { - "Invalid message (invalid pool weights)", + "Invalid message (invalid pool weights for balancer)", createAccount().String(), - types.PoolWeights{ - BalancerWeight: 0, - StableWeight: 1, - ConcentratedWeight: 1, - CosmwasmWeight: 1, + types.InfoByPoolType{ + Balancer: types.BalancerPoolInfo{Weight: 0}, + Stable: types.StablePoolInfo{Weight: 1}, + Concentrated: types.ConcentratedPoolInfo{Weight: 1, MaxTicksCrossed: 1}, + Cosmwasm: types.CosmwasmPoolInfo{}, + }, + false, + }, + { + "Invalid message (invalid pool info for cosmwasm)", + createAccount().String(), + types.InfoByPoolType{ + Balancer: types.BalancerPoolInfo{Weight: 1}, + Stable: types.StablePoolInfo{Weight: 1}, + Concentrated: types.ConcentratedPoolInfo{Weight: 1, MaxTicksCrossed: 1}, + Cosmwasm: types.CosmwasmPoolInfo{ + WeightMaps: []types.WeightMap{ + { + ContractAddress: "contractAddress", + Weight: 1, + }, + }, + }, + }, + false, + }, + { + "Invalid message (invalid pool info for concentrated)", + createAccount().String(), + types.InfoByPoolType{ + Balancer: types.BalancerPoolInfo{Weight: 1}, + Stable: types.StablePoolInfo{Weight: 1}, + Concentrated: types.ConcentratedPoolInfo{Weight: 1}, + Cosmwasm: types.CosmwasmPoolInfo{}, }, false, }, { "Valid message", createAccount().String(), - types.PoolWeights{ - BalancerWeight: 1, - StableWeight: 1, - ConcentratedWeight: 1, - CosmwasmWeight: 1, + types.InfoByPoolType{ + Balancer: types.BalancerPoolInfo{Weight: 1}, + Stable: types.StablePoolInfo{Weight: 1}, + Concentrated: types.ConcentratedPoolInfo{Weight: 1, MaxTicksCrossed: 1}, + Cosmwasm: types.CosmwasmPoolInfo{ + WeightMaps: []types.WeightMap{ + { + ContractAddress: createAccount().String(), + Weight: 1, + }, + }, + }, }, true, }, @@ -550,7 +586,7 @@ func TestMsgSetPoolWeights(t *testing.T) { for _, tc := range cases { t.Run(tc.description, func(t *testing.T) { - msg := types.NewMsgSetPoolWeights(tc.admin, tc.poolWeights) + msg := types.NewMsgSetPoolTypeInfo(tc.admin, tc.infoByPoolType) err := msg.ValidateBasic() if tc.pass { require.NoError(t, err) diff --git a/x/protorev/types/protorev.pb.go b/x/protorev/types/protorev.pb.go index d6409e24beb..2a343ed2ce7 100644 --- a/x/protorev/types/protorev.pb.go +++ b/x/protorev/types/protorev.pb.go @@ -268,6 +268,9 @@ func (m *RouteStatistics) GetRoute() []uint64 { // significantly between the different pool types. Each weight roughly // corresponds to the amount of time (in ms) it takes to execute a swap on that // pool type. +// +// DEPRECATED: This field is deprecated and will be removed in the next +// release. It is replaced by the `info_by_pool_type` field. type PoolWeights struct { // The weight of a stableswap pool StableWeight uint64 `protobuf:"varint,1,opt,name=stable_weight,json=stableWeight,proto3" json:"stable_weight,omitempty" yaml:"stable_weight"` @@ -340,6 +343,332 @@ func (m *PoolWeights) GetCosmwasmWeight() uint64 { return 0 } +// InfoByPoolType contains information pertaining to how expensive (in terms of +// gas and time) it is to execute a swap on a given pool type. This distinction +// is made and necessary because the execution time ranges significantly between +// the different pool types. +type InfoByPoolType struct { + // The stable pool info + Stable StablePoolInfo `protobuf:"bytes,1,opt,name=stable,proto3" json:"stable" yaml:"stable"` + // The balancer pool info + Balancer BalancerPoolInfo `protobuf:"bytes,2,opt,name=balancer,proto3" json:"balancer" yaml:"balancer"` + // The concentrated pool info + Concentrated ConcentratedPoolInfo `protobuf:"bytes,3,opt,name=concentrated,proto3" json:"concentrated" yaml:"concentrated"` + // The cosmwasm pool info + Cosmwasm CosmwasmPoolInfo `protobuf:"bytes,4,opt,name=cosmwasm,proto3" json:"cosmwasm" yaml:"cosmwasm"` +} + +func (m *InfoByPoolType) Reset() { *m = InfoByPoolType{} } +func (m *InfoByPoolType) String() string { return proto.CompactTextString(m) } +func (*InfoByPoolType) ProtoMessage() {} +func (*InfoByPoolType) Descriptor() ([]byte, []int) { + return fileDescriptor_1e9f2391fd9fec01, []int{5} +} +func (m *InfoByPoolType) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *InfoByPoolType) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_InfoByPoolType.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *InfoByPoolType) XXX_Merge(src proto.Message) { + xxx_messageInfo_InfoByPoolType.Merge(m, src) +} +func (m *InfoByPoolType) XXX_Size() int { + return m.Size() +} +func (m *InfoByPoolType) XXX_DiscardUnknown() { + xxx_messageInfo_InfoByPoolType.DiscardUnknown(m) +} + +var xxx_messageInfo_InfoByPoolType proto.InternalMessageInfo + +func (m *InfoByPoolType) GetStable() StablePoolInfo { + if m != nil { + return m.Stable + } + return StablePoolInfo{} +} + +func (m *InfoByPoolType) GetBalancer() BalancerPoolInfo { + if m != nil { + return m.Balancer + } + return BalancerPoolInfo{} +} + +func (m *InfoByPoolType) GetConcentrated() ConcentratedPoolInfo { + if m != nil { + return m.Concentrated + } + return ConcentratedPoolInfo{} +} + +func (m *InfoByPoolType) GetCosmwasm() CosmwasmPoolInfo { + if m != nil { + return m.Cosmwasm + } + return CosmwasmPoolInfo{} +} + +// StablePoolInfo contains meta data pertaining to a stableswap pool type. +type StablePoolInfo struct { + // The weight of a stableswap pool + Weight uint64 `protobuf:"varint,1,opt,name=weight,proto3" json:"weight,omitempty" yaml:"weight"` +} + +func (m *StablePoolInfo) Reset() { *m = StablePoolInfo{} } +func (m *StablePoolInfo) String() string { return proto.CompactTextString(m) } +func (*StablePoolInfo) ProtoMessage() {} +func (*StablePoolInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_1e9f2391fd9fec01, []int{6} +} +func (m *StablePoolInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StablePoolInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StablePoolInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StablePoolInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_StablePoolInfo.Merge(m, src) +} +func (m *StablePoolInfo) XXX_Size() int { + return m.Size() +} +func (m *StablePoolInfo) XXX_DiscardUnknown() { + xxx_messageInfo_StablePoolInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_StablePoolInfo proto.InternalMessageInfo + +func (m *StablePoolInfo) GetWeight() uint64 { + if m != nil { + return m.Weight + } + return 0 +} + +// BalancerPoolInfo contains meta data pertaining to a balancer pool type. +type BalancerPoolInfo struct { + // The weight of a balancer pool + Weight uint64 `protobuf:"varint,1,opt,name=weight,proto3" json:"weight,omitempty" yaml:"weight"` +} + +func (m *BalancerPoolInfo) Reset() { *m = BalancerPoolInfo{} } +func (m *BalancerPoolInfo) String() string { return proto.CompactTextString(m) } +func (*BalancerPoolInfo) ProtoMessage() {} +func (*BalancerPoolInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_1e9f2391fd9fec01, []int{7} +} +func (m *BalancerPoolInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BalancerPoolInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BalancerPoolInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *BalancerPoolInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_BalancerPoolInfo.Merge(m, src) +} +func (m *BalancerPoolInfo) XXX_Size() int { + return m.Size() +} +func (m *BalancerPoolInfo) XXX_DiscardUnknown() { + xxx_messageInfo_BalancerPoolInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_BalancerPoolInfo proto.InternalMessageInfo + +func (m *BalancerPoolInfo) GetWeight() uint64 { + if m != nil { + return m.Weight + } + return 0 +} + +// ConcentratedPoolInfo contains meta data pertaining to a concentrated pool +// type. +type ConcentratedPoolInfo struct { + // The weight of a concentrated pool + Weight uint64 `protobuf:"varint,1,opt,name=weight,proto3" json:"weight,omitempty" yaml:"weight"` + // The maximum number of ticks we can move when rebalancing + MaxTicksCrossed uint64 `protobuf:"varint,2,opt,name=max_ticks_crossed,json=maxTicksCrossed,proto3" json:"max_ticks_crossed,omitempty" yaml:"max_ticks_crossed"` +} + +func (m *ConcentratedPoolInfo) Reset() { *m = ConcentratedPoolInfo{} } +func (m *ConcentratedPoolInfo) String() string { return proto.CompactTextString(m) } +func (*ConcentratedPoolInfo) ProtoMessage() {} +func (*ConcentratedPoolInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_1e9f2391fd9fec01, []int{8} +} +func (m *ConcentratedPoolInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConcentratedPoolInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConcentratedPoolInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ConcentratedPoolInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConcentratedPoolInfo.Merge(m, src) +} +func (m *ConcentratedPoolInfo) XXX_Size() int { + return m.Size() +} +func (m *ConcentratedPoolInfo) XXX_DiscardUnknown() { + xxx_messageInfo_ConcentratedPoolInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_ConcentratedPoolInfo proto.InternalMessageInfo + +func (m *ConcentratedPoolInfo) GetWeight() uint64 { + if m != nil { + return m.Weight + } + return 0 +} + +func (m *ConcentratedPoolInfo) GetMaxTicksCrossed() uint64 { + if m != nil { + return m.MaxTicksCrossed + } + return 0 +} + +// CosmwasmPoolInfo contains meta data pertaining to a cosmwasm pool type. +type CosmwasmPoolInfo struct { + // The weight of a cosmwasm pool (by contract address) + WeightMaps []WeightMap `protobuf:"bytes,1,rep,name=weight_maps,json=weightMaps,proto3" json:"weight_maps" yaml:"weight_maps"` +} + +func (m *CosmwasmPoolInfo) Reset() { *m = CosmwasmPoolInfo{} } +func (m *CosmwasmPoolInfo) String() string { return proto.CompactTextString(m) } +func (*CosmwasmPoolInfo) ProtoMessage() {} +func (*CosmwasmPoolInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_1e9f2391fd9fec01, []int{9} +} +func (m *CosmwasmPoolInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CosmwasmPoolInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CosmwasmPoolInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CosmwasmPoolInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_CosmwasmPoolInfo.Merge(m, src) +} +func (m *CosmwasmPoolInfo) XXX_Size() int { + return m.Size() +} +func (m *CosmwasmPoolInfo) XXX_DiscardUnknown() { + xxx_messageInfo_CosmwasmPoolInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_CosmwasmPoolInfo proto.InternalMessageInfo + +func (m *CosmwasmPoolInfo) GetWeightMaps() []WeightMap { + if m != nil { + return m.WeightMaps + } + return nil +} + +// WeightMap maps a contract address to a weight. The weight of an address +// corresponds to the amount of ms required to execute a swap on that contract. +type WeightMap struct { + // The weight of a cosmwasm pool (by contract address) + Weight uint64 `protobuf:"varint,1,opt,name=weight,proto3" json:"weight,omitempty" yaml:"weight"` + // The contract address + ContractAddress string `protobuf:"bytes,2,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty" yaml:"contract_address"` +} + +func (m *WeightMap) Reset() { *m = WeightMap{} } +func (m *WeightMap) String() string { return proto.CompactTextString(m) } +func (*WeightMap) ProtoMessage() {} +func (*WeightMap) Descriptor() ([]byte, []int) { + return fileDescriptor_1e9f2391fd9fec01, []int{10} +} +func (m *WeightMap) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WeightMap) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_WeightMap.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *WeightMap) XXX_Merge(src proto.Message) { + xxx_messageInfo_WeightMap.Merge(m, src) +} +func (m *WeightMap) XXX_Size() int { + return m.Size() +} +func (m *WeightMap) XXX_DiscardUnknown() { + xxx_messageInfo_WeightMap.DiscardUnknown(m) +} + +var xxx_messageInfo_WeightMap proto.InternalMessageInfo + +func (m *WeightMap) GetWeight() uint64 { + if m != nil { + return m.Weight + } + return 0 +} + +func (m *WeightMap) GetContractAddress() string { + if m != nil { + return m.ContractAddress + } + return "" +} + // BaseDenom represents a single base denom that the module uses for its // arbitrage trades. It contains the denom name alongside the step size of the // binary search that is used to find the optimal swap amount @@ -355,7 +684,7 @@ func (m *BaseDenom) Reset() { *m = BaseDenom{} } func (m *BaseDenom) String() string { return proto.CompactTextString(m) } func (*BaseDenom) ProtoMessage() {} func (*BaseDenom) Descriptor() ([]byte, []int) { - return fileDescriptor_1e9f2391fd9fec01, []int{5} + return fileDescriptor_1e9f2391fd9fec01, []int{11} } func (m *BaseDenom) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -397,6 +726,12 @@ func init() { proto.RegisterType((*Trade)(nil), "osmosis.protorev.v1beta1.Trade") proto.RegisterType((*RouteStatistics)(nil), "osmosis.protorev.v1beta1.RouteStatistics") proto.RegisterType((*PoolWeights)(nil), "osmosis.protorev.v1beta1.PoolWeights") + proto.RegisterType((*InfoByPoolType)(nil), "osmosis.protorev.v1beta1.InfoByPoolType") + proto.RegisterType((*StablePoolInfo)(nil), "osmosis.protorev.v1beta1.StablePoolInfo") + proto.RegisterType((*BalancerPoolInfo)(nil), "osmosis.protorev.v1beta1.BalancerPoolInfo") + proto.RegisterType((*ConcentratedPoolInfo)(nil), "osmosis.protorev.v1beta1.ConcentratedPoolInfo") + proto.RegisterType((*CosmwasmPoolInfo)(nil), "osmosis.protorev.v1beta1.CosmwasmPoolInfo") + proto.RegisterType((*WeightMap)(nil), "osmosis.protorev.v1beta1.WeightMap") proto.RegisterType((*BaseDenom)(nil), "osmosis.protorev.v1beta1.BaseDenom") } @@ -405,50 +740,67 @@ func init() { } var fileDescriptor_1e9f2391fd9fec01 = []byte{ - // 682 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x54, 0xbf, 0x6f, 0xd3, 0x40, - 0x14, 0xce, 0x35, 0xe9, 0x8f, 0x5c, 0xdb, 0xa4, 0xb8, 0xa5, 0xb8, 0x19, 0xec, 0xe8, 0x90, 0x4a, - 0x96, 0xda, 0x0a, 0x20, 0x21, 0x55, 0x62, 0xc0, 0x65, 0xa9, 0x90, 0xda, 0xea, 0x5a, 0x09, 0xc1, - 0x62, 0x9d, 0x9d, 0x6b, 0x6a, 0x35, 0xf1, 0x45, 0xbe, 0x4b, 0x4b, 0xfb, 0x57, 0x30, 0xc0, 0xce, - 0xc6, 0x7f, 0xc1, 0xdc, 0xb1, 0x63, 0xc5, 0x60, 0xa1, 0x76, 0x61, 0xf6, 0xca, 0x82, 0x7c, 0x77, - 0x4e, 0xa2, 0x08, 0x24, 0x18, 0x60, 0xca, 0xbd, 0xef, 0xbd, 0xef, 0x7b, 0xf7, 0xbe, 0x77, 0x31, - 0x7c, 0xc4, 0x78, 0x9f, 0xf1, 0x88, 0xbb, 0x83, 0x84, 0x09, 0x96, 0xd0, 0x33, 0xf7, 0xac, 0x1d, - 0x50, 0x41, 0xda, 0x23, 0xc0, 0x91, 0x07, 0xc3, 0xd4, 0x85, 0xce, 0x08, 0xd7, 0x85, 0x8d, 0x8d, - 0x50, 0xa6, 0x7c, 0x99, 0x70, 0x55, 0xa0, 0xaa, 0x1a, 0x6b, 0x5d, 0xd6, 0x65, 0x0a, 0xcf, 0x4f, - 0x1a, 0xb5, 0x54, 0x8d, 0x1b, 0x10, 0x4e, 0x47, 0xed, 0x42, 0x16, 0xc5, 0x2a, 0x8f, 0x6e, 0x00, - 0x34, 0x8e, 0xd8, 0x29, 0x8d, 0x0f, 0x48, 0x94, 0xbc, 0x48, 0x02, 0xcc, 0x86, 0x82, 0x72, 0xe3, - 0x0d, 0x84, 0x24, 0x09, 0xfc, 0x44, 0x46, 0x26, 0x68, 0x96, 0x5b, 0x8b, 0x8f, 0x6d, 0xe7, 0x77, - 0xd7, 0x72, 0x24, 0xcb, 0xdb, 0xb8, 0x4a, 0xed, 0x52, 0x96, 0xda, 0xf7, 0x2e, 0x48, 0xbf, 0xb7, - 0x8d, 0xc6, 0x02, 0x08, 0x57, 0xc9, 0x48, 0xda, 0x81, 0x0b, 0x22, 0x6f, 0xe8, 0x47, 0xb1, 0x39, - 0xd3, 0x04, 0xad, 0xaa, 0xb7, 0x9a, 0xa5, 0x76, 0x5d, 0x71, 0x8a, 0x0c, 0xc2, 0xf3, 0xf2, 0xb8, - 0x1b, 0x1b, 0x6d, 0x58, 0x55, 0x28, 0x1b, 0x0a, 0xb3, 0x2c, 0x09, 0x6b, 0x59, 0x6a, 0xaf, 0x4c, - 0x12, 0xd8, 0x50, 0x20, 0xac, 0x64, 0xf7, 0x87, 0x62, 0xbb, 0xf2, 0xfd, 0x93, 0x0d, 0xd0, 0x17, - 0x00, 0x67, 0x65, 0x4f, 0x63, 0x0f, 0xce, 0x89, 0x84, 0x74, 0xfe, 0x64, 0x92, 0xa3, 0xbc, 0xce, - 0xbb, 0xaf, 0x27, 0x59, 0xd6, 0x4d, 0x24, 0x19, 0x61, 0xad, 0x62, 0xf8, 0xb0, 0xca, 0x05, 0x1d, - 0xf8, 0x3c, 0xba, 0xa4, 0x7a, 0x06, 0x2f, 0x67, 0x7c, 0x4d, 0xed, 0xcd, 0x6e, 0x24, 0x4e, 0x86, - 0x81, 0x13, 0xb2, 0xbe, 0x5e, 0x8f, 0xfe, 0xd9, 0xe2, 0x9d, 0x53, 0x57, 0x5c, 0x0c, 0x28, 0x77, - 0x76, 0x63, 0x31, 0x1e, 0x60, 0x24, 0x84, 0xf0, 0x42, 0x7e, 0x3e, 0x8c, 0x2e, 0xa9, 0x1e, 0xe0, - 0x23, 0x80, 0xb3, 0xf2, 0x3e, 0xc6, 0x43, 0x58, 0x19, 0x30, 0xd6, 0x33, 0x41, 0x13, 0xb4, 0x2a, - 0x5e, 0x3d, 0x4b, 0xed, 0x45, 0xc5, 0xce, 0x51, 0x84, 0x65, 0xf2, 0xff, 0x19, 0xfb, 0x03, 0xc0, - 0xba, 0x34, 0xf6, 0x50, 0x10, 0x11, 0x71, 0x11, 0x85, 0xdc, 0x78, 0x05, 0xe7, 0x07, 0x09, 0x3b, - 0x8e, 0x44, 0xe1, 0xf1, 0x86, 0xa3, 0x5f, 0x67, 0xfe, 0xf2, 0x46, 0xf6, 0xee, 0xb0, 0x28, 0xf6, - 0xd6, 0xb5, 0xbb, 0x35, 0x3d, 0x83, 0xe2, 0x21, 0x5c, 0x28, 0x18, 0x1c, 0xae, 0xc4, 0xc3, 0x7e, - 0x40, 0x13, 0x9f, 0x1d, 0xfb, 0x7a, 0x73, 0x6a, 0xa2, 0xdd, 0xbf, 0xb6, 0xf9, 0x81, 0x6a, 0x32, - 0xad, 0x87, 0x70, 0x4d, 0x41, 0xfb, 0xc7, 0x47, 0x6a, 0xa9, 0x9b, 0x70, 0x56, 0xbe, 0x56, 0xb3, - 0xdc, 0x2c, 0xb7, 0x2a, 0xde, 0x4a, 0x96, 0xda, 0x4b, 0x8a, 0x2b, 0x61, 0x84, 0x55, 0x1a, 0x7d, - 0x9e, 0x81, 0x8b, 0x07, 0x8c, 0xf5, 0x5e, 0xd3, 0xa8, 0x7b, 0x22, 0xb8, 0xf1, 0x1c, 0x2e, 0x73, - 0x41, 0x82, 0x1e, 0xf5, 0xcf, 0x25, 0xa2, 0x97, 0x64, 0x66, 0xa9, 0xbd, 0x56, 0xac, 0x78, 0x22, - 0x8d, 0xf0, 0x92, 0x8a, 0x15, 0xdf, 0xd8, 0x81, 0xf5, 0x80, 0xf4, 0x48, 0x1c, 0xd2, 0xa4, 0x10, - 0x98, 0x91, 0x02, 0x8d, 0x2c, 0xb5, 0xd7, 0x95, 0xc0, 0x54, 0x01, 0xc2, 0xb5, 0x02, 0xd1, 0x22, - 0xfb, 0x70, 0x35, 0x64, 0x71, 0x48, 0x63, 0x91, 0x10, 0x41, 0x3b, 0x85, 0x50, 0x59, 0x0a, 0x59, - 0x59, 0x6a, 0x37, 0x94, 0xd0, 0x2f, 0x8a, 0x10, 0x36, 0x26, 0xd1, 0xf1, 0xad, 0x72, 0x3f, 0xcf, - 0x09, 0xef, 0x17, 0x62, 0x95, 0xe9, 0x5b, 0x4d, 0x15, 0x20, 0x5c, 0x2b, 0x10, 0x25, 0x82, 0x3e, - 0x00, 0x58, 0xf5, 0x08, 0xa7, 0x2f, 0x69, 0xcc, 0xfa, 0xb9, 0xbf, 0x9d, 0xfc, 0x20, 0xfd, 0xa9, - 0x4e, 0xfa, 0x2b, 0x61, 0x84, 0x55, 0xfa, 0x9f, 0xff, 0xb9, 0xbc, 0xbd, 0xab, 0x5b, 0x0b, 0x5c, - 0xdf, 0x5a, 0xe0, 0xdb, 0xad, 0x05, 0xde, 0xdf, 0x59, 0xa5, 0xeb, 0x3b, 0xab, 0x74, 0x73, 0x67, - 0x95, 0xde, 0x3e, 0x9d, 0xd0, 0xd7, 0x5f, 0x88, 0xad, 0x1e, 0x09, 0x78, 0x11, 0xb8, 0x67, 0xed, - 0x67, 0xee, 0xbb, 0xf1, 0xe7, 0x5b, 0x76, 0x0c, 0xe6, 0x64, 0xfc, 0xe4, 0x67, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x25, 0xcc, 0x13, 0x2b, 0xdf, 0x05, 0x00, 0x00, + // 950 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0xcf, 0x6f, 0x1b, 0x45, + 0x14, 0xce, 0xc6, 0x4e, 0x1a, 0x8f, 0x53, 0xdb, 0x99, 0x84, 0xd6, 0x71, 0x91, 0x37, 0x9a, 0x4a, + 0xc5, 0x20, 0x75, 0xad, 0x04, 0x24, 0xa4, 0xa2, 0x1e, 0xba, 0x46, 0x88, 0x08, 0xd1, 0x54, 0x13, + 0x4b, 0x15, 0x5c, 0x96, 0xd9, 0xf5, 0x24, 0x5d, 0xc5, 0xbb, 0x63, 0xed, 0x8c, 0xf3, 0xa3, 0x07, + 0xfe, 0x01, 0x2e, 0x1c, 0xe0, 0xc6, 0x81, 0x1b, 0xff, 0x05, 0xe7, 0x1e, 0x7b, 0xac, 0x38, 0xac, + 0x50, 0x72, 0xe1, 0xbc, 0x57, 0x2e, 0x68, 0xe7, 0xc7, 0xda, 0x71, 0x63, 0x84, 0x0f, 0xf4, 0x94, + 0x99, 0xef, 0x7d, 0xef, 0x7b, 0xf3, 0xbe, 0x37, 0x3b, 0x31, 0xf8, 0x80, 0xf1, 0x88, 0xf1, 0x90, + 0x77, 0x47, 0x09, 0x13, 0x2c, 0xa1, 0xa7, 0xdd, 0xd3, 0x5d, 0x9f, 0x0a, 0xb2, 0x5b, 0x00, 0x8e, + 0x5c, 0xc0, 0xa6, 0x26, 0x3a, 0x05, 0xae, 0x89, 0xad, 0xed, 0x40, 0x86, 0x3c, 0x19, 0xe8, 0xaa, + 0x8d, 0x62, 0xb5, 0xb6, 0x8e, 0xd9, 0x31, 0x53, 0x78, 0xbe, 0xd2, 0x68, 0x5b, 0x71, 0xba, 0x3e, + 0xe1, 0xb4, 0x28, 0x17, 0xb0, 0x30, 0x56, 0x71, 0xf4, 0xc6, 0x02, 0xb0, 0xcf, 0x4e, 0x68, 0xfc, + 0x8c, 0x84, 0xc9, 0x93, 0xc4, 0xc7, 0x6c, 0x2c, 0x28, 0x87, 0xdf, 0x00, 0x40, 0x12, 0xdf, 0x4b, + 0xe4, 0xae, 0x69, 0xed, 0x94, 0x3a, 0xd5, 0x3d, 0xdb, 0x99, 0x77, 0x2c, 0x47, 0x66, 0xb9, 0xdb, + 0xaf, 0x52, 0x7b, 0x29, 0x4b, 0xed, 0x8d, 0x0b, 0x12, 0x0d, 0x1f, 0xa1, 0x89, 0x00, 0xc2, 0x15, + 0x52, 0x48, 0x3b, 0x60, 0x4d, 0xe4, 0x05, 0xbd, 0x30, 0x6e, 0x2e, 0xef, 0x58, 0x9d, 0x8a, 0xbb, + 0x99, 0xa5, 0x76, 0x5d, 0xe5, 0x98, 0x08, 0xc2, 0xb7, 0xe4, 0x72, 0x3f, 0x86, 0xbb, 0xa0, 0xa2, + 0x50, 0x36, 0x16, 0xcd, 0x92, 0x4c, 0xd8, 0xca, 0x52, 0xbb, 0x31, 0x9d, 0xc0, 0xc6, 0x02, 0x61, + 0x25, 0x7b, 0x30, 0x16, 0x8f, 0xca, 0x7f, 0xfd, 0x6a, 0x5b, 0xe8, 0x77, 0x0b, 0xac, 0xc8, 0x9a, + 0xf0, 0x29, 0x58, 0x15, 0x09, 0x19, 0xfc, 0x97, 0x4e, 0xfa, 0x39, 0xcf, 0x7d, 0x4f, 0x77, 0x72, + 0x5b, 0x17, 0x91, 0xc9, 0x08, 0x6b, 0x15, 0xe8, 0x81, 0x0a, 0x17, 0x74, 0xe4, 0xf1, 0xf0, 0x25, + 0xd5, 0x3d, 0xb8, 0x79, 0xc6, 0x1f, 0xa9, 0xfd, 0xe0, 0x38, 0x14, 0x2f, 0xc6, 0xbe, 0x13, 0xb0, + 0x48, 0x8f, 0x47, 0xff, 0x79, 0xc8, 0x07, 0x27, 0x5d, 0x71, 0x31, 0xa2, 0xdc, 0xd9, 0x8f, 0xc5, + 0xa4, 0x81, 0x42, 0x08, 0xe1, 0xb5, 0x7c, 0x7d, 0x18, 0xbe, 0xa4, 0xba, 0x81, 0x9f, 0x2d, 0xb0, + 0x22, 0xcf, 0x03, 0xef, 0x83, 0xf2, 0x88, 0xb1, 0x61, 0xd3, 0xda, 0xb1, 0x3a, 0x65, 0xb7, 0x9e, + 0xa5, 0x76, 0x55, 0x65, 0xe7, 0x28, 0xc2, 0x32, 0xf8, 0xee, 0x8c, 0xfd, 0xdb, 0x02, 0x75, 0x69, + 0xec, 0xa1, 0x20, 0x22, 0xe4, 0x22, 0x0c, 0x38, 0xfc, 0x0a, 0xdc, 0x1a, 0x25, 0xec, 0x28, 0x14, + 0xc6, 0xe3, 0x6d, 0x47, 0xdf, 0xce, 0xfc, 0xe6, 0x15, 0xf6, 0xf6, 0x58, 0x18, 0xbb, 0x77, 0xb4, + 0xbb, 0x35, 0xdd, 0x83, 0xca, 0x43, 0xd8, 0x28, 0x40, 0x0e, 0x1a, 0xf1, 0x38, 0xf2, 0x69, 0xe2, + 0xb1, 0x23, 0x4f, 0x4f, 0x4e, 0x75, 0xb4, 0xbf, 0xb0, 0xcd, 0x77, 0x55, 0x91, 0x59, 0x3d, 0x84, + 0x6b, 0x0a, 0x3a, 0x38, 0xea, 0xab, 0xa1, 0x3e, 0x00, 0x2b, 0xf2, 0xb6, 0x36, 0x4b, 0x3b, 0xa5, + 0x4e, 0xd9, 0x6d, 0x64, 0xa9, 0xbd, 0xae, 0x72, 0x25, 0x8c, 0xb0, 0x0a, 0xa3, 0xdf, 0x96, 0x41, + 0xf5, 0x19, 0x63, 0xc3, 0xe7, 0x34, 0x3c, 0x7e, 0x21, 0x38, 0x7c, 0x0c, 0x6e, 0x73, 0x41, 0xfc, + 0x21, 0xf5, 0xce, 0x24, 0xa2, 0x87, 0xd4, 0xcc, 0x52, 0x7b, 0xcb, 0x8c, 0x78, 0x2a, 0x8c, 0xf0, + 0xba, 0xda, 0xab, 0x7c, 0xd8, 0x03, 0x75, 0x9f, 0x0c, 0x49, 0x1c, 0xd0, 0xc4, 0x08, 0x2c, 0x4b, + 0x81, 0x56, 0x96, 0xda, 0x77, 0x94, 0xc0, 0x0c, 0x01, 0xe1, 0x9a, 0x41, 0xb4, 0xc8, 0x01, 0xd8, + 0x0c, 0x58, 0x1c, 0xd0, 0x58, 0x24, 0x44, 0xd0, 0x81, 0x11, 0x2a, 0x49, 0xa1, 0x76, 0x96, 0xda, + 0x2d, 0x25, 0x74, 0x03, 0x09, 0x61, 0x38, 0x8d, 0x4e, 0x4e, 0x95, 0xfb, 0x79, 0x46, 0x78, 0x64, + 0xc4, 0xca, 0xb3, 0xa7, 0x9a, 0x21, 0x20, 0x5c, 0x33, 0x88, 0x12, 0x41, 0xbf, 0x94, 0x40, 0x6d, + 0x3f, 0x3e, 0x62, 0xee, 0x45, 0xee, 0x57, 0xff, 0x62, 0x44, 0xe1, 0x73, 0xb0, 0xaa, 0xba, 0x97, + 0x2e, 0x55, 0xf7, 0x3a, 0xf3, 0xbf, 0xc4, 0x43, 0xc9, 0xcb, 0x33, 0xa5, 0xc6, 0xcc, 0x27, 0xa9, + 0x54, 0x10, 0xd6, 0x72, 0xd0, 0x03, 0x6b, 0xc6, 0x13, 0xe9, 0x5f, 0x75, 0xef, 0xa3, 0xf9, 0xd2, + 0xae, 0x66, 0x16, 0xe2, 0x77, 0xb5, 0x78, 0xfd, 0xba, 0xdf, 0x08, 0x17, 0xa2, 0x90, 0x81, 0xf5, + 0x69, 0x9f, 0xa4, 0xb7, 0xd5, 0x3d, 0x67, 0x7e, 0x91, 0xde, 0x14, 0xbb, 0x28, 0x74, 0x4f, 0x17, + 0xda, 0x7c, 0x7b, 0x1e, 0x08, 0x5f, 0x2b, 0x90, 0x77, 0x64, 0xfc, 0x94, 0xde, 0xff, 0x6b, 0x47, + 0x3d, 0xcd, 0x9c, 0xd7, 0x91, 0x51, 0x42, 0xb8, 0x10, 0x45, 0x9f, 0x81, 0xda, 0x75, 0x8f, 0xe1, + 0x87, 0x60, 0xf5, 0xda, 0x1d, 0xde, 0x98, 0xf8, 0x6d, 0x66, 0xac, 0x09, 0xe8, 0x31, 0x68, 0xcc, + 0xba, 0xb8, 0x48, 0xfa, 0x0f, 0x16, 0xd8, 0xba, 0xc9, 0xa0, 0x05, 0x34, 0xe0, 0x97, 0x60, 0x23, + 0x22, 0xe7, 0x9e, 0x08, 0x83, 0x13, 0xee, 0x05, 0x09, 0xe3, 0x9c, 0x0e, 0xf4, 0xb7, 0xf3, 0x7e, + 0x96, 0xda, 0x4d, 0x95, 0xf5, 0x16, 0x05, 0xe1, 0x7a, 0x44, 0xce, 0xfb, 0x39, 0xd4, 0xd3, 0x88, + 0x00, 0x8d, 0x59, 0x03, 0xe1, 0x77, 0xa0, 0xaa, 0xea, 0x78, 0x11, 0x19, 0x99, 0x47, 0xed, 0xfe, + 0xfc, 0x09, 0xa8, 0x3b, 0xff, 0x35, 0x19, 0xb9, 0x2d, 0x6d, 0x3d, 0x9c, 0x3e, 0xb6, 0x54, 0x41, + 0x18, 0x9c, 0x19, 0x1a, 0x47, 0xdf, 0x83, 0x4a, 0x91, 0xb4, 0x48, 0xdf, 0x5f, 0x80, 0x46, 0xc0, + 0x72, 0xdf, 0x02, 0xe1, 0x91, 0xc1, 0x20, 0xa1, 0xdc, 0xbc, 0x8e, 0xf7, 0x26, 0xef, 0xdd, 0x2c, + 0x03, 0xe1, 0xba, 0x81, 0x9e, 0x68, 0xe4, 0x27, 0x0b, 0x54, 0x5c, 0xc2, 0xe9, 0xe7, 0x34, 0x66, + 0x51, 0xfe, 0xfc, 0x0d, 0xf2, 0x85, 0xac, 0x5f, 0x99, 0x7e, 0xfe, 0x24, 0x8c, 0xb0, 0x0a, 0xff, + 0xef, 0xff, 0xfb, 0xdc, 0xa7, 0xaf, 0x2e, 0xdb, 0xd6, 0xeb, 0xcb, 0xb6, 0xf5, 0xe7, 0x65, 0xdb, + 0xfa, 0xf1, 0xaa, 0xbd, 0xf4, 0xfa, 0xaa, 0xbd, 0xf4, 0xe6, 0xaa, 0xbd, 0xf4, 0xed, 0x27, 0x53, + 0xfa, 0x7a, 0x0e, 0x0f, 0x87, 0xc4, 0xe7, 0x66, 0xd3, 0x3d, 0xdd, 0xfd, 0xb4, 0x7b, 0x3e, 0xf9, + 0x75, 0x25, 0x2b, 0xfa, 0xab, 0x72, 0xff, 0xf1, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x86, 0x45, + 0x14, 0x25, 0x7e, 0x09, 0x00, 0x00, } func (this *TokenPairArbRoutes) Equal(that interface{}) bool { @@ -796,7 +1148,7 @@ func (m *PoolWeights) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *BaseDenom) Marshal() (dAtA []byte, err error) { +func (m *InfoByPoolType) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -806,66 +1158,290 @@ func (m *BaseDenom) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *BaseDenom) MarshalTo(dAtA []byte) (int, error) { +func (m *InfoByPoolType) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *BaseDenom) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *InfoByPoolType) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l { - size := m.StepSize.Size() + size, err := m.Cosmwasm.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } i -= size - if _, err := m.StepSize.MarshalTo(dAtA[i:]); err != nil { + i = encodeVarintProtorev(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + { + size, err := m.Concentrated.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProtorev(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size, err := m.Balancer.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { return 0, err } + i -= size i = encodeVarintProtorev(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 - if len(m.Denom) > 0 { - i -= len(m.Denom) - copy(dAtA[i:], m.Denom) - i = encodeVarintProtorev(dAtA, i, uint64(len(m.Denom))) - i-- - dAtA[i] = 0xa + { + size, err := m.Stable.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProtorev(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0xa return len(dAtA) - i, nil } -func encodeVarintProtorev(dAtA []byte, offset int, v uint64) int { - offset -= sovProtorev(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ +func (m *StablePoolInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - dAtA[offset] = uint8(v) - return base + return dAtA[:n], nil } -func (m *TokenPairArbRoutes) Size() (n int) { - if m == nil { - return 0 - } + +func (m *StablePoolInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StablePoolInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if len(m.ArbRoutes) > 0 { - for _, e := range m.ArbRoutes { - l = e.Size() - n += 1 + l + sovProtorev(uint64(l)) - } - } - l = len(m.TokenIn) - if l > 0 { - n += 1 + l + sovProtorev(uint64(l)) - } - l = len(m.TokenOut) - if l > 0 { - n += 1 + l + sovProtorev(uint64(l)) + if m.Weight != 0 { + i = encodeVarintProtorev(dAtA, i, uint64(m.Weight)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *BalancerPoolInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BalancerPoolInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BalancerPoolInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Weight != 0 { + i = encodeVarintProtorev(dAtA, i, uint64(m.Weight)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ConcentratedPoolInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ConcentratedPoolInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConcentratedPoolInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.MaxTicksCrossed != 0 { + i = encodeVarintProtorev(dAtA, i, uint64(m.MaxTicksCrossed)) + i-- + dAtA[i] = 0x10 + } + if m.Weight != 0 { + i = encodeVarintProtorev(dAtA, i, uint64(m.Weight)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *CosmwasmPoolInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CosmwasmPoolInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CosmwasmPoolInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.WeightMaps) > 0 { + for iNdEx := len(m.WeightMaps) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.WeightMaps[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProtorev(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *WeightMap) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WeightMap) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WeightMap) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ContractAddress) > 0 { + i -= len(m.ContractAddress) + copy(dAtA[i:], m.ContractAddress) + i = encodeVarintProtorev(dAtA, i, uint64(len(m.ContractAddress))) + i-- + dAtA[i] = 0x12 + } + if m.Weight != 0 { + i = encodeVarintProtorev(dAtA, i, uint64(m.Weight)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *BaseDenom) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BaseDenom) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BaseDenom) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.StepSize.Size() + i -= size + if _, err := m.StepSize.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintProtorev(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Denom) > 0 { + i -= len(m.Denom) + copy(dAtA[i:], m.Denom) + i = encodeVarintProtorev(dAtA, i, uint64(len(m.Denom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintProtorev(dAtA []byte, offset int, v uint64) int { + offset -= sovProtorev(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *TokenPairArbRoutes) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ArbRoutes) > 0 { + for _, e := range m.ArbRoutes { + l = e.Size() + n += 1 + l + sovProtorev(uint64(l)) + } + } + l = len(m.TokenIn) + if l > 0 { + n += 1 + l + sovProtorev(uint64(l)) + } + l = len(m.TokenOut) + if l > 0 { + n += 1 + l + sovProtorev(uint64(l)) } return n } @@ -952,6 +1528,93 @@ func (m *PoolWeights) Size() (n int) { return n } +func (m *InfoByPoolType) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Stable.Size() + n += 1 + l + sovProtorev(uint64(l)) + l = m.Balancer.Size() + n += 1 + l + sovProtorev(uint64(l)) + l = m.Concentrated.Size() + n += 1 + l + sovProtorev(uint64(l)) + l = m.Cosmwasm.Size() + n += 1 + l + sovProtorev(uint64(l)) + return n +} + +func (m *StablePoolInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Weight != 0 { + n += 1 + sovProtorev(uint64(m.Weight)) + } + return n +} + +func (m *BalancerPoolInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Weight != 0 { + n += 1 + sovProtorev(uint64(m.Weight)) + } + return n +} + +func (m *ConcentratedPoolInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Weight != 0 { + n += 1 + sovProtorev(uint64(m.Weight)) + } + if m.MaxTicksCrossed != 0 { + n += 1 + sovProtorev(uint64(m.MaxTicksCrossed)) + } + return n +} + +func (m *CosmwasmPoolInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.WeightMaps) > 0 { + for _, e := range m.WeightMaps { + l = e.Size() + n += 1 + l + sovProtorev(uint64(l)) + } + } + return n +} + +func (m *WeightMap) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Weight != 0 { + n += 1 + sovProtorev(uint64(m.Weight)) + } + l = len(m.ContractAddress) + if l > 0 { + n += 1 + l + sovProtorev(uint64(l)) + } + return n +} + func (m *BaseDenom) Size() (n int) { if m == nil { return 0 @@ -1692,6 +2355,599 @@ func (m *PoolWeights) Unmarshal(dAtA []byte) error { } return nil } +func (m *InfoByPoolType) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: InfoByPoolType: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: InfoByPoolType: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stable", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProtorev + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProtorev + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Stable.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Balancer", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProtorev + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProtorev + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Balancer.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Concentrated", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProtorev + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProtorev + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Concentrated.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Cosmwasm", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProtorev + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProtorev + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Cosmwasm.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProtorev(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProtorev + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StablePoolInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StablePoolInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StablePoolInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Weight", wireType) + } + m.Weight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Weight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipProtorev(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProtorev + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BalancerPoolInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BalancerPoolInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BalancerPoolInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Weight", wireType) + } + m.Weight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Weight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipProtorev(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProtorev + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConcentratedPoolInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ConcentratedPoolInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConcentratedPoolInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Weight", wireType) + } + m.Weight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Weight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxTicksCrossed", wireType) + } + m.MaxTicksCrossed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxTicksCrossed |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipProtorev(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProtorev + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CosmwasmPoolInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CosmwasmPoolInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CosmwasmPoolInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WeightMaps", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProtorev + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProtorev + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.WeightMaps = append(m.WeightMaps, WeightMap{}) + if err := m.WeightMaps[len(m.WeightMaps)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProtorev(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProtorev + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WeightMap) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WeightMap: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WeightMap: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Weight", wireType) + } + m.Weight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Weight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProtorev + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthProtorev + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProtorev + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProtorev(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProtorev + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *BaseDenom) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/protorev/types/query.pb.go b/x/protorev/types/query.pb.go index 5b639a6faa2..eba853f61a9 100644 --- a/x/protorev/types/query.pb.go +++ b/x/protorev/types/query.pb.go @@ -839,23 +839,23 @@ func (m *QueryGetProtoRevDeveloperAccountResponse) GetDeveloperAccount() string return "" } -// QueryGetProtoRevPoolWeightsRequest is request type for the -// Query/GetProtoRevPoolWeights RPC method. -type QueryGetProtoRevPoolWeightsRequest struct { +// QueryGetProtoRevInfoByPoolTypeRequest is request type for the +// Query/GetProtoRevInfoByPoolType RPC method. +type QueryGetProtoRevInfoByPoolTypeRequest struct { } -func (m *QueryGetProtoRevPoolWeightsRequest) Reset() { *m = QueryGetProtoRevPoolWeightsRequest{} } -func (m *QueryGetProtoRevPoolWeightsRequest) String() string { return proto.CompactTextString(m) } -func (*QueryGetProtoRevPoolWeightsRequest) ProtoMessage() {} -func (*QueryGetProtoRevPoolWeightsRequest) Descriptor() ([]byte, []int) { +func (m *QueryGetProtoRevInfoByPoolTypeRequest) Reset() { *m = QueryGetProtoRevInfoByPoolTypeRequest{} } +func (m *QueryGetProtoRevInfoByPoolTypeRequest) String() string { return proto.CompactTextString(m) } +func (*QueryGetProtoRevInfoByPoolTypeRequest) ProtoMessage() {} +func (*QueryGetProtoRevInfoByPoolTypeRequest) Descriptor() ([]byte, []int) { return fileDescriptor_f5e7ac9973cce389, []int{18} } -func (m *QueryGetProtoRevPoolWeightsRequest) XXX_Unmarshal(b []byte) error { +func (m *QueryGetProtoRevInfoByPoolTypeRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *QueryGetProtoRevPoolWeightsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *QueryGetProtoRevInfoByPoolTypeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_QueryGetProtoRevPoolWeightsRequest.Marshal(b, m, deterministic) + return xxx_messageInfo_QueryGetProtoRevInfoByPoolTypeRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -865,37 +865,40 @@ func (m *QueryGetProtoRevPoolWeightsRequest) XXX_Marshal(b []byte, deterministic return b[:n], nil } } -func (m *QueryGetProtoRevPoolWeightsRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryGetProtoRevPoolWeightsRequest.Merge(m, src) +func (m *QueryGetProtoRevInfoByPoolTypeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetProtoRevInfoByPoolTypeRequest.Merge(m, src) } -func (m *QueryGetProtoRevPoolWeightsRequest) XXX_Size() int { +func (m *QueryGetProtoRevInfoByPoolTypeRequest) XXX_Size() int { return m.Size() } -func (m *QueryGetProtoRevPoolWeightsRequest) XXX_DiscardUnknown() { - xxx_messageInfo_QueryGetProtoRevPoolWeightsRequest.DiscardUnknown(m) +func (m *QueryGetProtoRevInfoByPoolTypeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetProtoRevInfoByPoolTypeRequest.DiscardUnknown(m) } -var xxx_messageInfo_QueryGetProtoRevPoolWeightsRequest proto.InternalMessageInfo +var xxx_messageInfo_QueryGetProtoRevInfoByPoolTypeRequest proto.InternalMessageInfo -// QueryGetProtoRevPoolWeightsResponse is response type for the -// Query/GetProtoRevPoolWeights RPC method. -type QueryGetProtoRevPoolWeightsResponse struct { - // pool_weights is a list of all of the pool weights - PoolWeights PoolWeights `protobuf:"bytes,1,opt,name=pool_weights,json=poolWeights,proto3" json:"pool_weights" yaml:"pool_weights"` +// QueryGetProtoRevInfoByPoolTypeResponse is response type for the +// Query/GetProtoRevInfoByPoolType RPC method. +type QueryGetProtoRevInfoByPoolTypeResponse struct { + // InfoByPoolType contains all information pertaining to how different + // pool types are handled by the module. + InfoByPoolType InfoByPoolType `protobuf:"bytes,1,opt,name=info_by_pool_type,json=infoByPoolType,proto3" json:"info_by_pool_type" yaml:"info_by_pool_type"` } -func (m *QueryGetProtoRevPoolWeightsResponse) Reset() { *m = QueryGetProtoRevPoolWeightsResponse{} } -func (m *QueryGetProtoRevPoolWeightsResponse) String() string { return proto.CompactTextString(m) } -func (*QueryGetProtoRevPoolWeightsResponse) ProtoMessage() {} -func (*QueryGetProtoRevPoolWeightsResponse) Descriptor() ([]byte, []int) { +func (m *QueryGetProtoRevInfoByPoolTypeResponse) Reset() { + *m = QueryGetProtoRevInfoByPoolTypeResponse{} +} +func (m *QueryGetProtoRevInfoByPoolTypeResponse) String() string { return proto.CompactTextString(m) } +func (*QueryGetProtoRevInfoByPoolTypeResponse) ProtoMessage() {} +func (*QueryGetProtoRevInfoByPoolTypeResponse) Descriptor() ([]byte, []int) { return fileDescriptor_f5e7ac9973cce389, []int{19} } -func (m *QueryGetProtoRevPoolWeightsResponse) XXX_Unmarshal(b []byte) error { +func (m *QueryGetProtoRevInfoByPoolTypeResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *QueryGetProtoRevPoolWeightsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *QueryGetProtoRevInfoByPoolTypeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_QueryGetProtoRevPoolWeightsResponse.Marshal(b, m, deterministic) + return xxx_messageInfo_QueryGetProtoRevInfoByPoolTypeResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -905,23 +908,23 @@ func (m *QueryGetProtoRevPoolWeightsResponse) XXX_Marshal(b []byte, deterministi return b[:n], nil } } -func (m *QueryGetProtoRevPoolWeightsResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryGetProtoRevPoolWeightsResponse.Merge(m, src) +func (m *QueryGetProtoRevInfoByPoolTypeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetProtoRevInfoByPoolTypeResponse.Merge(m, src) } -func (m *QueryGetProtoRevPoolWeightsResponse) XXX_Size() int { +func (m *QueryGetProtoRevInfoByPoolTypeResponse) XXX_Size() int { return m.Size() } -func (m *QueryGetProtoRevPoolWeightsResponse) XXX_DiscardUnknown() { - xxx_messageInfo_QueryGetProtoRevPoolWeightsResponse.DiscardUnknown(m) +func (m *QueryGetProtoRevInfoByPoolTypeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetProtoRevInfoByPoolTypeResponse.DiscardUnknown(m) } -var xxx_messageInfo_QueryGetProtoRevPoolWeightsResponse proto.InternalMessageInfo +var xxx_messageInfo_QueryGetProtoRevInfoByPoolTypeResponse proto.InternalMessageInfo -func (m *QueryGetProtoRevPoolWeightsResponse) GetPoolWeights() PoolWeights { +func (m *QueryGetProtoRevInfoByPoolTypeResponse) GetInfoByPoolType() InfoByPoolType { if m != nil { - return m.PoolWeights + return m.InfoByPoolType } - return PoolWeights{} + return InfoByPoolType{} } // QueryGetProtoRevMaxPoolPointsPerBlockRequest is request type for the @@ -1405,8 +1408,8 @@ func init() { proto.RegisterType((*QueryGetProtoRevAdminAccountResponse)(nil), "osmosis.protorev.v1beta1.QueryGetProtoRevAdminAccountResponse") proto.RegisterType((*QueryGetProtoRevDeveloperAccountRequest)(nil), "osmosis.protorev.v1beta1.QueryGetProtoRevDeveloperAccountRequest") proto.RegisterType((*QueryGetProtoRevDeveloperAccountResponse)(nil), "osmosis.protorev.v1beta1.QueryGetProtoRevDeveloperAccountResponse") - proto.RegisterType((*QueryGetProtoRevPoolWeightsRequest)(nil), "osmosis.protorev.v1beta1.QueryGetProtoRevPoolWeightsRequest") - proto.RegisterType((*QueryGetProtoRevPoolWeightsResponse)(nil), "osmosis.protorev.v1beta1.QueryGetProtoRevPoolWeightsResponse") + proto.RegisterType((*QueryGetProtoRevInfoByPoolTypeRequest)(nil), "osmosis.protorev.v1beta1.QueryGetProtoRevInfoByPoolTypeRequest") + proto.RegisterType((*QueryGetProtoRevInfoByPoolTypeResponse)(nil), "osmosis.protorev.v1beta1.QueryGetProtoRevInfoByPoolTypeResponse") proto.RegisterType((*QueryGetProtoRevMaxPoolPointsPerBlockRequest)(nil), "osmosis.protorev.v1beta1.QueryGetProtoRevMaxPoolPointsPerBlockRequest") proto.RegisterType((*QueryGetProtoRevMaxPoolPointsPerBlockResponse)(nil), "osmosis.protorev.v1beta1.QueryGetProtoRevMaxPoolPointsPerBlockResponse") proto.RegisterType((*QueryGetProtoRevMaxPoolPointsPerTxRequest)(nil), "osmosis.protorev.v1beta1.QueryGetProtoRevMaxPoolPointsPerTxRequest") @@ -1424,103 +1427,103 @@ func init() { } var fileDescriptor_f5e7ac9973cce389 = []byte{ - // 1531 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x5d, 0x6f, 0x1b, 0xc5, - 0x1a, 0xce, 0xf6, 0x23, 0x39, 0x9d, 0xb4, 0x3d, 0xed, 0xb4, 0x49, 0x93, 0x4d, 0x6a, 0x3b, 0x93, - 0x38, 0x9f, 0x8d, 0x7d, 0xfa, 0x71, 0x4e, 0x0f, 0x1f, 0x05, 0xb2, 0x0d, 0x54, 0x51, 0x45, 0x63, - 0x96, 0x22, 0x24, 0x90, 0x30, 0x6b, 0x7b, 0xe2, 0xae, 0xba, 0xde, 0x71, 0x77, 0xd7, 0x21, 0xbe, - 0x05, 0x09, 0x84, 0x84, 0xc4, 0xd7, 0x35, 0x12, 0xf7, 0xf0, 0x07, 0xb8, 0xe0, 0x82, 0xbb, 0x8a, - 0xab, 0x22, 0x24, 0x84, 0x0a, 0xb2, 0x50, 0xcb, 0x05, 0xd7, 0xfe, 0x05, 0x68, 0x67, 0xde, 0xb5, - 0xd7, 0x3b, 0xbb, 0x8e, 0x9d, 0x48, 0x5c, 0xd5, 0x3b, 0xf3, 0xbe, 0xcf, 0xfb, 0x3c, 0x33, 0xf3, - 0xce, 0x3c, 0x29, 0x5a, 0x60, 0x6e, 0x8d, 0xb9, 0xa6, 0x9b, 0xaf, 0x3b, 0xcc, 0x63, 0x0e, 0xdd, - 0xcd, 0xef, 0x5e, 0x2e, 0x51, 0xcf, 0xb8, 0x9c, 0x7f, 0xd0, 0xa0, 0x4e, 0x33, 0xc7, 0x87, 0xf1, - 0x14, 0x44, 0xe5, 0x82, 0xa8, 0x1c, 0x44, 0xa9, 0xe7, 0xab, 0xac, 0xca, 0xf8, 0x68, 0xde, 0xff, - 0x25, 0x02, 0xd4, 0xd9, 0x2a, 0x63, 0x55, 0x8b, 0xe6, 0x8d, 0xba, 0x99, 0x37, 0x6c, 0x9b, 0x79, - 0x86, 0x67, 0x32, 0x1b, 0xd2, 0xd5, 0xd5, 0x32, 0x87, 0xcb, 0x97, 0x0c, 0x97, 0x8a, 0x32, 0x9d, - 0xa2, 0x75, 0xa3, 0x6a, 0xda, 0x3c, 0x18, 0x62, 0xb3, 0x89, 0xfc, 0xea, 0x86, 0x63, 0xd4, 0x02, - 0xc8, 0xa5, 0xe4, 0xb0, 0x80, 0xb1, 0x08, 0x4c, 0x85, 0x6b, 0x07, 0x31, 0x65, 0x66, 0x42, 0x3d, - 0x72, 0x1e, 0xe1, 0xd7, 0x7c, 0x46, 0x05, 0x8e, 0xae, 0xd3, 0x07, 0x0d, 0xea, 0x7a, 0x64, 0x07, - 0x9d, 0xeb, 0x19, 0x75, 0xeb, 0xcc, 0x76, 0x29, 0xde, 0x46, 0xa3, 0x82, 0xc5, 0x94, 0x92, 0x51, - 0x96, 0xc7, 0xaf, 0x64, 0x72, 0x49, 0xeb, 0x94, 0x13, 0x99, 0xda, 0xc4, 0xc3, 0x56, 0x7a, 0xa4, - 0xdd, 0x4a, 0x9f, 0x6a, 0x1a, 0x35, 0xeb, 0x59, 0x22, 0xb2, 0x89, 0x0e, 0x30, 0x64, 0x09, 0x65, - 0x79, 0x9d, 0x5b, 0xd4, 0x2b, 0xf8, 0x08, 0x3a, 0xdd, 0xbd, 0xd3, 0xa8, 0x95, 0xa8, 0xb3, 0xbd, - 0x73, 0xd7, 0x31, 0x2a, 0xb4, 0x43, 0xe8, 0x2b, 0x05, 0x2d, 0xee, 0x17, 0x09, 0x24, 0x5d, 0x74, - 0xc6, 0xe6, 0x33, 0x45, 0xb6, 0x53, 0xf4, 0xf8, 0x1c, 0xa7, 0x7b, 0x42, 0xdb, 0xf2, 0xc9, 0x3c, - 0x6e, 0xa5, 0x17, 0xab, 0xa6, 0x77, 0xaf, 0x51, 0xca, 0x95, 0x59, 0x2d, 0x0f, 0xcb, 0x23, 0xfe, - 0x59, 0x77, 0x2b, 0xf7, 0xf3, 0x5e, 0xb3, 0x4e, 0xdd, 0xdc, 0x96, 0xed, 0xb5, 0x5b, 0xe9, 0x0b, - 0x82, 0x76, 0x14, 0x8f, 0xe8, 0xa7, 0xed, 0x9e, 0xe2, 0x64, 0x5b, 0x16, 0x52, 0x70, 0xd8, 0x8e, - 0xe9, 0xb9, 0x5a, 0x73, 0x93, 0xda, 0xac, 0x06, 0x42, 0xf0, 0x22, 0x3a, 0x5e, 0xf1, 0xbf, 0x81, - 0xd2, 0x99, 0x76, 0x2b, 0x7d, 0x52, 0x14, 0xe1, 0xc3, 0x44, 0x17, 0xd3, 0xc4, 0x96, 0xf5, 0x46, - 0x01, 0x41, 0xef, 0x26, 0x1a, 0xad, 0xf3, 0x19, 0xd8, 0x94, 0xe9, 0x9c, 0x10, 0x93, 0xf3, 0xb7, - 0xbc, 0xb3, 0x1f, 0x37, 0x99, 0x69, 0x6b, 0x67, 0x43, 0x3b, 0xc1, 0x53, 0xfc, 0x9d, 0x10, 0x3f, - 0xe6, 0xd1, 0x5c, 0xb4, 0xde, 0x86, 0x65, 0x41, 0xc9, 0x60, 0x17, 0x1e, 0x20, 0xd2, 0x2f, 0x08, - 0x08, 0xdd, 0x46, 0x63, 0x02, 0xd4, 0x5f, 0xf7, 0xa3, 0xfd, 0x19, 0x4d, 0xc2, 0xf9, 0x38, 0x1d, - 0x66, 0xe5, 0x12, 0x7d, 0xac, 0xf3, 0x0b, 0x2d, 0x47, 0x4b, 0xbe, 0xee, 0x77, 0x97, 0xeb, 0x99, - 0x65, 0x57, 0x6b, 0xea, 0xac, 0xe1, 0xd1, 0xd0, 0xda, 0x3a, 0xfe, 0x37, 0x2f, 0x7b, 0x2c, 0xbc, - 0xb6, 0x7c, 0x98, 0xe8, 0x62, 0x9a, 0x7c, 0xae, 0xa0, 0x95, 0x01, 0x40, 0x41, 0x4e, 0x05, 0x21, - 0xb7, 0x33, 0x09, 0x6b, 0xbc, 0x92, 0x7c, 0xf0, 0x79, 0x72, 0x08, 0x6d, 0x1a, 0x14, 0x9e, 0x15, - 0x4c, 0xba, 0x50, 0x44, 0x0f, 0xe1, 0x92, 0x35, 0x99, 0xd2, 0x86, 0x65, 0x45, 0xc0, 0x82, 0x7d, - 0xf8, 0x42, 0x41, 0xab, 0x83, 0x44, 0x27, 0x28, 0x38, 0xfa, 0x4f, 0x29, 0xb8, 0xcb, 0xee, 0x53, - 0xbb, 0x60, 0x98, 0xce, 0x86, 0x53, 0xe2, 0xa8, 0x1d, 0x05, 0x1f, 0xc7, 0x28, 0x88, 0x8b, 0x06, - 0x05, 0x6f, 0xa3, 0x51, 0xbe, 0x75, 0x01, 0xfb, 0x4b, 0xc9, 0xec, 0x65, 0x94, 0xe8, 0x25, 0x24, - 0x90, 0x88, 0x0e, 0x90, 0x24, 0x8b, 0xe6, 0xa5, 0xc5, 0xac, 0xd4, 0x4c, 0x7b, 0xa3, 0x5c, 0x66, - 0x0d, 0xdb, 0x0b, 0x28, 0x53, 0xb4, 0xd0, 0x3f, 0x0c, 0xb8, 0xde, 0x40, 0xa7, 0x0c, 0x7f, 0xbc, - 0x68, 0x88, 0x09, 0xe8, 0xf4, 0xa9, 0x76, 0x2b, 0x7d, 0x5e, 0x10, 0xe8, 0x99, 0x26, 0xfa, 0x49, - 0x23, 0x04, 0x43, 0x56, 0xd0, 0x52, 0xb4, 0xcc, 0x26, 0xdd, 0xa5, 0x16, 0xab, 0x53, 0x27, 0xc2, - 0xa8, 0x21, 0xf7, 0x86, 0x1c, 0x0a, 0xac, 0xb6, 0xd0, 0xd9, 0x4a, 0x30, 0x17, 0x61, 0x36, 0xdb, - 0x6e, 0xa5, 0xa7, 0x82, 0x3b, 0x28, 0x12, 0x42, 0xf4, 0x33, 0x95, 0x08, 0x24, 0x59, 0x90, 0x6f, - 0x81, 0x02, 0x63, 0xd6, 0x9b, 0xd4, 0xac, 0xde, 0xeb, 0xde, 0x15, 0x9f, 0x28, 0xf2, 0xb2, 0xf6, - 0x84, 0x01, 0x31, 0x8a, 0x4e, 0xd6, 0x19, 0xb3, 0x8a, 0xef, 0x89, 0x71, 0x68, 0xb0, 0x6c, 0x9f, - 0x97, 0xa5, 0x0b, 0xa2, 0xcd, 0xc0, 0xce, 0x9e, 0x83, 0xeb, 0x23, 0x04, 0x44, 0xf4, 0xf1, 0x7a, - 0x37, 0x92, 0xe4, 0xd0, 0xa5, 0x28, 0x9b, 0x57, 0x8d, 0x3d, 0x1f, 0xab, 0xc0, 0x4c, 0xdb, 0x73, - 0x0b, 0xd4, 0xd1, 0x2c, 0x56, 0xbe, 0x1f, 0xd0, 0xff, 0x54, 0x41, 0xeb, 0x03, 0x26, 0x80, 0x90, - 0x77, 0xd0, 0x74, 0xcd, 0xd8, 0x2b, 0x72, 0x0e, 0x75, 0x1e, 0x52, 0xf4, 0x17, 0xb2, 0xe4, 0x07, - 0x71, 0x55, 0xc7, 0xb4, 0x85, 0x76, 0x2b, 0x9d, 0x11, 0x54, 0x13, 0x43, 0x89, 0x3e, 0x51, 0x8b, - 0xab, 0x13, 0xd7, 0x5f, 0x51, 0x42, 0x77, 0xf7, 0x02, 0xfa, 0x1f, 0xc4, 0xf4, 0x57, 0x5c, 0x34, - 0x70, 0x7f, 0x03, 0x4d, 0xc6, 0x11, 0xf2, 0xf6, 0x80, 0xf8, 0x5c, 0xbb, 0x95, 0xbe, 0x98, 0x4c, - 0xdc, 0xdb, 0x23, 0x3a, 0xae, 0x49, 0xf0, 0x71, 0x8f, 0x8a, 0x66, 0xb8, 0x94, 0xbf, 0x5f, 0x9d, - 0x83, 0xf2, 0xa1, 0x22, 0x9f, 0xa7, 0x70, 0x14, 0x50, 0x7c, 0x17, 0x8d, 0xfb, 0xcf, 0x47, 0x91, - 0x3f, 0x8f, 0xc1, 0x3d, 0x30, 0x9f, 0x7c, 0x4c, 0x3a, 0x10, 0x9a, 0x0a, 0x87, 0x04, 0x0b, 0x01, - 0x21, 0x14, 0xa2, 0xa3, 0x52, 0xa7, 0x12, 0xc9, 0xa0, 0x54, 0x94, 0xc7, 0xcb, 0xb6, 0x51, 0xb2, - 0x68, 0x25, 0xa0, 0xba, 0x8d, 0xd2, 0x89, 0x11, 0x40, 0xf3, 0x12, 0x1a, 0xa3, 0x62, 0x88, 0x2f, - 0xdd, 0xbf, 0x34, 0xdc, 0x7d, 0xdd, 0x60, 0x82, 0xe8, 0x41, 0x88, 0xdf, 0x24, 0x33, 0x71, 0x4d, - 0x12, 0xbc, 0x68, 0xd7, 0x10, 0xea, 0xd2, 0x85, 0x76, 0x9d, 0xe8, 0x5e, 0xc5, 0xdd, 0x39, 0xa2, - 0x9f, 0xe8, 0x28, 0xc1, 0xd7, 0xd1, 0x38, 0xf3, 0xee, 0x51, 0x07, 0xd2, 0x8e, 0xf0, 0xb4, 0xc9, - 0xee, 0x0a, 0x84, 0x26, 0x89, 0x8e, 0xf8, 0x17, 0x4f, 0x24, 0xb7, 0xd1, 0x6c, 0x3c, 0x1b, 0x10, - 0xb7, 0x86, 0xc6, 0xf8, 0xd6, 0x9b, 0x15, 0x38, 0x17, 0x21, 0x71, 0x30, 0xe1, 0x3b, 0x0a, 0xc6, - 0xac, 0xad, 0xca, 0x95, 0x6f, 0x2e, 0xa0, 0xe3, 0x1c, 0x0d, 0x7f, 0xa4, 0xa0, 0x51, 0xe1, 0x07, - 0x71, 0x9f, 0x8b, 0x5b, 0xb6, 0xa1, 0xea, 0xfa, 0x80, 0xd1, 0x82, 0x1e, 0xc9, 0xbc, 0xff, 0xf3, - 0x9f, 0x5f, 0x1e, 0x51, 0xf1, 0x54, 0x5e, 0x72, 0xc7, 0xc2, 0x6f, 0xe2, 0x1f, 0x15, 0x34, 0x9d, - 0xe8, 0x20, 0xf1, 0x8b, 0xfb, 0x94, 0xdb, 0xcf, 0xa5, 0xaa, 0x2f, 0x1d, 0x1c, 0x00, 0x24, 0xac, - 0x72, 0x09, 0x0b, 0x98, 0xc8, 0x12, 0xa2, 0x26, 0x34, 0x2a, 0xa6, 0xd7, 0x1e, 0x0e, 0x23, 0x26, - 0xd6, 0xa9, 0x0e, 0x23, 0x26, 0xde, 0x99, 0xf6, 0x13, 0x03, 0xf6, 0xae, 0x58, 0x6a, 0x8a, 0x73, - 0x88, 0xbf, 0x53, 0xd0, 0x44, 0xac, 0xad, 0xc4, 0xcf, 0x0d, 0xce, 0x43, 0x72, 0xac, 0xea, 0xf3, - 0x07, 0x4b, 0x06, 0x01, 0x59, 0x2e, 0x20, 0x8d, 0x2f, 0xca, 0x02, 0x0c, 0xcb, 0x2a, 0x82, 0x08, - 0xfc, 0x8b, 0x82, 0x66, 0xfb, 0x59, 0x49, 0xac, 0x0d, 0xce, 0x22, 0xc9, 0xdc, 0xaa, 0x37, 0x0f, - 0x85, 0x01, 0x82, 0xd6, 0xb9, 0xa0, 0x25, 0x9c, 0x95, 0x05, 0x75, 0x9d, 0x9c, 0xbf, 0x29, 0xdc, - 0x1a, 0xe1, 0xc7, 0x0a, 0xba, 0xd8, 0xd7, 0x62, 0xe2, 0x9b, 0x43, 0xad, 0x6f, 0xbc, 0x9d, 0x55, - 0x37, 0x0f, 0x07, 0x02, 0xda, 0x72, 0x5c, 0xdb, 0x32, 0x5e, 0x8c, 0xdf, 0x2c, 0xae, 0xa8, 0xd8, - 0x55, 0x89, 0x7f, 0xeb, 0x15, 0x27, 0xfb, 0xc6, 0x61, 0xc4, 0x25, 0x3a, 0xdd, 0x61, 0xc4, 0x25, - 0x1b, 0x60, 0x92, 0xe7, 0xe2, 0x56, 0xf0, 0x92, 0x2c, 0xce, 0xf3, 0xb3, 0x8a, 0x75, 0xc3, 0x74, - 0x8a, 0x86, 0x53, 0x12, 0x3a, 0x5d, 0xfc, 0x83, 0x82, 0x2e, 0x24, 0x38, 0x55, 0x7c, 0x63, 0x88, - 0xf5, 0x96, 0x8d, 0xb0, 0xfa, 0xc2, 0x41, 0xd3, 0x41, 0xcb, 0x12, 0xd7, 0x32, 0x87, 0xd3, 0x31, - 0x1b, 0x15, 0x76, 0xc6, 0xf8, 0x27, 0x05, 0xcd, 0xf4, 0xf1, 0xb6, 0x78, 0x63, 0x70, 0x22, 0x09, - 0x16, 0x5a, 0xd5, 0x0e, 0x03, 0x01, 0x7a, 0xd6, 0xb8, 0x9e, 0x2c, 0x9e, 0x97, 0xf5, 0x48, 0x7e, - 0x1a, 0x7f, 0xaf, 0xa0, 0xc9, 0x78, 0x47, 0x8c, 0x87, 0xb8, 0xab, 0x64, 0xbf, 0xad, 0xde, 0x38, - 0x60, 0x36, 0x88, 0x58, 0xe4, 0x22, 0x32, 0x38, 0x15, 0x73, 0x57, 0x87, 0x5c, 0x35, 0xfe, 0xbd, - 0xb7, 0x6b, 0x64, 0x4f, 0x39, 0x4c, 0xd7, 0x24, 0xfa, 0xd7, 0x61, 0xba, 0x26, 0xd9, 0xd6, 0x92, - 0xff, 0x70, 0x51, 0xab, 0x78, 0x59, 0x16, 0x15, 0x6f, 0x63, 0xf1, 0x5f, 0x0a, 0xca, 0xec, 0xe7, - 0xf8, 0xf1, 0x2b, 0x07, 0x27, 0x17, 0xfe, 0x1b, 0x43, 0xbd, 0x75, 0x68, 0x1c, 0xd0, 0x79, 0x95, - 0xeb, 0x5c, 0xc7, 0x6b, 0x83, 0xe9, 0xe4, 0x7f, 0x67, 0x44, 0x5f, 0xdc, 0xae, 0xe5, 0x1e, 0xe6, - 0xc5, 0x95, 0xec, 0xfc, 0x30, 0x2f, 0xae, 0xec, 0xf2, 0xfb, 0xbd, 0xb8, 0x21, 0xdf, 0x8e, 0xbf, - 0x55, 0x10, 0x96, 0x4d, 0x38, 0xfe, 0xff, 0xe0, 0xb5, 0x7b, 0x9d, 0xbd, 0xfa, 0xcc, 0x01, 0x32, - 0x81, 0xf2, 0x1c, 0xa7, 0x3c, 0x83, 0xa7, 0x65, 0xca, 0x60, 0xf3, 0xf1, 0xd7, 0x0a, 0xfa, 0x77, - 0xa4, 0xff, 0xf0, 0x7f, 0x87, 0xeb, 0xd7, 0x80, 0xe8, 0xff, 0x86, 0x4d, 0x03, 0x96, 0x29, 0xce, - 0x72, 0x0a, 0x4f, 0xc6, 0xf7, 0xb7, 0x76, 0xe7, 0xe1, 0x93, 0x94, 0xf2, 0xe8, 0x49, 0x4a, 0xf9, - 0xe3, 0x49, 0x4a, 0xf9, 0xec, 0x69, 0x6a, 0xe4, 0xd1, 0xd3, 0xd4, 0xc8, 0xaf, 0x4f, 0x53, 0x23, - 0x6f, 0x5d, 0x0b, 0xfd, 0x6f, 0x29, 0xe4, 0xae, 0x5b, 0x46, 0xc9, 0xed, 0x00, 0xed, 0x5e, 0xbe, - 0x9e, 0xdf, 0x0b, 0xbd, 0x47, 0xcd, 0x3a, 0x75, 0x4b, 0xa3, 0xfc, 0xfb, 0xea, 0xdf, 0x01, 0x00, - 0x00, 0xff, 0xff, 0xea, 0xd0, 0xea, 0xdc, 0x70, 0x17, 0x00, 0x00, + // 1532 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xdb, 0x6f, 0x1b, 0xc5, + 0x17, 0xce, 0xf6, 0x92, 0xfc, 0x3a, 0xbd, 0xfc, 0x9a, 0xa1, 0x49, 0x93, 0x6d, 0x6a, 0x3b, 0x93, + 0x7b, 0xd2, 0xd8, 0xf4, 0x02, 0xe5, 0x56, 0x20, 0xdb, 0x40, 0x15, 0x55, 0x34, 0x66, 0x09, 0x2f, + 0x20, 0x61, 0xd6, 0xf6, 0x24, 0x5d, 0x75, 0xbd, 0xe3, 0xee, 0xae, 0xa3, 0xf8, 0x15, 0x24, 0x10, + 0x12, 0x12, 0xb7, 0x67, 0x04, 0x7f, 0x00, 0xff, 0x40, 0x1f, 0x79, 0xab, 0xe0, 0xa5, 0x08, 0x09, + 0xa1, 0x82, 0x2c, 0xd4, 0xf2, 0xc0, 0xb3, 0xff, 0x02, 0xb4, 0x33, 0x67, 0xed, 0xf5, 0xce, 0xae, + 0xe3, 0x4d, 0x24, 0x9e, 0x62, 0xef, 0x39, 0xe7, 0x3b, 0xdf, 0x37, 0x67, 0x67, 0xe6, 0x73, 0xd0, + 0x2c, 0x73, 0x6b, 0xcc, 0x35, 0xdd, 0x42, 0xdd, 0x61, 0x1e, 0x73, 0xe8, 0x6e, 0x61, 0xf7, 0x72, + 0x99, 0x7a, 0xc6, 0xe5, 0xc2, 0xfd, 0x06, 0x75, 0x9a, 0x79, 0xfe, 0x18, 0x4f, 0x40, 0x56, 0x3e, + 0xc8, 0xca, 0x43, 0x96, 0x7a, 0x6e, 0x87, 0xed, 0x30, 0xfe, 0xb4, 0xe0, 0x7f, 0x12, 0x09, 0xea, + 0xd4, 0x0e, 0x63, 0x3b, 0x16, 0x2d, 0x18, 0x75, 0xb3, 0x60, 0xd8, 0x36, 0xf3, 0x0c, 0xcf, 0x64, + 0x36, 0x94, 0xab, 0xcb, 0x15, 0x0e, 0x57, 0x28, 0x1b, 0x2e, 0x15, 0x6d, 0x3a, 0x4d, 0xeb, 0xc6, + 0x8e, 0x69, 0xf3, 0x64, 0xc8, 0x9d, 0x4b, 0xe4, 0x57, 0x37, 0x1c, 0xa3, 0x16, 0x40, 0x2e, 0x24, + 0xa7, 0x05, 0x8c, 0x45, 0x62, 0x26, 0xdc, 0x3b, 0xc8, 0xa9, 0x30, 0x13, 0xfa, 0x91, 0x73, 0x08, + 0xbf, 0xed, 0x33, 0x2a, 0x72, 0x74, 0x9d, 0xde, 0x6f, 0x50, 0xd7, 0x23, 0xdb, 0xe8, 0x99, 0x9e, + 0xa7, 0x6e, 0x9d, 0xd9, 0x2e, 0xc5, 0x9b, 0x68, 0x58, 0xb0, 0x98, 0x50, 0x72, 0xca, 0xe2, 0xc9, + 0x2b, 0xb9, 0x7c, 0xd2, 0x3a, 0xe5, 0x45, 0xa5, 0x36, 0xf6, 0xb0, 0x95, 0x1d, 0x6a, 0xb7, 0xb2, + 0xa7, 0x9b, 0x46, 0xcd, 0x7a, 0x89, 0x88, 0x6a, 0xa2, 0x03, 0x0c, 0x59, 0x40, 0x73, 0xbc, 0xcf, + 0x2d, 0xea, 0x15, 0x7d, 0x04, 0x9d, 0xee, 0xde, 0x69, 0xd4, 0xca, 0xd4, 0xd9, 0xdc, 0xde, 0x72, + 0x8c, 0x2a, 0xed, 0x10, 0xfa, 0x56, 0x41, 0xf3, 0xfb, 0x65, 0x02, 0x49, 0x17, 0x9d, 0xb5, 0x79, + 0xa4, 0xc4, 0xb6, 0x4b, 0x1e, 0x8f, 0x71, 0xba, 0x27, 0xb4, 0x0d, 0x9f, 0xcc, 0xe3, 0x56, 0x76, + 0x7e, 0xc7, 0xf4, 0xee, 0x36, 0xca, 0xf9, 0x0a, 0xab, 0x15, 0x60, 0x79, 0xc4, 0x9f, 0x55, 0xb7, + 0x7a, 0xaf, 0xe0, 0x35, 0xeb, 0xd4, 0xcd, 0x6f, 0xd8, 0x5e, 0xbb, 0x95, 0x3d, 0x2f, 0x68, 0x47, + 0xf1, 0x88, 0x7e, 0xc6, 0xee, 0x69, 0x4e, 0x36, 0x65, 0x21, 0x45, 0x87, 0x6d, 0x9b, 0x9e, 0xab, + 0x35, 0xd7, 0xa9, 0xcd, 0x6a, 0x20, 0x04, 0xcf, 0xa3, 0xe3, 0x55, 0xff, 0x3b, 0x50, 0x3a, 0xdb, + 0x6e, 0x65, 0x4f, 0x89, 0x26, 0xfc, 0x31, 0xd1, 0x45, 0x98, 0xd8, 0xb2, 0xde, 0x28, 0x20, 0xe8, + 0x5d, 0x47, 0xc3, 0x75, 0x1e, 0x81, 0xa1, 0x4c, 0xe6, 0x85, 0x98, 0xbc, 0x3f, 0xf2, 0xce, 0x3c, + 0x6e, 0x32, 0xd3, 0xd6, 0x46, 0x43, 0x93, 0xe0, 0x25, 0xfe, 0x24, 0xc4, 0x87, 0x19, 0x34, 0x1d, + 0xed, 0xb7, 0x66, 0x59, 0xd0, 0x32, 0x98, 0xc2, 0x7d, 0x44, 0xfa, 0x25, 0x01, 0xa1, 0xdb, 0x68, + 0x44, 0x80, 0xfa, 0xeb, 0x7e, 0xb4, 0x3f, 0xa3, 0x71, 0x78, 0x3f, 0xce, 0x84, 0x59, 0xb9, 0x44, + 0x1f, 0xe9, 0x7c, 0x42, 0x8b, 0xd1, 0x96, 0xef, 0xf8, 0xbb, 0xcb, 0xf5, 0xcc, 0x8a, 0xab, 0x35, + 0x75, 0xd6, 0xf0, 0x68, 0x68, 0x6d, 0x1d, 0xff, 0x3b, 0x6f, 0x7b, 0x2c, 0xbc, 0xb6, 0xfc, 0x31, + 0xd1, 0x45, 0x98, 0x7c, 0xa5, 0xa0, 0xa5, 0x01, 0x40, 0x41, 0x4e, 0x15, 0x21, 0xb7, 0x13, 0x84, + 0x35, 0x5e, 0x4a, 0x7e, 0xf1, 0x79, 0x71, 0x08, 0x6d, 0x12, 0x14, 0x8e, 0x0a, 0x26, 0x5d, 0x28, + 0xa2, 0x87, 0x70, 0xc9, 0x8a, 0x4c, 0x69, 0xcd, 0xb2, 0x22, 0x60, 0xc1, 0x1c, 0xbe, 0x56, 0xd0, + 0xf2, 0x20, 0xd9, 0x09, 0x0a, 0x8e, 0xfe, 0x57, 0x0a, 0xb6, 0xd8, 0x3d, 0x6a, 0x17, 0x0d, 0xd3, + 0x59, 0x73, 0xca, 0x1c, 0xb5, 0xa3, 0xe0, 0xb3, 0x18, 0x05, 0x71, 0xd9, 0xa0, 0xe0, 0x7d, 0x34, + 0xcc, 0x47, 0x17, 0xb0, 0xbf, 0x94, 0xcc, 0x5e, 0x46, 0x89, 0x1e, 0x42, 0x02, 0x89, 0xe8, 0x00, + 0x49, 0xe6, 0xd0, 0x8c, 0xb4, 0x98, 0xd5, 0x9a, 0x69, 0xaf, 0x55, 0x2a, 0xac, 0x61, 0x7b, 0x01, + 0x65, 0x8a, 0x66, 0xfb, 0xa7, 0x01, 0xd7, 0x1b, 0xe8, 0xb4, 0xe1, 0x3f, 0x2f, 0x19, 0x22, 0x00, + 0x3b, 0x7d, 0xa2, 0xdd, 0xca, 0x9e, 0x13, 0x04, 0x7a, 0xc2, 0x44, 0x3f, 0x65, 0x84, 0x60, 0xc8, + 0x12, 0x5a, 0x88, 0xb6, 0x59, 0xa7, 0xbb, 0xd4, 0x62, 0x75, 0xea, 0x44, 0x18, 0x35, 0xe4, 0xbd, + 0x21, 0xa7, 0x02, 0xab, 0x0d, 0x34, 0x5a, 0x0d, 0x62, 0x11, 0x66, 0x53, 0xed, 0x56, 0x76, 0x22, + 0x38, 0x83, 0x22, 0x29, 0x44, 0x3f, 0x5b, 0x8d, 0x40, 0xc6, 0x1d, 0xda, 0x1b, 0xf6, 0x36, 0xd3, + 0x9a, 0x45, 0xc6, 0xac, 0xad, 0x66, 0x3d, 0xd8, 0x8f, 0xe4, 0xbb, 0x98, 0x43, 0x3b, 0x9a, 0x09, + 0xf4, 0x1a, 0x68, 0xd4, 0xb4, 0xb7, 0x59, 0xa9, 0xdc, 0x2c, 0xd5, 0x19, 0xb3, 0x4a, 0xfe, 0x21, + 0x0c, 0x7b, 0x6d, 0x31, 0x79, 0xd6, 0xbd, 0x60, 0x5a, 0x0e, 0xe6, 0x0c, 0x62, 0x24, 0x40, 0xa2, + 0x9f, 0x31, 0x7b, 0x2a, 0x48, 0x1e, 0x5d, 0x8a, 0x12, 0x7c, 0xcb, 0xd8, 0xf3, 0xc3, 0x45, 0x66, + 0xda, 0x9e, 0x5b, 0xa4, 0x8e, 0x66, 0xb1, 0xca, 0xbd, 0x40, 0xd1, 0x17, 0x0a, 0x5a, 0x1d, 0xb0, + 0x00, 0x84, 0x7d, 0x80, 0x26, 0x6b, 0xc6, 0x9e, 0xe0, 0x50, 0xe7, 0x29, 0x25, 0x7f, 0x79, 0xcb, + 0x7e, 0x12, 0x17, 0x78, 0x4c, 0x9b, 0x6d, 0xb7, 0xb2, 0x39, 0x41, 0x39, 0x31, 0x95, 0xe8, 0x63, + 0xb5, 0xb8, 0x3e, 0x71, 0xbb, 0x2e, 0x4a, 0x68, 0x6b, 0x2f, 0xa0, 0xff, 0x71, 0xcc, 0xae, 0x8b, + 0xcb, 0x06, 0xee, 0xef, 0xa2, 0xf1, 0x38, 0x42, 0xde, 0x1e, 0x10, 0x9f, 0x6e, 0xb7, 0xb2, 0x17, + 0x93, 0x89, 0x7b, 0x7b, 0x44, 0xc7, 0x35, 0x09, 0x3e, 0xee, 0xaa, 0xd1, 0x0c, 0x97, 0xf2, 0x5b, + 0xad, 0x73, 0x40, 0x7c, 0xa2, 0xc8, 0x77, 0x4d, 0x38, 0x0b, 0x28, 0x7e, 0x88, 0x4e, 0xfa, 0x97, + 0x4a, 0x89, 0x5f, 0x9a, 0xc1, 0xe9, 0x30, 0x93, 0xfc, 0xc6, 0x74, 0x20, 0x34, 0x15, 0x5e, 0x16, + 0x2c, 0x04, 0x84, 0x50, 0x88, 0x8e, 0xca, 0x9d, 0x4e, 0x24, 0x87, 0x32, 0x51, 0x1e, 0x6f, 0xd8, + 0x46, 0xd9, 0xa2, 0xd5, 0x80, 0xea, 0x26, 0xca, 0x26, 0x66, 0x00, 0xcd, 0x4b, 0x68, 0x84, 0x8a, + 0x47, 0x7c, 0xe9, 0xfe, 0xa7, 0xe1, 0xee, 0x9d, 0x07, 0x01, 0xa2, 0x07, 0x29, 0xe4, 0x73, 0x05, + 0x5d, 0x90, 0x2e, 0x7f, 0xc6, 0xac, 0xe0, 0x9e, 0xbb, 0x86, 0x50, 0x97, 0x2e, 0x6c, 0xe2, 0xb1, + 0xee, 0x01, 0xdd, 0x8d, 0x11, 0xfd, 0x44, 0x47, 0x09, 0xbe, 0x8e, 0x4e, 0x32, 0xef, 0x2e, 0x75, + 0xa0, 0xec, 0x08, 0x2f, 0x1b, 0xef, 0xae, 0x40, 0x28, 0x48, 0x74, 0xc4, 0xbf, 0xf1, 0x42, 0x72, + 0x1b, 0x4d, 0xc5, 0xb3, 0x01, 0x71, 0x2b, 0x68, 0x84, 0x8f, 0xde, 0xac, 0xc2, 0x7b, 0x11, 0x12, + 0x07, 0x01, 0xdf, 0x67, 0x30, 0x66, 0x6d, 0x54, 0xaf, 0x3c, 0x38, 0x8f, 0x8e, 0x73, 0x34, 0xfc, + 0xa9, 0x82, 0x86, 0x85, 0x4b, 0xc4, 0x7d, 0x8e, 0x73, 0xd9, 0x9c, 0xaa, 0xab, 0x03, 0x66, 0x0b, + 0x7a, 0x24, 0xf7, 0xd1, 0xaf, 0x7f, 0x7f, 0x73, 0x44, 0xc5, 0x13, 0x05, 0xc9, 0x33, 0x0b, 0x17, + 0x8a, 0x7f, 0x52, 0xd0, 0x64, 0xa2, 0xaf, 0xc4, 0xaf, 0xed, 0xd3, 0x6e, 0x3f, 0xef, 0xaa, 0xbe, + 0x7e, 0x70, 0x00, 0x90, 0xb0, 0xcc, 0x25, 0xcc, 0x62, 0x22, 0x4b, 0x88, 0x5a, 0xd3, 0xa8, 0x98, + 0x5e, 0xd3, 0x98, 0x46, 0x4c, 0xac, 0x7f, 0x4d, 0x23, 0x26, 0xde, 0xaf, 0xf6, 0x13, 0x03, 0xa6, + 0xcf, 0x3f, 0xb4, 0xf9, 0x7b, 0x88, 0x1f, 0x28, 0x68, 0x2c, 0xd6, 0x6c, 0xe2, 0x97, 0x07, 0xe7, + 0x21, 0xf9, 0x58, 0xf5, 0x95, 0x83, 0x15, 0x83, 0x80, 0x39, 0x2e, 0x20, 0x8b, 0x2f, 0xca, 0x02, + 0x0c, 0xcb, 0x2a, 0x81, 0x08, 0xfc, 0x9b, 0x82, 0xa6, 0xfa, 0x19, 0x4c, 0xac, 0x0d, 0xce, 0x22, + 0xc9, 0xf2, 0xaa, 0x37, 0x0f, 0x85, 0x01, 0x82, 0x56, 0xb9, 0xa0, 0x05, 0x3c, 0x27, 0x0b, 0xea, + 0xfa, 0x3b, 0x7f, 0x28, 0xdc, 0x30, 0xe1, 0xc7, 0x0a, 0xba, 0xd8, 0xd7, 0x78, 0xe2, 0x9b, 0xa9, + 0xd6, 0x37, 0xde, 0xe4, 0xaa, 0xeb, 0x87, 0x03, 0x01, 0x6d, 0x79, 0xae, 0x6d, 0x11, 0xcf, 0xc7, + 0x0f, 0x8b, 0x2b, 0x2a, 0x75, 0x55, 0xe2, 0x3f, 0x7a, 0xc5, 0xc9, 0x6e, 0x32, 0x8d, 0xb8, 0x44, + 0xff, 0x9b, 0x46, 0x5c, 0xb2, 0x2d, 0x26, 0x05, 0x2e, 0x6e, 0x09, 0x2f, 0xc8, 0xe2, 0x3c, 0xbf, + 0xaa, 0x54, 0x37, 0x4c, 0xa7, 0x64, 0x38, 0x65, 0xa1, 0xd3, 0xc5, 0x3f, 0x2a, 0xe8, 0x7c, 0x82, + 0x7f, 0xc5, 0x37, 0x52, 0xac, 0xb7, 0x6c, 0x8f, 0xd5, 0x57, 0x0f, 0x5a, 0x0e, 0x5a, 0x16, 0xb8, + 0x96, 0x69, 0x9c, 0x8d, 0x19, 0x54, 0xd8, 0x2f, 0xe3, 0x5f, 0x14, 0x74, 0xa1, 0x8f, 0xe3, 0xc5, + 0x6b, 0x83, 0x13, 0x49, 0x30, 0xd6, 0xaa, 0x76, 0x18, 0x08, 0xd0, 0xb3, 0xc2, 0xf5, 0xcc, 0xe1, + 0x19, 0x59, 0x8f, 0xe4, 0xb2, 0xf1, 0xcf, 0xbd, 0x87, 0x76, 0xaf, 0xaf, 0x4d, 0x73, 0x68, 0xc7, + 0x1a, 0xf1, 0x34, 0x87, 0x76, 0xbc, 0x3f, 0xef, 0xa7, 0x46, 0xb2, 0xd9, 0xf8, 0xcf, 0xde, 0x3d, + 0x24, 0x3b, 0xcc, 0x34, 0x7b, 0x28, 0xd1, 0xcd, 0xa6, 0xd9, 0x43, 0xc9, 0x26, 0x97, 0x3c, 0xcb, + 0x95, 0x2d, 0xe3, 0x45, 0x59, 0x59, 0xbc, 0xa9, 0xc5, 0xff, 0x28, 0x28, 0xb7, 0x9f, 0xff, 0xc7, + 0x6f, 0x1e, 0x9c, 0x5c, 0xf8, 0x17, 0x87, 0x7a, 0xeb, 0xd0, 0x38, 0xa0, 0xf3, 0x2a, 0xd7, 0xb9, + 0x8a, 0x57, 0x06, 0xd3, 0xc9, 0x7f, 0x75, 0x44, 0xef, 0xdf, 0xae, 0x01, 0x4f, 0x73, 0xff, 0x4a, + 0xe6, 0x3e, 0xcd, 0xfd, 0x2b, 0x7b, 0xfe, 0x7e, 0xf7, 0x6f, 0xc8, 0xc5, 0xe3, 0x1f, 0x14, 0x84, + 0x65, 0x4b, 0x8e, 0x5f, 0x18, 0xbc, 0x77, 0xaf, 0xcf, 0x57, 0x5f, 0x3c, 0x40, 0x25, 0x50, 0x9e, + 0xe6, 0x94, 0x2f, 0xe0, 0x49, 0x99, 0x32, 0x98, 0x7e, 0xfc, 0xbd, 0x82, 0xfe, 0x1f, 0x71, 0xd8, + 0xf8, 0xb9, 0x14, 0x66, 0xab, 0xfb, 0xfb, 0x40, 0x7d, 0x3e, 0x6d, 0x19, 0xb0, 0xcc, 0x70, 0x96, + 0x13, 0x78, 0x3c, 0xc6, 0x99, 0x31, 0x66, 0x69, 0x77, 0x1e, 0x3e, 0xc9, 0x28, 0x8f, 0x9e, 0x64, + 0x94, 0xbf, 0x9e, 0x64, 0x94, 0x2f, 0x9f, 0x66, 0x86, 0x1e, 0x3d, 0xcd, 0x0c, 0xfd, 0xfe, 0x34, + 0x33, 0xf4, 0xde, 0xb5, 0xd0, 0x7f, 0x54, 0xa1, 0x76, 0xd5, 0x32, 0xca, 0x6e, 0x07, 0x68, 0xf7, + 0xf2, 0xf5, 0xc2, 0x5e, 0xe8, 0x76, 0x6a, 0xd6, 0xa9, 0x5b, 0x1e, 0xe6, 0xdf, 0xaf, 0xfe, 0x1b, + 0x00, 0x00, 0xff, 0xff, 0xf0, 0x78, 0x37, 0xe1, 0x94, 0x17, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1558,9 +1561,9 @@ type QueryClient interface { GetProtoRevAdminAccount(ctx context.Context, in *QueryGetProtoRevAdminAccountRequest, opts ...grpc.CallOption) (*QueryGetProtoRevAdminAccountResponse, error) // GetProtoRevDeveloperAccount queries the developer account of the module GetProtoRevDeveloperAccount(ctx context.Context, in *QueryGetProtoRevDeveloperAccountRequest, opts ...grpc.CallOption) (*QueryGetProtoRevDeveloperAccountResponse, error) - // GetProtoRevPoolWeights queries the weights of each pool type currently - // being used by the module - GetProtoRevPoolWeights(ctx context.Context, in *QueryGetProtoRevPoolWeightsRequest, opts ...grpc.CallOption) (*QueryGetProtoRevPoolWeightsResponse, error) + // GetProtoRevInfoByPoolType queries pool type information that is currently + // being utilized by the module + GetProtoRevInfoByPoolType(ctx context.Context, in *QueryGetProtoRevInfoByPoolTypeRequest, opts ...grpc.CallOption) (*QueryGetProtoRevInfoByPoolTypeResponse, error) // GetProtoRevMaxPoolPointsPerTx queries the maximum number of pool points // that can be consumed per transaction GetProtoRevMaxPoolPointsPerTx(ctx context.Context, in *QueryGetProtoRevMaxPoolPointsPerTxRequest, opts ...grpc.CallOption) (*QueryGetProtoRevMaxPoolPointsPerTxResponse, error) @@ -1666,9 +1669,9 @@ func (c *queryClient) GetProtoRevDeveloperAccount(ctx context.Context, in *Query return out, nil } -func (c *queryClient) GetProtoRevPoolWeights(ctx context.Context, in *QueryGetProtoRevPoolWeightsRequest, opts ...grpc.CallOption) (*QueryGetProtoRevPoolWeightsResponse, error) { - out := new(QueryGetProtoRevPoolWeightsResponse) - err := c.cc.Invoke(ctx, "/osmosis.protorev.v1beta1.Query/GetProtoRevPoolWeights", in, out, opts...) +func (c *queryClient) GetProtoRevInfoByPoolType(ctx context.Context, in *QueryGetProtoRevInfoByPoolTypeRequest, opts ...grpc.CallOption) (*QueryGetProtoRevInfoByPoolTypeResponse, error) { + out := new(QueryGetProtoRevInfoByPoolTypeResponse) + err := c.cc.Invoke(ctx, "/osmosis.protorev.v1beta1.Query/GetProtoRevInfoByPoolType", in, out, opts...) if err != nil { return nil, err } @@ -1745,9 +1748,9 @@ type QueryServer interface { GetProtoRevAdminAccount(context.Context, *QueryGetProtoRevAdminAccountRequest) (*QueryGetProtoRevAdminAccountResponse, error) // GetProtoRevDeveloperAccount queries the developer account of the module GetProtoRevDeveloperAccount(context.Context, *QueryGetProtoRevDeveloperAccountRequest) (*QueryGetProtoRevDeveloperAccountResponse, error) - // GetProtoRevPoolWeights queries the weights of each pool type currently - // being used by the module - GetProtoRevPoolWeights(context.Context, *QueryGetProtoRevPoolWeightsRequest) (*QueryGetProtoRevPoolWeightsResponse, error) + // GetProtoRevInfoByPoolType queries pool type information that is currently + // being utilized by the module + GetProtoRevInfoByPoolType(context.Context, *QueryGetProtoRevInfoByPoolTypeRequest) (*QueryGetProtoRevInfoByPoolTypeResponse, error) // GetProtoRevMaxPoolPointsPerTx queries the maximum number of pool points // that can be consumed per transaction GetProtoRevMaxPoolPointsPerTx(context.Context, *QueryGetProtoRevMaxPoolPointsPerTxRequest) (*QueryGetProtoRevMaxPoolPointsPerTxResponse, error) @@ -1795,8 +1798,8 @@ func (*UnimplementedQueryServer) GetProtoRevAdminAccount(ctx context.Context, re func (*UnimplementedQueryServer) GetProtoRevDeveloperAccount(ctx context.Context, req *QueryGetProtoRevDeveloperAccountRequest) (*QueryGetProtoRevDeveloperAccountResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetProtoRevDeveloperAccount not implemented") } -func (*UnimplementedQueryServer) GetProtoRevPoolWeights(ctx context.Context, req *QueryGetProtoRevPoolWeightsRequest) (*QueryGetProtoRevPoolWeightsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetProtoRevPoolWeights not implemented") +func (*UnimplementedQueryServer) GetProtoRevInfoByPoolType(ctx context.Context, req *QueryGetProtoRevInfoByPoolTypeRequest) (*QueryGetProtoRevInfoByPoolTypeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetProtoRevInfoByPoolType not implemented") } func (*UnimplementedQueryServer) GetProtoRevMaxPoolPointsPerTx(ctx context.Context, req *QueryGetProtoRevMaxPoolPointsPerTxRequest) (*QueryGetProtoRevMaxPoolPointsPerTxResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetProtoRevMaxPoolPointsPerTx not implemented") @@ -1980,20 +1983,20 @@ func _Query_GetProtoRevDeveloperAccount_Handler(srv interface{}, ctx context.Con return interceptor(ctx, in, info, handler) } -func _Query_GetProtoRevPoolWeights_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(QueryGetProtoRevPoolWeightsRequest) +func _Query_GetProtoRevInfoByPoolType_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryGetProtoRevInfoByPoolTypeRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).GetProtoRevPoolWeights(ctx, in) + return srv.(QueryServer).GetProtoRevInfoByPoolType(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/osmosis.protorev.v1beta1.Query/GetProtoRevPoolWeights", + FullMethod: "/osmosis.protorev.v1beta1.Query/GetProtoRevInfoByPoolType", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).GetProtoRevPoolWeights(ctx, req.(*QueryGetProtoRevPoolWeightsRequest)) + return srv.(QueryServer).GetProtoRevInfoByPoolType(ctx, req.(*QueryGetProtoRevInfoByPoolTypeRequest)) } return interceptor(ctx, in, info, handler) } @@ -2129,8 +2132,8 @@ var _Query_serviceDesc = grpc.ServiceDesc{ Handler: _Query_GetProtoRevDeveloperAccount_Handler, }, { - MethodName: "GetProtoRevPoolWeights", - Handler: _Query_GetProtoRevPoolWeights_Handler, + MethodName: "GetProtoRevInfoByPoolType", + Handler: _Query_GetProtoRevInfoByPoolType_Handler, }, { MethodName: "GetProtoRevMaxPoolPointsPerTx", @@ -2694,7 +2697,7 @@ func (m *QueryGetProtoRevDeveloperAccountResponse) MarshalToSizedBuffer(dAtA []b return len(dAtA) - i, nil } -func (m *QueryGetProtoRevPoolWeightsRequest) Marshal() (dAtA []byte, err error) { +func (m *QueryGetProtoRevInfoByPoolTypeRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -2704,12 +2707,12 @@ func (m *QueryGetProtoRevPoolWeightsRequest) Marshal() (dAtA []byte, err error) return dAtA[:n], nil } -func (m *QueryGetProtoRevPoolWeightsRequest) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryGetProtoRevInfoByPoolTypeRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryGetProtoRevPoolWeightsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryGetProtoRevInfoByPoolTypeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -2717,7 +2720,7 @@ func (m *QueryGetProtoRevPoolWeightsRequest) MarshalToSizedBuffer(dAtA []byte) ( return len(dAtA) - i, nil } -func (m *QueryGetProtoRevPoolWeightsResponse) Marshal() (dAtA []byte, err error) { +func (m *QueryGetProtoRevInfoByPoolTypeResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -2727,18 +2730,18 @@ func (m *QueryGetProtoRevPoolWeightsResponse) Marshal() (dAtA []byte, err error) return dAtA[:n], nil } -func (m *QueryGetProtoRevPoolWeightsResponse) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryGetProtoRevInfoByPoolTypeResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryGetProtoRevPoolWeightsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryGetProtoRevInfoByPoolTypeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l { - size, err := m.PoolWeights.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.InfoByPoolType.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -3253,7 +3256,7 @@ func (m *QueryGetProtoRevDeveloperAccountResponse) Size() (n int) { return n } -func (m *QueryGetProtoRevPoolWeightsRequest) Size() (n int) { +func (m *QueryGetProtoRevInfoByPoolTypeRequest) Size() (n int) { if m == nil { return 0 } @@ -3262,13 +3265,13 @@ func (m *QueryGetProtoRevPoolWeightsRequest) Size() (n int) { return n } -func (m *QueryGetProtoRevPoolWeightsResponse) Size() (n int) { +func (m *QueryGetProtoRevInfoByPoolTypeResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l - l = m.PoolWeights.Size() + l = m.InfoByPoolType.Size() n += 1 + l + sovQuery(uint64(l)) return n } @@ -4705,7 +4708,7 @@ func (m *QueryGetProtoRevDeveloperAccountResponse) Unmarshal(dAtA []byte) error } return nil } -func (m *QueryGetProtoRevPoolWeightsRequest) Unmarshal(dAtA []byte) error { +func (m *QueryGetProtoRevInfoByPoolTypeRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -4728,10 +4731,10 @@ func (m *QueryGetProtoRevPoolWeightsRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryGetProtoRevPoolWeightsRequest: wiretype end group for non-group") + return fmt.Errorf("proto: QueryGetProtoRevInfoByPoolTypeRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryGetProtoRevPoolWeightsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryGetProtoRevInfoByPoolTypeRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: @@ -4755,7 +4758,7 @@ func (m *QueryGetProtoRevPoolWeightsRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryGetProtoRevPoolWeightsResponse) Unmarshal(dAtA []byte) error { +func (m *QueryGetProtoRevInfoByPoolTypeResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -4778,15 +4781,15 @@ func (m *QueryGetProtoRevPoolWeightsResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryGetProtoRevPoolWeightsResponse: wiretype end group for non-group") + return fmt.Errorf("proto: QueryGetProtoRevInfoByPoolTypeResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryGetProtoRevPoolWeightsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryGetProtoRevInfoByPoolTypeResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PoolWeights", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field InfoByPoolType", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -4813,7 +4816,7 @@ func (m *QueryGetProtoRevPoolWeightsResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.PoolWeights.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.InfoByPoolType.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/x/protorev/types/query.pb.gw.go b/x/protorev/types/query.pb.gw.go index 159161f6ef3..0aab022797c 100644 --- a/x/protorev/types/query.pb.gw.go +++ b/x/protorev/types/query.pb.gw.go @@ -231,20 +231,20 @@ func local_request_Query_GetProtoRevDeveloperAccount_0(ctx context.Context, mars } -func request_Query_GetProtoRevPoolWeights_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryGetProtoRevPoolWeightsRequest +func request_Query_GetProtoRevInfoByPoolType_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetProtoRevInfoByPoolTypeRequest var metadata runtime.ServerMetadata - msg, err := client.GetProtoRevPoolWeights(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.GetProtoRevInfoByPoolType(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_GetProtoRevPoolWeights_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryGetProtoRevPoolWeightsRequest +func local_request_Query_GetProtoRevInfoByPoolType_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetProtoRevInfoByPoolTypeRequest var metadata runtime.ServerMetadata - msg, err := server.GetProtoRevPoolWeights(ctx, &protoReq) + msg, err := server.GetProtoRevInfoByPoolType(ctx, &protoReq) return msg, metadata, err } @@ -570,7 +570,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) - mux.Handle("GET", pattern_Query_GetProtoRevPoolWeights_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetProtoRevInfoByPoolType_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream @@ -581,7 +581,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_GetProtoRevPoolWeights_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_GetProtoRevInfoByPoolType_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { @@ -589,7 +589,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } - forward_Query_GetProtoRevPoolWeights_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetProtoRevInfoByPoolType_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -929,7 +929,7 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) - mux.Handle("GET", pattern_Query_GetProtoRevPoolWeights_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_GetProtoRevInfoByPoolType_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -938,14 +938,14 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_GetProtoRevPoolWeights_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_GetProtoRevInfoByPoolType_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_GetProtoRevPoolWeights_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_GetProtoRevInfoByPoolType_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -1071,7 +1071,7 @@ var ( pattern_Query_GetProtoRevDeveloperAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"osmosis", "protorev", "developer_account"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_GetProtoRevPoolWeights_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"osmosis", "protorev", "pool_weights"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_GetProtoRevInfoByPoolType_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"osmosis", "protorev", "info_by_pool_type"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_GetProtoRevMaxPoolPointsPerTx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"osmosis", "protorev", "max_pool_points_per_tx"}, "", runtime.AssumeColonVerbOpt(false))) @@ -1103,7 +1103,7 @@ var ( forward_Query_GetProtoRevDeveloperAccount_0 = runtime.ForwardResponseMessage - forward_Query_GetProtoRevPoolWeights_0 = runtime.ForwardResponseMessage + forward_Query_GetProtoRevInfoByPoolType_0 = runtime.ForwardResponseMessage forward_Query_GetProtoRevMaxPoolPointsPerTx_0 = runtime.ForwardResponseMessage diff --git a/x/protorev/types/tx.pb.go b/x/protorev/types/tx.pb.go index a6f8966046e..57ace2f65ec 100644 --- a/x/protorev/types/tx.pb.go +++ b/x/protorev/types/tx.pb.go @@ -217,26 +217,26 @@ func (m *MsgSetDeveloperAccountResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgSetDeveloperAccountResponse proto.InternalMessageInfo -// MsgSetPoolWeights defines the Msg/SetPoolWeights request type. -type MsgSetPoolWeights struct { +// MsgSetInfoByPoolType defines the Msg/SetInfoByPoolType request type. +type MsgSetInfoByPoolType struct { // admin is the account that is authorized to set the pool weights. Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty" yaml:"admin"` - // pool_weights is the list of pool weights to set. - PoolWeights PoolWeights `protobuf:"bytes,2,opt,name=pool_weights,json=poolWeights,proto3" json:"pool_weights" yaml:"pool_weights"` + // info_by_pool_type contains information about the pool types. + InfoByPoolType InfoByPoolType `protobuf:"bytes,2,opt,name=info_by_pool_type,json=infoByPoolType,proto3" json:"info_by_pool_type" yaml:"info_by_pool_type"` } -func (m *MsgSetPoolWeights) Reset() { *m = MsgSetPoolWeights{} } -func (m *MsgSetPoolWeights) String() string { return proto.CompactTextString(m) } -func (*MsgSetPoolWeights) ProtoMessage() {} -func (*MsgSetPoolWeights) Descriptor() ([]byte, []int) { +func (m *MsgSetInfoByPoolType) Reset() { *m = MsgSetInfoByPoolType{} } +func (m *MsgSetInfoByPoolType) String() string { return proto.CompactTextString(m) } +func (*MsgSetInfoByPoolType) ProtoMessage() {} +func (*MsgSetInfoByPoolType) Descriptor() ([]byte, []int) { return fileDescriptor_2783dce032fc6954, []int{4} } -func (m *MsgSetPoolWeights) XXX_Unmarshal(b []byte) error { +func (m *MsgSetInfoByPoolType) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *MsgSetPoolWeights) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *MsgSetInfoByPoolType) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_MsgSetPoolWeights.Marshal(b, m, deterministic) + return xxx_messageInfo_MsgSetInfoByPoolType.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -246,48 +246,48 @@ func (m *MsgSetPoolWeights) XXX_Marshal(b []byte, deterministic bool) ([]byte, e return b[:n], nil } } -func (m *MsgSetPoolWeights) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgSetPoolWeights.Merge(m, src) +func (m *MsgSetInfoByPoolType) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetInfoByPoolType.Merge(m, src) } -func (m *MsgSetPoolWeights) XXX_Size() int { +func (m *MsgSetInfoByPoolType) XXX_Size() int { return m.Size() } -func (m *MsgSetPoolWeights) XXX_DiscardUnknown() { - xxx_messageInfo_MsgSetPoolWeights.DiscardUnknown(m) +func (m *MsgSetInfoByPoolType) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetInfoByPoolType.DiscardUnknown(m) } -var xxx_messageInfo_MsgSetPoolWeights proto.InternalMessageInfo +var xxx_messageInfo_MsgSetInfoByPoolType proto.InternalMessageInfo -func (m *MsgSetPoolWeights) GetAdmin() string { +func (m *MsgSetInfoByPoolType) GetAdmin() string { if m != nil { return m.Admin } return "" } -func (m *MsgSetPoolWeights) GetPoolWeights() PoolWeights { +func (m *MsgSetInfoByPoolType) GetInfoByPoolType() InfoByPoolType { if m != nil { - return m.PoolWeights + return m.InfoByPoolType } - return PoolWeights{} + return InfoByPoolType{} } -// MsgSetPoolWeightsResponse defines the Msg/SetPoolWeights response type. -type MsgSetPoolWeightsResponse struct { +// MsgSetInfoByPoolTypeResponse defines the Msg/SetInfoByPoolType response type. +type MsgSetInfoByPoolTypeResponse struct { } -func (m *MsgSetPoolWeightsResponse) Reset() { *m = MsgSetPoolWeightsResponse{} } -func (m *MsgSetPoolWeightsResponse) String() string { return proto.CompactTextString(m) } -func (*MsgSetPoolWeightsResponse) ProtoMessage() {} -func (*MsgSetPoolWeightsResponse) Descriptor() ([]byte, []int) { +func (m *MsgSetInfoByPoolTypeResponse) Reset() { *m = MsgSetInfoByPoolTypeResponse{} } +func (m *MsgSetInfoByPoolTypeResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSetInfoByPoolTypeResponse) ProtoMessage() {} +func (*MsgSetInfoByPoolTypeResponse) Descriptor() ([]byte, []int) { return fileDescriptor_2783dce032fc6954, []int{5} } -func (m *MsgSetPoolWeightsResponse) XXX_Unmarshal(b []byte) error { +func (m *MsgSetInfoByPoolTypeResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *MsgSetPoolWeightsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *MsgSetInfoByPoolTypeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_MsgSetPoolWeightsResponse.Marshal(b, m, deterministic) + return xxx_messageInfo_MsgSetInfoByPoolTypeResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -297,17 +297,17 @@ func (m *MsgSetPoolWeightsResponse) XXX_Marshal(b []byte, deterministic bool) ([ return b[:n], nil } } -func (m *MsgSetPoolWeightsResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgSetPoolWeightsResponse.Merge(m, src) +func (m *MsgSetInfoByPoolTypeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetInfoByPoolTypeResponse.Merge(m, src) } -func (m *MsgSetPoolWeightsResponse) XXX_Size() int { +func (m *MsgSetInfoByPoolTypeResponse) XXX_Size() int { return m.Size() } -func (m *MsgSetPoolWeightsResponse) XXX_DiscardUnknown() { - xxx_messageInfo_MsgSetPoolWeightsResponse.DiscardUnknown(m) +func (m *MsgSetInfoByPoolTypeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetInfoByPoolTypeResponse.DiscardUnknown(m) } -var xxx_messageInfo_MsgSetPoolWeightsResponse proto.InternalMessageInfo +var xxx_messageInfo_MsgSetInfoByPoolTypeResponse proto.InternalMessageInfo // MsgSetMaxPoolPointsPerTx defines the Msg/SetMaxPoolPointsPerTx request type. type MsgSetMaxPoolPointsPerTx struct { @@ -596,8 +596,8 @@ func init() { proto.RegisterType((*MsgSetHotRoutesResponse)(nil), "osmosis.protorev.v1beta1.MsgSetHotRoutesResponse") proto.RegisterType((*MsgSetDeveloperAccount)(nil), "osmosis.protorev.v1beta1.MsgSetDeveloperAccount") proto.RegisterType((*MsgSetDeveloperAccountResponse)(nil), "osmosis.protorev.v1beta1.MsgSetDeveloperAccountResponse") - proto.RegisterType((*MsgSetPoolWeights)(nil), "osmosis.protorev.v1beta1.MsgSetPoolWeights") - proto.RegisterType((*MsgSetPoolWeightsResponse)(nil), "osmosis.protorev.v1beta1.MsgSetPoolWeightsResponse") + proto.RegisterType((*MsgSetInfoByPoolType)(nil), "osmosis.protorev.v1beta1.MsgSetInfoByPoolType") + proto.RegisterType((*MsgSetInfoByPoolTypeResponse)(nil), "osmosis.protorev.v1beta1.MsgSetInfoByPoolTypeResponse") proto.RegisterType((*MsgSetMaxPoolPointsPerTx)(nil), "osmosis.protorev.v1beta1.MsgSetMaxPoolPointsPerTx") proto.RegisterType((*MsgSetMaxPoolPointsPerTxResponse)(nil), "osmosis.protorev.v1beta1.MsgSetMaxPoolPointsPerTxResponse") proto.RegisterType((*MsgSetMaxPoolPointsPerBlock)(nil), "osmosis.protorev.v1beta1.MsgSetMaxPoolPointsPerBlock") @@ -609,62 +609,63 @@ func init() { func init() { proto.RegisterFile("osmosis/protorev/v1beta1/tx.proto", fileDescriptor_2783dce032fc6954) } var fileDescriptor_2783dce032fc6954 = []byte{ - // 876 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x95, 0x4f, 0x6f, 0xe3, 0x44, - 0x18, 0xc6, 0x33, 0x5d, 0x40, 0xec, 0xa4, 0xc0, 0xc6, 0xfb, 0xcf, 0x71, 0xbb, 0x4e, 0x3a, 0xd9, - 0x6a, 0xd3, 0xb2, 0x1b, 0x93, 0xec, 0xa2, 0x45, 0x91, 0x40, 0xaa, 0xb5, 0x07, 0x38, 0x14, 0x45, - 0xde, 0x45, 0x48, 0x1c, 0x30, 0x76, 0x32, 0x38, 0xd6, 0xc6, 0x1e, 0xcb, 0x33, 0x0d, 0xd9, 0x2b, - 0x47, 0x4e, 0x48, 0x48, 0x1c, 0x90, 0xe0, 0x33, 0x20, 0xc4, 0x95, 0x7b, 0xb9, 0x55, 0xf4, 0x40, - 0x85, 0x44, 0x84, 0x5a, 0x24, 0xee, 0xf9, 0x04, 0xc8, 0x33, 0x8e, 0xf3, 0xcf, 0x26, 0x0d, 0xb9, - 0x54, 0xcd, 0xcc, 0x33, 0xcf, 0xfb, 0x7b, 0xde, 0x19, 0xbd, 0x86, 0x3b, 0x84, 0x7a, 0x84, 0xba, - 0x54, 0x0b, 0x42, 0xc2, 0x48, 0x88, 0xfb, 0x5a, 0xbf, 0x6e, 0x63, 0x66, 0xd5, 0x35, 0x36, 0xa8, - 0xf1, 0x35, 0x49, 0x8e, 0x25, 0xb5, 0xb1, 0xa4, 0x16, 0x4b, 0x94, 0x1b, 0x0e, 0x71, 0x08, 0x5f, - 0xd5, 0xa2, 0xff, 0x84, 0x40, 0x29, 0x58, 0x9e, 0xeb, 0x13, 0x8d, 0xff, 0x8d, 0x97, 0xb6, 0x1d, - 0x42, 0x9c, 0x1e, 0xd6, 0xac, 0xc0, 0xd5, 0x2c, 0xdf, 0x27, 0xcc, 0x62, 0x2e, 0xf1, 0x63, 0x47, - 0xe5, 0x5e, 0x26, 0x43, 0x52, 0x51, 0x08, 0x8b, 0x6d, 0xae, 0x34, 0x45, 0x49, 0xf1, 0x43, 0x6c, - 0xa1, 0xdf, 0x01, 0x7c, 0xe3, 0x90, 0x3a, 0x4f, 0x31, 0x7b, 0x9f, 0x30, 0x83, 0x1c, 0x31, 0x4c, - 0xa5, 0xf7, 0xe0, 0xcb, 0x56, 0xc7, 0x73, 0x7d, 0x19, 0x94, 0x41, 0xf5, 0xaa, 0x5e, 0x1d, 0x0d, - 0x4b, 0x9b, 0x2f, 0x2c, 0xaf, 0xd7, 0x44, 0x7c, 0x19, 0xfd, 0xf6, 0xf3, 0x83, 0x1b, 0xb1, 0xc9, - 0x41, 0xa7, 0x13, 0x62, 0x4a, 0x9f, 0xb2, 0xd0, 0xf5, 0x1d, 0x43, 0x1c, 0x93, 0x3e, 0x87, 0xb0, - 0x4b, 0x98, 0x19, 0x72, 0x37, 0x79, 0xa3, 0x7c, 0xa5, 0x9a, 0x6f, 0xdc, 0xaf, 0x65, 0x75, 0xa3, - 0xf6, 0x8c, 0x3c, 0xc7, 0x7e, 0xcb, 0x72, 0xc3, 0x83, 0xd0, 0x16, 0x04, 0x7a, 0xf1, 0x78, 0x58, - 0xca, 0x8d, 0x86, 0xa5, 0x82, 0x28, 0x3b, 0x71, 0x43, 0xc6, 0xd5, 0xee, 0x98, 0xb3, 0xb9, 0xfd, - 0xd5, 0x3f, 0x3f, 0xee, 0xdf, 0x1e, 0x37, 0x61, 0x2e, 0x05, 0x2a, 0xc2, 0xdb, 0x73, 0x4b, 0x06, - 0xa6, 0x01, 0xf1, 0x29, 0x46, 0xc7, 0x00, 0xde, 0x12, 0x7b, 0x4f, 0x70, 0x1f, 0xf7, 0x48, 0x80, - 0xc3, 0x83, 0x76, 0x9b, 0x1c, 0xf9, 0x6c, 0xed, 0xec, 0x1f, 0xc0, 0x42, 0x67, 0xec, 0x69, 0x5a, - 0xc2, 0x54, 0xde, 0xe0, 0x5e, 0xdb, 0xa3, 0x61, 0x49, 0x16, 0x5e, 0x0b, 0x12, 0x64, 0x5c, 0xeb, - 0xcc, 0xa1, 0x34, 0x2b, 0x51, 0x3c, 0x75, 0x36, 0xde, 0x3c, 0x2f, 0x2a, 0x43, 0x35, 0x7d, 0x27, - 0x09, 0xfb, 0x07, 0x80, 0x05, 0x21, 0x69, 0x11, 0xd2, 0xfb, 0x18, 0xbb, 0x4e, 0x97, 0xad, 0x7f, - 0xc7, 0x18, 0x6e, 0x06, 0x84, 0xf4, 0xcc, 0x2f, 0x84, 0x1f, 0x8f, 0x98, 0x6f, 0xec, 0x66, 0xdf, - 0xf2, 0x54, 0x71, 0x7d, 0x2b, 0xbe, 0xde, 0xeb, 0xa2, 0xe2, 0xb4, 0x11, 0x32, 0xf2, 0xc1, 0x44, - 0xd9, 0x54, 0xa3, 0x1e, 0x14, 0x67, 0x7b, 0x30, 0xe5, 0x84, 0xb6, 0x60, 0x71, 0x61, 0x31, 0x49, - 0x7e, 0x06, 0xa0, 0x2c, 0x76, 0x0f, 0xad, 0x41, 0x24, 0x68, 0x11, 0xd7, 0x67, 0xb4, 0x85, 0xc3, - 0x67, 0x83, 0xb5, 0x1b, 0xf0, 0x11, 0xbc, 0xe5, 0x59, 0x03, 0x93, 0xb3, 0x07, 0xdc, 0xd7, 0x8c, - 0xee, 0x93, 0x0d, 0x78, 0x2b, 0x5e, 0xd2, 0x77, 0x46, 0xc3, 0xd2, 0x1d, 0x61, 0x98, 0xae, 0x43, - 0x86, 0xe4, 0x2d, 0x60, 0x35, 0x77, 0xa3, 0xc0, 0xe5, 0xd9, 0xc0, 0x8b, 0xf4, 0x08, 0xc1, 0x72, - 0xd6, 0x5e, 0x12, 0xff, 0x4f, 0x00, 0xb7, 0xd2, 0x45, 0x7a, 0x8f, 0xb4, 0x9f, 0xaf, 0xdd, 0x81, - 0x4f, 0x61, 0x31, 0x2d, 0x99, 0x1d, 0x99, 0xc7, 0x4d, 0xb8, 0x3b, 0x1a, 0x96, 0xca, 0xd9, 0x4d, - 0xe0, 0x52, 0x64, 0xdc, 0xf4, 0xd2, 0xf8, 0x96, 0xde, 0xfd, 0x2e, 0xac, 0xfc, 0x47, 0xbc, 0xa4, - 0x0d, 0xa7, 0x00, 0x5e, 0x13, 0x3a, 0xdd, 0xa2, 0xf8, 0x09, 0xf6, 0x89, 0xb7, 0xfe, 0xf3, 0xff, - 0x0c, 0xe6, 0x6d, 0x8b, 0x62, 0xb3, 0xc3, 0xed, 0xe2, 0x19, 0x57, 0xc9, 0x7e, 0xfd, 0x49, 0x69, - 0x5d, 0x89, 0xdf, 0xbe, 0x24, 0xca, 0x4d, 0xb9, 0x20, 0x03, 0xda, 0x09, 0x61, 0xf3, 0x4e, 0x94, - 0x5e, 0x9e, 0x4d, 0x3f, 0x09, 0x80, 0x94, 0xf1, 0xd3, 0x9e, 0xac, 0x8d, 0x13, 0x37, 0xbe, 0x7f, - 0x15, 0x5e, 0x39, 0xa4, 0x8e, 0xf4, 0x2d, 0x80, 0x9b, 0x33, 0x83, 0x7d, 0x2f, 0x1b, 0x70, 0x6e, - 0x54, 0x2a, 0xf5, 0x4b, 0x4b, 0x93, 0x46, 0x57, 0xbf, 0x3c, 0xfd, 0xfb, 0x9b, 0x0d, 0x84, 0xca, - 0xda, 0xc2, 0x77, 0x89, 0x62, 0x66, 0x4e, 0x86, 0xb8, 0xf4, 0x13, 0x80, 0xd7, 0xd3, 0x86, 0xef, - 0x5b, 0xcb, 0x8a, 0xce, 0x9f, 0x50, 0xde, 0x59, 0xf5, 0x44, 0x42, 0xab, 0x71, 0xda, 0x3d, 0x74, - 0x2f, 0x9d, 0x76, 0x61, 0x42, 0x4b, 0xbf, 0x00, 0x78, 0x33, 0x7d, 0x94, 0x34, 0x96, 0x41, 0x2c, - 0x9e, 0x51, 0x9a, 0xab, 0x9f, 0x49, 0xd0, 0x1f, 0x71, 0xf4, 0x1a, 0xba, 0x9f, 0x8e, 0x9e, 0x3e, - 0x6e, 0xa4, 0x5f, 0x01, 0x94, 0x33, 0x67, 0xc1, 0xdb, 0xab, 0xe2, 0xf0, 0x63, 0xca, 0xbb, 0xff, - 0xeb, 0x58, 0x12, 0xe4, 0x31, 0x0f, 0x52, 0x47, 0xda, 0xe5, 0x83, 0xf0, 0x91, 0x21, 0xfd, 0x00, - 0xe0, 0xeb, 0x73, 0x1f, 0xb4, 0x37, 0x97, 0xa1, 0x4c, 0x89, 0x95, 0x87, 0x2b, 0x88, 0x13, 0xda, - 0x7d, 0x4e, 0x7b, 0x17, 0xa1, 0x74, 0xda, 0xe9, 0xaf, 0x98, 0xf4, 0x1d, 0x80, 0xaf, 0xcd, 0x4e, - 0x9c, 0xfd, 0x65, 0x25, 0x27, 0x5a, 0xa5, 0x71, 0x79, 0x6d, 0x42, 0xb7, 0xc7, 0xe9, 0x2a, 0x68, - 0x27, 0x9d, 0x6e, 0x6a, 0xce, 0xe8, 0x1f, 0x1e, 0x9f, 0xab, 0xe0, 0xe4, 0x5c, 0x05, 0x7f, 0x9d, - 0xab, 0xe0, 0xeb, 0x0b, 0x35, 0x77, 0x72, 0xa1, 0xe6, 0xce, 0x2e, 0xd4, 0xdc, 0x27, 0x8f, 0x1c, - 0x97, 0x75, 0x8f, 0xec, 0x5a, 0x9b, 0x78, 0x63, 0x9b, 0x07, 0x3d, 0xcb, 0xa6, 0x89, 0x67, 0xbf, - 0xfe, 0x58, 0x1b, 0x4c, 0x9c, 0xd9, 0x8b, 0x00, 0x53, 0xfb, 0x15, 0xfe, 0xfb, 0xe1, 0xbf, 0x01, - 0x00, 0x00, 0xff, 0xff, 0xc6, 0xa0, 0x48, 0x69, 0x14, 0x0b, 0x00, 0x00, + // 889 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0xc1, 0x6f, 0xdb, 0x54, + 0x1c, 0xee, 0xeb, 0x00, 0xb1, 0xd7, 0x01, 0x8b, 0xe9, 0xb6, 0xc4, 0x64, 0x8e, 0xfb, 0xca, 0xb4, + 0x74, 0xda, 0x6c, 0x12, 0x06, 0x43, 0x96, 0x40, 0xaa, 0xb5, 0x03, 0x3b, 0x14, 0x55, 0x5e, 0x11, + 0x12, 0x07, 0x8c, 0x9d, 0xbc, 0xba, 0xd6, 0x62, 0x3f, 0xcb, 0xef, 0xb5, 0x4a, 0xae, 0x1c, 0x39, + 0x21, 0x21, 0x71, 0xe0, 0x5f, 0x80, 0x03, 0x42, 0x5c, 0xb9, 0x97, 0xdb, 0xc4, 0x0e, 0xec, 0x42, + 0x84, 0x5a, 0x24, 0xae, 0x28, 0x7f, 0x01, 0xf2, 0x7b, 0x8e, 0x53, 0xc7, 0x36, 0x6d, 0x96, 0x4b, + 0x14, 0xff, 0xde, 0xf7, 0xfb, 0x7e, 0xdf, 0xf7, 0xd9, 0xfe, 0x19, 0x6e, 0x10, 0x1a, 0x10, 0xea, + 0x53, 0x3d, 0x8a, 0x09, 0x23, 0x31, 0x3e, 0xd2, 0x8f, 0x3a, 0x2e, 0x66, 0x4e, 0x47, 0x67, 0x43, + 0x8d, 0xd7, 0xa4, 0x7a, 0x0a, 0xd1, 0xa6, 0x10, 0x2d, 0x85, 0xc8, 0xeb, 0x1e, 0xf1, 0x08, 0xaf, + 0xea, 0xc9, 0x3f, 0x01, 0x90, 0x6b, 0x4e, 0xe0, 0x87, 0x44, 0xe7, 0xbf, 0x69, 0xa9, 0xe9, 0x11, + 0xe2, 0x0d, 0xb0, 0xee, 0x44, 0xbe, 0xee, 0x84, 0x21, 0x61, 0x0e, 0xf3, 0x49, 0x98, 0x32, 0xca, + 0xb7, 0x2b, 0x35, 0x64, 0x13, 0x05, 0xb0, 0xd1, 0xe3, 0x48, 0x5b, 0x8c, 0x14, 0x17, 0xe2, 0x08, + 0xfd, 0x01, 0xe0, 0x1b, 0x3b, 0xd4, 0x7b, 0x8c, 0xd9, 0xc7, 0x84, 0x59, 0xe4, 0x90, 0x61, 0x2a, + 0x7d, 0x04, 0x5f, 0x76, 0xfa, 0x81, 0x1f, 0xd6, 0x81, 0x0a, 0xda, 0x97, 0xcd, 0xf6, 0x64, 0xdc, + 0xba, 0x32, 0x72, 0x82, 0x81, 0x81, 0x78, 0x19, 0xfd, 0xfe, 0xcb, 0xbd, 0xf5, 0x94, 0x64, 0xbb, + 0xdf, 0x8f, 0x31, 0xa5, 0x8f, 0x59, 0xec, 0x87, 0x9e, 0x25, 0xda, 0xa4, 0x7d, 0x08, 0x0f, 0x08, + 0xb3, 0x63, 0xce, 0x56, 0x5f, 0x55, 0x2f, 0xb5, 0xd7, 0xba, 0x77, 0xb5, 0xaa, 0x34, 0xb4, 0x3d, + 0xf2, 0x04, 0x87, 0xbb, 0x8e, 0x1f, 0x6f, 0xc7, 0xae, 0x50, 0x60, 0x36, 0x8e, 0xc7, 0xad, 0x95, + 0xc9, 0xb8, 0x55, 0x13, 0x63, 0x67, 0x6c, 0xc8, 0xba, 0x7c, 0x30, 0xd5, 0x69, 0x34, 0xbf, 0xfe, + 0xe7, 0xa7, 0x3b, 0x37, 0xa6, 0x21, 0xcc, 0xb9, 0x40, 0x0d, 0x78, 0x63, 0xae, 0x64, 0x61, 0x1a, + 0x91, 0x90, 0x62, 0x74, 0x0c, 0xe0, 0x75, 0x71, 0xf6, 0x10, 0x1f, 0xe1, 0x01, 0x89, 0x70, 0xbc, + 0xdd, 0xeb, 0x91, 0xc3, 0x90, 0x2d, 0xed, 0xfd, 0x11, 0xac, 0xf5, 0xa7, 0x9c, 0xb6, 0x23, 0x48, + 0xeb, 0xab, 0x9c, 0xab, 0x39, 0x19, 0xb7, 0xea, 0x82, 0xab, 0x00, 0x41, 0xd6, 0xd5, 0xfe, 0x9c, + 0x14, 0x63, 0x33, 0xb1, 0xa7, 0xe4, 0xed, 0xcd, 0xeb, 0x45, 0x2a, 0x54, 0xca, 0x4f, 0x32, 0xb3, + 0xff, 0x02, 0xb8, 0x2e, 0x20, 0x8f, 0xc2, 0x7d, 0x62, 0x8e, 0x76, 0x09, 0x19, 0xec, 0x8d, 0x22, + 0xbc, 0xb4, 0xd5, 0x43, 0x58, 0xf3, 0xc3, 0x7d, 0x62, 0xbb, 0x23, 0x3b, 0x22, 0x64, 0x60, 0xb3, + 0x51, 0x84, 0xb9, 0xd5, 0xb5, 0x6e, 0xbb, 0xfa, 0x6e, 0xe7, 0x45, 0x98, 0x6a, 0x7a, 0xa7, 0xd3, + 0x60, 0x0a, 0x84, 0xc8, 0x7a, 0xdd, 0xcf, 0x75, 0x18, 0x1b, 0x49, 0x2c, 0xcd, 0x7c, 0x2c, 0x79, + 0x52, 0xa4, 0xc0, 0x66, 0x59, 0x3d, 0x8b, 0xe4, 0x39, 0x80, 0x75, 0x01, 0xd8, 0x71, 0x86, 0xc9, + 0xe9, 0x2e, 0xf1, 0x43, 0x46, 0x77, 0x71, 0xbc, 0x37, 0x5c, 0x3a, 0x96, 0x4f, 0xe1, 0xf5, 0xc0, + 0x19, 0x0a, 0x07, 0x11, 0xe7, 0xb5, 0x93, 0x1b, 0xcd, 0x86, 0x3c, 0x9b, 0x97, 0xcc, 0x8d, 0xc9, + 0xb8, 0x75, 0x53, 0x10, 0x96, 0xe3, 0x90, 0x25, 0x05, 0x05, 0x59, 0xc6, 0xad, 0xc4, 0xb6, 0x9a, + 0xb7, 0x5d, 0x54, 0x8f, 0x10, 0x54, 0xab, 0xce, 0x32, 0xfb, 0x7f, 0x02, 0xf8, 0x56, 0x39, 0xc8, + 0x1c, 0x90, 0xde, 0x93, 0xa5, 0x13, 0xf8, 0x02, 0x36, 0xca, 0x9c, 0xb9, 0x09, 0x79, 0x1a, 0xc2, + 0xdb, 0x93, 0x71, 0x4b, 0xad, 0x0e, 0x81, 0x43, 0x91, 0x75, 0x2d, 0x28, 0xd3, 0x67, 0x28, 0x49, + 0x14, 0x8d, 0x7c, 0x14, 0x09, 0xec, 0x33, 0xec, 0x7b, 0x07, 0x8c, 0xa2, 0x5b, 0x70, 0xf3, 0x7f, + 0xec, 0x65, 0x31, 0x3c, 0x03, 0xf0, 0xaa, 0xc0, 0x99, 0x0e, 0xc5, 0x0f, 0x71, 0x48, 0x82, 0xe5, + 0x77, 0xdf, 0x97, 0x70, 0xcd, 0x75, 0x28, 0xb6, 0xfb, 0x9c, 0x2e, 0x5d, 0x7e, 0x9b, 0xd5, 0xaf, + 0x43, 0x36, 0xda, 0x94, 0xd3, 0x37, 0x41, 0x12, 0xe3, 0xce, 0xb0, 0x20, 0x0b, 0xba, 0x99, 0x42, + 0xe3, 0x66, 0xe2, 0xbe, 0x9e, 0x77, 0x3f, 0x33, 0x80, 0xe4, 0xe9, 0xa3, 0x3d, 0xab, 0x4d, 0x1d, + 0x77, 0x7f, 0x78, 0x15, 0x5e, 0xda, 0xa1, 0x9e, 0xf4, 0x1d, 0x80, 0x57, 0x72, 0x1b, 0x7f, 0xab, + 0x5a, 0xe0, 0xdc, 0x0e, 0x95, 0x3b, 0x17, 0x86, 0x66, 0x41, 0xb7, 0xbf, 0x7a, 0xf6, 0xf7, 0xb7, + 0xab, 0x08, 0xa9, 0x7a, 0xe1, 0x83, 0x45, 0x31, 0xb3, 0x67, 0xdb, 0x5d, 0xfa, 0x19, 0xc0, 0x37, + 0xcb, 0xb6, 0xf2, 0x3b, 0xe7, 0x0d, 0x9d, 0xef, 0x90, 0x3f, 0x58, 0xb4, 0x23, 0x53, 0xab, 0x73, + 0xb5, 0x5b, 0xe8, 0x76, 0xb9, 0xda, 0xc2, 0xea, 0x96, 0x7e, 0x05, 0xf0, 0x5a, 0xf9, 0x2a, 0xe9, + 0x9e, 0x27, 0xa2, 0xd8, 0x23, 0x1b, 0x8b, 0xf7, 0x64, 0xd2, 0xef, 0x73, 0xe9, 0x1a, 0xba, 0x5b, + 0x2e, 0xbd, 0x7c, 0xdd, 0x48, 0xbf, 0x01, 0x58, 0xaf, 0xdc, 0x05, 0xef, 0x2d, 0x2a, 0x87, 0xb7, + 0xc9, 0x1f, 0xbe, 0x50, 0x5b, 0x66, 0xe4, 0x01, 0x37, 0xd2, 0x41, 0xfa, 0xc5, 0x8d, 0xf0, 0x95, + 0x21, 0xfd, 0x08, 0x60, 0xad, 0xf8, 0xa5, 0xd3, 0xce, 0x53, 0x93, 0xc7, 0xcb, 0xef, 0x2f, 0x86, + 0xbf, 0xe8, 0xa3, 0x53, 0xf8, 0xb8, 0x49, 0xdf, 0x03, 0xf8, 0x5a, 0x7e, 0xff, 0xdc, 0x39, 0x6f, + 0xf4, 0x0c, 0x2b, 0x77, 0x2f, 0x8e, 0xcd, 0x24, 0x6e, 0x71, 0x89, 0x9b, 0x68, 0xa3, 0x5c, 0xe2, + 0x99, 0xad, 0x63, 0x7e, 0x72, 0x7c, 0xa2, 0x80, 0xa7, 0x27, 0x0a, 0xf8, 0xeb, 0x44, 0x01, 0xdf, + 0x9c, 0x2a, 0x2b, 0x4f, 0x4f, 0x95, 0x95, 0xe7, 0xa7, 0xca, 0xca, 0xe7, 0xf7, 0x3d, 0x9f, 0x1d, + 0x1c, 0xba, 0x5a, 0x8f, 0x04, 0x53, 0x9a, 0x7b, 0x03, 0xc7, 0xa5, 0x19, 0xe7, 0x51, 0xe7, 0x81, + 0x3e, 0x9c, 0x31, 0x27, 0x5e, 0xa9, 0xfb, 0x0a, 0xbf, 0x7e, 0xf7, 0xbf, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x62, 0x22, 0x73, 0xe0, 0x3b, 0x0b, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -691,9 +692,9 @@ type MsgClient interface { // SetMaxPoolPointsPerBlock sets the maximum number of pool points that can be // consumed per block. Can only be called by the admin account. SetMaxPoolPointsPerBlock(ctx context.Context, in *MsgSetMaxPoolPointsPerBlock, opts ...grpc.CallOption) (*MsgSetMaxPoolPointsPerBlockResponse, error) - // SetPoolWeights sets the weights of each pool type in the store. Can only be - // called by the admin account. - SetPoolWeights(ctx context.Context, in *MsgSetPoolWeights, opts ...grpc.CallOption) (*MsgSetPoolWeightsResponse, error) + // SetInfoByPoolType sets the pool type information needed to make smart + // assumptions about swapping on different pool types + SetInfoByPoolType(ctx context.Context, in *MsgSetInfoByPoolType, opts ...grpc.CallOption) (*MsgSetInfoByPoolTypeResponse, error) // SetBaseDenoms sets the base denoms that will be used to create cyclic // arbitrage routes. Can only be called by the admin account. SetBaseDenoms(ctx context.Context, in *MsgSetBaseDenoms, opts ...grpc.CallOption) (*MsgSetBaseDenomsResponse, error) @@ -743,9 +744,9 @@ func (c *msgClient) SetMaxPoolPointsPerBlock(ctx context.Context, in *MsgSetMaxP return out, nil } -func (c *msgClient) SetPoolWeights(ctx context.Context, in *MsgSetPoolWeights, opts ...grpc.CallOption) (*MsgSetPoolWeightsResponse, error) { - out := new(MsgSetPoolWeightsResponse) - err := c.cc.Invoke(ctx, "/osmosis.protorev.v1beta1.Msg/SetPoolWeights", in, out, opts...) +func (c *msgClient) SetInfoByPoolType(ctx context.Context, in *MsgSetInfoByPoolType, opts ...grpc.CallOption) (*MsgSetInfoByPoolTypeResponse, error) { + out := new(MsgSetInfoByPoolTypeResponse) + err := c.cc.Invoke(ctx, "/osmosis.protorev.v1beta1.Msg/SetInfoByPoolType", in, out, opts...) if err != nil { return nil, err } @@ -775,9 +776,9 @@ type MsgServer interface { // SetMaxPoolPointsPerBlock sets the maximum number of pool points that can be // consumed per block. Can only be called by the admin account. SetMaxPoolPointsPerBlock(context.Context, *MsgSetMaxPoolPointsPerBlock) (*MsgSetMaxPoolPointsPerBlockResponse, error) - // SetPoolWeights sets the weights of each pool type in the store. Can only be - // called by the admin account. - SetPoolWeights(context.Context, *MsgSetPoolWeights) (*MsgSetPoolWeightsResponse, error) + // SetInfoByPoolType sets the pool type information needed to make smart + // assumptions about swapping on different pool types + SetInfoByPoolType(context.Context, *MsgSetInfoByPoolType) (*MsgSetInfoByPoolTypeResponse, error) // SetBaseDenoms sets the base denoms that will be used to create cyclic // arbitrage routes. Can only be called by the admin account. SetBaseDenoms(context.Context, *MsgSetBaseDenoms) (*MsgSetBaseDenomsResponse, error) @@ -799,8 +800,8 @@ func (*UnimplementedMsgServer) SetMaxPoolPointsPerTx(ctx context.Context, req *M func (*UnimplementedMsgServer) SetMaxPoolPointsPerBlock(ctx context.Context, req *MsgSetMaxPoolPointsPerBlock) (*MsgSetMaxPoolPointsPerBlockResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SetMaxPoolPointsPerBlock not implemented") } -func (*UnimplementedMsgServer) SetPoolWeights(ctx context.Context, req *MsgSetPoolWeights) (*MsgSetPoolWeightsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SetPoolWeights not implemented") +func (*UnimplementedMsgServer) SetInfoByPoolType(ctx context.Context, req *MsgSetInfoByPoolType) (*MsgSetInfoByPoolTypeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetInfoByPoolType not implemented") } func (*UnimplementedMsgServer) SetBaseDenoms(ctx context.Context, req *MsgSetBaseDenoms) (*MsgSetBaseDenomsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SetBaseDenoms not implemented") @@ -882,20 +883,20 @@ func _Msg_SetMaxPoolPointsPerBlock_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } -func _Msg_SetPoolWeights_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MsgSetPoolWeights) +func _Msg_SetInfoByPoolType_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSetInfoByPoolType) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(MsgServer).SetPoolWeights(ctx, in) + return srv.(MsgServer).SetInfoByPoolType(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/osmosis.protorev.v1beta1.Msg/SetPoolWeights", + FullMethod: "/osmosis.protorev.v1beta1.Msg/SetInfoByPoolType", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).SetPoolWeights(ctx, req.(*MsgSetPoolWeights)) + return srv.(MsgServer).SetInfoByPoolType(ctx, req.(*MsgSetInfoByPoolType)) } return interceptor(ctx, in, info, handler) } @@ -939,8 +940,8 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ Handler: _Msg_SetMaxPoolPointsPerBlock_Handler, }, { - MethodName: "SetPoolWeights", - Handler: _Msg_SetPoolWeights_Handler, + MethodName: "SetInfoByPoolType", + Handler: _Msg_SetInfoByPoolType_Handler, }, { MethodName: "SetBaseDenoms", @@ -1078,7 +1079,7 @@ func (m *MsgSetDeveloperAccountResponse) MarshalToSizedBuffer(dAtA []byte) (int, return len(dAtA) - i, nil } -func (m *MsgSetPoolWeights) Marshal() (dAtA []byte, err error) { +func (m *MsgSetInfoByPoolType) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1088,18 +1089,18 @@ func (m *MsgSetPoolWeights) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *MsgSetPoolWeights) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgSetInfoByPoolType) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgSetPoolWeights) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgSetInfoByPoolType) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l { - size, err := m.PoolWeights.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.InfoByPoolType.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -1118,7 +1119,7 @@ func (m *MsgSetPoolWeights) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *MsgSetPoolWeightsResponse) Marshal() (dAtA []byte, err error) { +func (m *MsgSetInfoByPoolTypeResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1128,12 +1129,12 @@ func (m *MsgSetPoolWeightsResponse) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *MsgSetPoolWeightsResponse) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgSetInfoByPoolTypeResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgSetPoolWeightsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgSetInfoByPoolTypeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -1389,7 +1390,7 @@ func (m *MsgSetDeveloperAccountResponse) Size() (n int) { return n } -func (m *MsgSetPoolWeights) Size() (n int) { +func (m *MsgSetInfoByPoolType) Size() (n int) { if m == nil { return 0 } @@ -1399,12 +1400,12 @@ func (m *MsgSetPoolWeights) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } - l = m.PoolWeights.Size() + l = m.InfoByPoolType.Size() n += 1 + l + sovTx(uint64(l)) return n } -func (m *MsgSetPoolWeightsResponse) Size() (n int) { +func (m *MsgSetInfoByPoolTypeResponse) Size() (n int) { if m == nil { return 0 } @@ -1827,7 +1828,7 @@ func (m *MsgSetDeveloperAccountResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgSetPoolWeights) Unmarshal(dAtA []byte) error { +func (m *MsgSetInfoByPoolType) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1850,10 +1851,10 @@ func (m *MsgSetPoolWeights) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgSetPoolWeights: wiretype end group for non-group") + return fmt.Errorf("proto: MsgSetInfoByPoolType: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgSetPoolWeights: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgSetInfoByPoolType: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -1890,7 +1891,7 @@ func (m *MsgSetPoolWeights) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PoolWeights", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field InfoByPoolType", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1917,7 +1918,7 @@ func (m *MsgSetPoolWeights) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.PoolWeights.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.InfoByPoolType.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1942,7 +1943,7 @@ func (m *MsgSetPoolWeights) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgSetPoolWeightsResponse) Unmarshal(dAtA []byte) error { +func (m *MsgSetInfoByPoolTypeResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1965,10 +1966,10 @@ func (m *MsgSetPoolWeightsResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgSetPoolWeightsResponse: wiretype end group for non-group") + return fmt.Errorf("proto: MsgSetInfoByPoolTypeResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgSetPoolWeightsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgSetInfoByPoolTypeResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: diff --git a/x/protorev/types/tx.pb.gw.go b/x/protorev/types/tx.pb.gw.go index dfa28a0f86a..c7bd774100e 100644 --- a/x/protorev/types/tx.pb.gw.go +++ b/x/protorev/types/tx.pb.gw.go @@ -178,37 +178,37 @@ func local_request_Msg_SetMaxPoolPointsPerBlock_0(ctx context.Context, marshaler } var ( - filter_Msg_SetPoolWeights_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + filter_Msg_SetInfoByPoolType_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) -func request_Msg_SetPoolWeights_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq MsgSetPoolWeights +func request_Msg_SetInfoByPoolType_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq MsgSetInfoByPoolType var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_SetPoolWeights_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_SetInfoByPoolType_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.SetPoolWeights(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.SetInfoByPoolType(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Msg_SetPoolWeights_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq MsgSetPoolWeights +func local_request_Msg_SetInfoByPoolType_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq MsgSetInfoByPoolType var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_SetPoolWeights_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_SetInfoByPoolType_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.SetPoolWeights(ctx, &protoReq) + msg, err := server.SetInfoByPoolType(ctx, &protoReq) return msg, metadata, err } @@ -347,7 +347,7 @@ func RegisterMsgHandlerServer(ctx context.Context, mux *runtime.ServeMux, server }) - mux.Handle("POST", pattern_Msg_SetPoolWeights_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_Msg_SetInfoByPoolType_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream @@ -358,7 +358,7 @@ func RegisterMsgHandlerServer(ctx context.Context, mux *runtime.ServeMux, server runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Msg_SetPoolWeights_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Msg_SetInfoByPoolType_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { @@ -366,7 +366,7 @@ func RegisterMsgHandlerServer(ctx context.Context, mux *runtime.ServeMux, server return } - forward_Msg_SetPoolWeights_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Msg_SetInfoByPoolType_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -514,7 +514,7 @@ func RegisterMsgHandlerClient(ctx context.Context, mux *runtime.ServeMux, client }) - mux.Handle("POST", pattern_Msg_SetPoolWeights_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_Msg_SetInfoByPoolType_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -523,14 +523,14 @@ func RegisterMsgHandlerClient(ctx context.Context, mux *runtime.ServeMux, client runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Msg_SetPoolWeights_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Msg_SetInfoByPoolType_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Msg_SetPoolWeights_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Msg_SetInfoByPoolType_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -566,7 +566,7 @@ var ( pattern_Msg_SetMaxPoolPointsPerBlock_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"osmosis", "protorev", "set_max_pool_points_per_block"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Msg_SetPoolWeights_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"osmosis", "protorev", "set_pool_weights"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Msg_SetInfoByPoolType_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"osmosis", "protorev", "set_info_by_pool_type"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Msg_SetBaseDenoms_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"osmosis", "protorev", "set_base_denoms"}, "", runtime.AssumeColonVerbOpt(false))) ) @@ -580,7 +580,7 @@ var ( forward_Msg_SetMaxPoolPointsPerBlock_0 = runtime.ForwardResponseMessage - forward_Msg_SetPoolWeights_0 = runtime.ForwardResponseMessage + forward_Msg_SetInfoByPoolType_0 = runtime.ForwardResponseMessage forward_Msg_SetBaseDenoms_0 = runtime.ForwardResponseMessage ) diff --git a/x/protorev/types/validate.go b/x/protorev/types/validate.go index ce8df75707b..8bc2627b855 100644 --- a/x/protorev/types/validate.go +++ b/x/protorev/types/validate.go @@ -42,15 +42,90 @@ func ValidateBaseDenoms(denoms []BaseDenom) error { return nil } -// ---------------------- PoolWeights Validation ---------------------- // -// Validates that the pool weights object is ready for use in the module. -func (pw *PoolWeights) Validate() error { - if pw == nil { - return fmt.Errorf("pool weights cannot be nil") +// ---------------------- InfoByPoolType Validation ---------------------- // +// Validates the information about each pool type that is used throughout the module. +func (info *InfoByPoolType) Validate() error { + if info == nil { + return fmt.Errorf("pool type info cannot be nil") } - if pw.BalancerWeight == 0 || pw.StableWeight == 0 || pw.ConcentratedWeight == 0 || pw.CosmwasmWeight == 0 { - return fmt.Errorf("pool weights cannot be 0") + if err := info.Balancer.Validate(); err != nil { + return err + } + + if err := info.Stable.Validate(); err != nil { + return err + } + + if err := info.Concentrated.Validate(); err != nil { + return err + } + + if err := info.Cosmwasm.Validate(); err != nil { + return err + } + + return nil +} + +// Validates balancer pool information. +func (b *BalancerPoolInfo) Validate() error { + if b == nil { + return fmt.Errorf("balancer pool info cannot be nil") + } + + if b.Weight == 0 { + return fmt.Errorf("balancer pool weight cannot be 0") + } + + return nil +} + +// Validates stable pool information. +func (s *StablePoolInfo) Validate() error { + if s == nil { + return fmt.Errorf("stable pool info cannot be nil") + } + + if s.Weight == 0 { + return fmt.Errorf("stable pool weight cannot be 0") + } + + return nil +} + +// Validates concentrated pool information. +func (c *ConcentratedPoolInfo) Validate() error { + if c == nil { + return fmt.Errorf("concentrated pool info cannot be nil") + } + + if c.Weight == 0 { + return fmt.Errorf("concentrated pool weight cannot be 0") + } + + if c.MaxTicksCrossed == 0 || c.MaxTicksCrossed > MaxTicksCrossed { + return fmt.Errorf("max ticks moved cannot be 0 or greater than %d", MaxTicksCrossed) + } + + return nil +} + +// Validates cosmwasm pool information. +func (c *CosmwasmPoolInfo) Validate() error { + if c == nil { + return fmt.Errorf("cosmwasm pool info cannot be nil") + } + + for _, weightMap := range c.WeightMaps { + address, err := sdk.AccAddressFromBech32(weightMap.ContractAddress) + if err != nil { + return err + } + + if weightMap.Weight == 0 { + return fmt.Errorf("cosmwasm pool weight cannot be 0 for contract address %s", address) + } } return nil diff --git a/x/superfluid/client/cli/tx.go b/x/superfluid/client/cli/tx.go index 0de2f6b6e34..68902957820 100644 --- a/x/superfluid/client/cli/tx.go +++ b/x/superfluid/client/cli/tx.go @@ -34,6 +34,7 @@ func GetTxCmd() *cobra.Command { // NewSuperfluidRedelegateCmd(), NewCmdLockAndSuperfluidDelegate(), NewCmdUnPoolWhitelistedPool(), + NewUnbondConvertAndStake(), ) osmocli.AddTxCmd(cmd, NewCreateFullRangePositionAndSuperfluidDelegateCmd) osmocli.AddTxCmd(cmd, NewAddToConcentratedLiquiditySuperfluidPositionCmd) @@ -423,3 +424,56 @@ func NewUnlockAndMigrateSharesToFullRangeConcentratedPositionCmd() (*osmocli.TxC Example: "unlock-and-migrate-cl 10 25000000000gamm/pool/2 1000000000uosmo,10000000uion", }, &types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition{} } + +func NewUnbondConvertAndStake() *cobra.Command { + cmd := &cobra.Command{ + Use: "unbond-convert-and-stake [lock-id] [valAddr] [min-amount-to-stake](optional) [shares-to-convert](optional)", + Short: "instantly unbond any locked gamm shares convert them into osmo and stake", + Example: "unbond-convert-and-stake 10 osmo1xxx 100000uosmo", + Args: cobra.MinimumNArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()).WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever) + + sender := clientCtx.GetFromAddress() + lockId, err := strconv.Atoi(args[0]) + if err != nil { + return err + } + + valAddr := args[1] + + var minAmtToStake sdk.Int + // if user provided args for min amount to stake, use it. If not, use empty coin struct + var sharesToConvert sdk.Coin + if len(args) >= 3 { + convertedInt, ok := sdk.NewIntFromString(args[2]) + if !ok { + return fmt.Errorf("Conversion for sdk.Int failed") + } + minAmtToStake = convertedInt + if len(args) == 4 { + coins, err := sdk.ParseCoinNormalized(args[3]) + if err != nil { + return err + } + sharesToConvert = coins + } + } else { + minAmtToStake = sdk.ZeroInt() + sharesToConvert = sdk.Coin{} + } + + msg := types.NewMsgUnbondConvertAndStake(sender, uint64(lockId), valAddr, minAmtToStake, sharesToConvert) + + return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + return cmd +} diff --git a/x/superfluid/keeper/concentrated_liquidity.go b/x/superfluid/keeper/concentrated_liquidity.go index 55f3b752e5f..515de68fe6b 100644 --- a/x/superfluid/keeper/concentrated_liquidity.go +++ b/x/superfluid/keeper/concentrated_liquidity.go @@ -29,33 +29,33 @@ import ( // - The position is not superfluid staked. // - The position is the last position in the pool. // - The lock duration does not match the unbonding duration. -func (k Keeper) addToConcentratedLiquiditySuperfluidPosition(ctx sdk.Context, sender sdk.AccAddress, positionId uint64, amount0ToAdd, amount1ToAdd sdk.Int) (uint64, sdk.Int, sdk.Int, sdk.Dec, uint64, error) { +func (k Keeper) addToConcentratedLiquiditySuperfluidPosition(ctx sdk.Context, sender sdk.AccAddress, positionId uint64, amount0ToAdd, amount1ToAdd sdk.Int) (cltypes.CreateFullRangePositionData, uint64, error) { position, err := k.clk.GetPosition(ctx, positionId) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err + return cltypes.CreateFullRangePositionData{}, 0, err } if amount0ToAdd.IsNegative() || amount1ToAdd.IsNegative() { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, cltypes.NegativeAmountAddedError{PositionId: position.PositionId, Asset0Amount: amount0ToAdd, Asset1Amount: amount1ToAdd} + return cltypes.CreateFullRangePositionData{}, 0, cltypes.NegativeAmountAddedError{PositionId: position.PositionId, Asset0Amount: amount0ToAdd, Asset1Amount: amount1ToAdd} } // If the position is not superfluid staked, return error. positionHasActiveUnderlyingLock, lockId, err := k.clk.PositionHasActiveUnderlyingLock(ctx, positionId) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err + return cltypes.CreateFullRangePositionData{}, 0, err } if !positionHasActiveUnderlyingLock || lockId == 0 { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, types.PositionNotSuperfluidStakedError{PositionId: position.PositionId} + return cltypes.CreateFullRangePositionData{}, 0, types.PositionNotSuperfluidStakedError{PositionId: position.PositionId} } // Defense in depth making sure that the position is full-range. if position.LowerTick != cltypes.MinInitializedTick || position.UpperTick != cltypes.MaxTick { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, types.ConcentratedTickRangeNotFullError{ActualLowerTick: position.LowerTick, ActualUpperTick: position.UpperTick} + return cltypes.CreateFullRangePositionData{}, 0, types.ConcentratedTickRangeNotFullError{ActualLowerTick: position.LowerTick, ActualUpperTick: position.UpperTick} } lock, err := k.lk.GetLockByID(ctx, lockId) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err + return cltypes.CreateFullRangePositionData{}, 0, err } // Defense in depth. Require the underlying lock: @@ -64,60 +64,60 @@ func (k Keeper) addToConcentratedLiquiditySuperfluidPosition(ctx sdk.Context, se // - duration is equal to unbonding time // - end time is zero (not unbonding) if lock.Owner != position.Address { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, types.LockOwnerMismatchError{LockId: lockId, LockOwner: lock.Owner, ProvidedOwner: position.Address} + return cltypes.CreateFullRangePositionData{}, 0, types.LockOwnerMismatchError{LockId: lockId, LockOwner: lock.Owner, ProvidedOwner: position.Address} } if lock.Owner != sender.String() { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, types.LockOwnerMismatchError{LockId: lockId, LockOwner: lock.Owner, ProvidedOwner: sender.String()} + return cltypes.CreateFullRangePositionData{}, 0, types.LockOwnerMismatchError{LockId: lockId, LockOwner: lock.Owner, ProvidedOwner: sender.String()} } unbondingDuration := k.sk.UnbondingTime(ctx) if lock.Duration != unbondingDuration || !lock.EndTime.IsZero() { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, types.LockImproperStateError{LockId: lockId, UnbondingDuration: unbondingDuration.String()} + return cltypes.CreateFullRangePositionData{}, 0, types.LockImproperStateError{LockId: lockId, UnbondingDuration: unbondingDuration.String()} } // Superfluid undelegate the superfluid delegated position. // This deletes the connection between the lock and the intermediate account, deletes the synthetic lock, and burns the synthetic osmo. intermediateAccount, err := k.SuperfluidUndelegateToConcentratedPosition(ctx, sender.String(), lockId) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err + return cltypes.CreateFullRangePositionData{}, 0, err } // Finish unlocking directly for the lock. // This also breaks and deletes associated synthetic locks. err = k.lk.ForceUnlock(ctx, *lock) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err + return cltypes.CreateFullRangePositionData{}, 0, err } // Withdraw full liquidity from the position. amount0Withdrawn, amount1Withdrawn, err := k.clk.WithdrawPosition(ctx, sender, positionId, position.Liquidity) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err + return cltypes.CreateFullRangePositionData{}, 0, err } // If this is the last position in the pool, error. anyPositionsRemainingInPool, err := k.clk.HasAnyPositionForPool(ctx, position.PoolId) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err + return cltypes.CreateFullRangePositionData{}, 0, err } if !anyPositionsRemainingInPool { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, cltypes.AddToLastPositionInPoolError{PoolId: position.PoolId, PositionId: position.PositionId} + return cltypes.CreateFullRangePositionData{}, 0, cltypes.AddToLastPositionInPoolError{PoolId: position.PoolId, PositionId: position.PositionId} } // Create a coins object that includes the old position coins and the new position coins. concentratedPool, err := k.clk.GetConcentratedPoolById(ctx, position.PoolId) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err + return cltypes.CreateFullRangePositionData{}, 0, err } newPositionCoins := sdk.NewCoins(sdk.NewCoin(concentratedPool.GetToken0(), amount0Withdrawn.Add(amount0ToAdd)), sdk.NewCoin(concentratedPool.GetToken1(), amount1Withdrawn.Add(amount1ToAdd))) // Create a full range (min to max tick) concentrated liquidity position, lock it, and superfluid delegate it. - newPositionId, actualNewAmount0, actualNewAmount1, newLiquidity, newLockId, err := k.clk.CreateFullRangePositionLocked(ctx, position.PoolId, sender, newPositionCoins, unbondingDuration) + positionData, newLockId, err := k.clk.CreateFullRangePositionLocked(ctx, position.PoolId, sender, newPositionCoins, unbondingDuration) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err + return cltypes.CreateFullRangePositionData{}, 0, err } err = k.SuperfluidDelegate(ctx, sender.String(), newLockId, intermediateAccount.ValAddr) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err + return cltypes.CreateFullRangePositionData{}, 0, err } // Emit events. @@ -130,13 +130,13 @@ func (k Keeper) addToConcentratedLiquiditySuperfluidPosition(ctx sdk.Context, se types.TypeEvtAddToConcentratedLiquiditySuperfluidPosition, sdk.NewAttribute(sdk.AttributeKeySender, sender.String()), sdk.NewAttribute(types.AttributePositionId, strconv.FormatUint(positionId, 10)), - sdk.NewAttribute(types.AttributeNewPositionId, strconv.FormatUint(newPositionId, 10)), - sdk.NewAttribute(types.AttributeAmount0, actualNewAmount0.String()), - sdk.NewAttribute(types.AttributeAmount1, actualNewAmount1.String()), + sdk.NewAttribute(types.AttributeNewPositionId, strconv.FormatUint(positionData.ID, 10)), + sdk.NewAttribute(types.AttributeAmount0, positionData.Amount0.String()), + sdk.NewAttribute(types.AttributeAmount1, positionData.Amount1.String()), sdk.NewAttribute(types.AttributeConcentratedLockId, strconv.FormatUint(newLockId, 10)), - sdk.NewAttribute(types.AttributeLiquidity, newLiquidity.String()), + sdk.NewAttribute(types.AttributeLiquidity, positionData.Liquidity.String()), ), }) - return newPositionId, actualNewAmount0, actualNewAmount1, newLiquidity, newLockId, nil + return positionData, newLockId, nil } diff --git a/x/superfluid/keeper/concentrated_liquidity_test.go b/x/superfluid/keeper/concentrated_liquidity_test.go index 5463e59c3a5..2218568d7af 100644 --- a/x/superfluid/keeper/concentrated_liquidity_test.go +++ b/x/superfluid/keeper/concentrated_liquidity_test.go @@ -135,7 +135,7 @@ func (s *KeeperTestSuite) TestAddToConcentratedLiquiditySuperfluidPosition() { if !tc.isLastPositionInPool { fundCoins := sdk.NewCoins(sdk.NewCoin(clPool.GetToken0(), sdk.NewInt(100000000)), sdk.NewCoin(clPool.GetToken1(), sdk.NewInt(100000000))) s.FundAcc(nonOwner, fundCoins) - _, _, _, _, err := concentratedLiquidityKeeper.CreateFullRangePosition(ctx, clPool.GetId(), nonOwner, fundCoins) + _, err := concentratedLiquidityKeeper.CreateFullRangePosition(ctx, clPool.GetId(), nonOwner, fundCoins) s.Require().NoError(err) } @@ -147,7 +147,7 @@ func (s *KeeperTestSuite) TestAddToConcentratedLiquiditySuperfluidPosition() { preAddToPositionPoolFunds := bankKeeper.GetAllBalances(ctx, clPoolAddress) // System under test. - newPositionId, finalAmount0, finalAmount1, newLiquidity, newLockId, err := superfluidKeeper.AddToConcentratedLiquiditySuperfluidPosition(ctx, executionAcc, positionId, tc.amount0Added, tc.amount1Added) + positionData, newLockId, err := superfluidKeeper.AddToConcentratedLiquiditySuperfluidPosition(ctx, executionAcc, positionId, tc.amount0Added, tc.amount1Added) if tc.expectedError != nil { s.Require().Error(err) s.Require().ErrorContains(err, tc.expectedError.Error()) @@ -184,11 +184,11 @@ func (s *KeeperTestSuite) TestAddToConcentratedLiquiditySuperfluidPosition() { expectedNewCoins := sdk.NewCoins(sdk.NewCoin(clPool.GetToken0(), amount0.Add(tc.amount0Added)), sdk.NewCoin(clPool.GetToken1(), amount1.Add(tc.amount1Added))) clPoolDenom := cltypes.GetConcentratedLockupDenomFromPoolId(clPool.GetId()) - expectedLockCoins := sdk.NewCoins(sdk.NewCoin(clPoolDenom, newLiquidity.TruncateInt())) + expectedLockCoins := sdk.NewCoins(sdk.NewCoin(clPoolDenom, positionData.Liquidity.TruncateInt())) // Resulting position should have the expected amount of coins within one unit (rounding down). - s.Require().Equal(0, errTolerance.Compare(expectedNewCoins[0].Amount, finalAmount0), fmt.Sprintf("expected (%s), actual (%s)", expectedNewCoins[0].Amount, finalAmount0)) - s.Require().Equal(0, errTolerance.Compare(expectedNewCoins[1].Amount, finalAmount1), fmt.Sprintf("expected (%s), actual (%s)", expectedNewCoins[1].Amount, finalAmount1)) + s.Require().Equal(0, errTolerance.Compare(expectedNewCoins[0].Amount, positionData.Amount0), fmt.Sprintf("expected (%s), actual (%s)", expectedNewCoins[0].Amount, positionData.Amount0)) + s.Require().Equal(0, errTolerance.Compare(expectedNewCoins[1].Amount, positionData.Amount1), fmt.Sprintf("expected (%s), actual (%s)", expectedNewCoins[1].Amount, positionData.Amount1)) // Check the new lock. newLock, err := s.App.LockupKeeper.GetLockByID(ctx, newLockId) @@ -199,7 +199,7 @@ func (s *KeeperTestSuite) TestAddToConcentratedLiquiditySuperfluidPosition() { s.Require().Equal(expectedLockCoins.String(), newLock.Coins.String()) // Check that a new position and lock ID were generated. - s.Require().NotEqual(positionId, newPositionId) + s.Require().NotEqual(positionId, positionData.ID) s.Require().NotEqual(lockId, newLockId) // Check if intermediary account connection for the old lock ID is deleted. @@ -224,7 +224,7 @@ func (s *KeeperTestSuite) TestAddToConcentratedLiquiditySuperfluidPosition() { s.Require().False(found) // Check if the new intermediary account has expected delegation amount. - expectedDelegationAmt := superfluidKeeper.GetRiskAdjustedOsmoValue(ctx, finalAmount0) + expectedDelegationAmt := superfluidKeeper.GetRiskAdjustedOsmoValue(ctx, positionData.Amount0) delegationAmt, found := stakingKeeper.GetDelegation(ctx, newIntermediaryAcc, valAddr) s.Require().True(found) s.Require().Equal(expectedDelegationAmt, delegationAmt.Shares.TruncateInt()) @@ -263,8 +263,11 @@ func (s *KeeperTestSuite) SetupSuperfluidConcentratedPosition(ctx sdk.Context, s unbondingDuration := stakingKeeper.GetParams(ctx).UnbondingTime // Create a full range position in the concentrated liquidity pool. - positionId, amount0, amount1, _, lockId, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPoolId, poolJoinAcc, fullRangeCoins, unbondingDuration) + positionData, lockId, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPoolId, poolJoinAcc, fullRangeCoins, unbondingDuration) s.Require().NoError(err) + positionId = positionData.ID + amount0 = positionData.Amount0 + amount1 = positionData.Amount1 // Register the CL full range LP tokens as a superfluid asset. clPoolDenom := cltypes.GetConcentratedLockupDenomFromPoolId(clPoolId) diff --git a/x/superfluid/keeper/export_test.go b/x/superfluid/keeper/export_test.go index 662059c0feb..1be8b4dde18 100644 --- a/x/superfluid/keeper/export_test.go +++ b/x/superfluid/keeper/export_test.go @@ -5,7 +5,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" + cltypes "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" lockuptypes "github.com/osmosis-labs/osmosis/v17/x/lockup/types" + types "github.com/osmosis-labs/osmosis/v17/x/superfluid/types" ) var ( @@ -21,31 +23,31 @@ func (k Keeper) PrepareConcentratedLockForSlash(ctx sdk.Context, lock *lockuptyp return k.prepareConcentratedLockForSlash(ctx, lock, slashAmt) } -func (k Keeper) MigrateSuperfluidBondedBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin, synthDenomBeforeMigration string, tokenOutMins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, concentratedLockId, poolIdLeaving, poolIdEntering uint64, err error) { +func (k Keeper) MigrateSuperfluidBondedBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin, synthDenomBeforeMigration string, tokenOutMins sdk.Coins) (cltypes.CreateFullRangePositionData, uint64, types.MigrationPoolIDs, error) { return k.migrateSuperfluidBondedBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, synthDenomBeforeMigration, tokenOutMins) } -func (k Keeper) MigrateSuperfluidUnbondingBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin, synthDenomBeforeMigration string, tokenOutMins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, concentratedLockId, poolIdLeaving, poolIdEntering uint64, err error) { +func (k Keeper) MigrateSuperfluidUnbondingBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin, synthDenomBeforeMigration string, tokenOutMins sdk.Coins) (cltypes.CreateFullRangePositionData, uint64, types.MigrationPoolIDs, error) { return k.migrateSuperfluidUnbondingBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, synthDenomBeforeMigration, tokenOutMins) } -func (k Keeper) MigrateNonSuperfluidLockBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, concentratedLockId, poolIdLeaving, poolIdEntering uint64, err error) { +func (k Keeper) MigrateNonSuperfluidLockBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (cltypes.CreateFullRangePositionData, uint64, types.MigrationPoolIDs, error) { return k.migrateNonSuperfluidLockBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, tokenOutMins) } -func (k Keeper) ValidateSharesToMigrateUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (exitCoins sdk.Coins, err error) { - return k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, sharesToMigrate, tokenOutMins) +func (k Keeper) ForceUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins, exitCoinsLengthIsTwo bool) (exitCoins sdk.Coins, err error) { + return k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, sharesToMigrate, tokenOutMins, exitCoinsLengthIsTwo) } -func (k Keeper) RouteMigration(ctx sdk.Context, sender sdk.AccAddress, lockId int64, sharesToMigrate sdk.Coin) (synthLockBeforeMigration lockuptypes.SyntheticLock, migrationType MigrationType, err error) { - return k.routeMigration(ctx, sender, lockId, sharesToMigrate) +func (k Keeper) GetMigrationType(ctx sdk.Context, lockId int64) (synthLockBeforeMigration lockuptypes.SyntheticLock, migrationType MigrationType, err error) { + return k.getMigrationType(ctx, lockId) } -func (k Keeper) ValidateMigration(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin) (poolIdLeaving, poolIdEntering uint64, preMigrationLock *lockuptypes.PeriodLock, remainingLockTime time.Duration, err error) { +func (k Keeper) ValidateMigration(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin) (types.MigrationPoolIDs, *lockuptypes.PeriodLock, time.Duration, error) { return k.validateMigration(ctx, sender, lockId, sharesToMigrate) } -func (k Keeper) AddToConcentratedLiquiditySuperfluidPosition(ctx sdk.Context, owner sdk.AccAddress, positionId uint64, amount0Added, amount1Added sdk.Int) (uint64, sdk.Int, sdk.Int, sdk.Dec, uint64, error) { +func (k Keeper) AddToConcentratedLiquiditySuperfluidPosition(ctx sdk.Context, owner sdk.AccAddress, positionId uint64, amount0Added, amount1Added sdk.Int) (cltypes.CreateFullRangePositionData, uint64, error) { return k.addToConcentratedLiquiditySuperfluidPosition(ctx, owner, positionId, amount0Added, amount1Added) } @@ -60,3 +62,25 @@ func (k Keeper) GetExistingLockRemainingDuration(ctx sdk.Context, lock *lockupty func (k Keeper) DistributeSuperfluidGauges(ctx sdk.Context) { k.distributeSuperfluidGauges(ctx) } + +func (k Keeper) ConvertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, lockId uint64, + minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, err error) { + return k.convertLockToStake(ctx, sender, valAddr, lockId, minAmtToStake) +} + +func (k Keeper) ConvertGammSharesToOsmoAndStake( + ctx sdk.Context, + sender sdk.AccAddress, valAddr string, + poolIdLeaving uint64, exitCoins sdk.Coins, minAmtToStake sdk.Int, originalSuperfluidValAddr string, +) (totalAmtCoverted sdk.Int, err error) { + return k.convertGammSharesToOsmoAndStake(ctx, sender, valAddr, poolIdLeaving, exitCoins, minAmtToStake, originalSuperfluidValAddr) +} + +func (k Keeper) ConvertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, sharesToStake sdk.Coin, + minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, err error) { + return k.convertUnlockedToStake(ctx, sender, valAddr, sharesToStake, minAmtToStake) +} + +func (k Keeper) DelegateBaseOnValsetPref(ctx sdk.Context, sender sdk.AccAddress, valAddr, originalSuperfluidValAddr string, totalAmtToStake sdk.Int) error { + return k.delegateBaseOnValsetPref(ctx, sender, valAddr, originalSuperfluidValAddr, totalAmtToStake) +} diff --git a/x/superfluid/keeper/grpc_query_test.go b/x/superfluid/keeper/grpc_query_test.go index 68a3fecf658..d322190513b 100644 --- a/x/superfluid/keeper/grpc_query_test.go +++ b/x/superfluid/keeper/grpc_query_test.go @@ -313,7 +313,7 @@ func (s *KeeperTestSuite) TestUserConcentratedSuperfluidPositionsBondedAndUnbond expectedBondedLockIds := []uint64{} expectedBondedTotalSharesLocked := sdk.Coins{} for i := 0; i < 4; i++ { - posId, _, _, _, lockId, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPoolId, s.TestAccs[0], coins, duration) + positionData, lockId, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPoolId, s.TestAccs[0], coins, duration) s.Require().NoError(err) lock, err := s.App.LockupKeeper.GetLockByID(s.Ctx, lockId) @@ -322,13 +322,13 @@ func (s *KeeperTestSuite) TestUserConcentratedSuperfluidPositionsBondedAndUnbond err = s.App.SuperfluidKeeper.SuperfluidDelegate(s.Ctx, lock.Owner, lock.ID, valAddrs[0].String()) s.Require().NoError(err) - expectedBondedPositionIds = append(expectedBondedPositionIds, posId) + expectedBondedPositionIds = append(expectedBondedPositionIds, positionData.ID) expectedBondedLockIds = append(expectedBondedLockIds, lockId) expectedBondedTotalSharesLocked = expectedBondedTotalSharesLocked.Add(lock.Coins[0]) } // Create 1 position in pool 1 that is not superfluid delegated. - _, _, _, _, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, clPoolId, s.TestAccs[0], coins) + _, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, clPoolId, s.TestAccs[0], coins) s.Require().NoError(err) // Create 4 positions in pool 2 that are superfluid undelegating. @@ -336,7 +336,7 @@ func (s *KeeperTestSuite) TestUserConcentratedSuperfluidPositionsBondedAndUnbond expectedUnbondingLockIds := []uint64{} expectedUnbondingTotalSharesLocked := sdk.Coins{} for i := 0; i < 4; i++ { - posId, _, _, _, lockId, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPoolId2, s.TestAccs[0], coins, duration) + positionData, lockId, err := s.App.ConcentratedLiquidityKeeper.CreateFullRangePositionLocked(s.Ctx, clPoolId2, s.TestAccs[0], coins, duration) s.Require().NoError(err) lock, err := s.App.LockupKeeper.GetLockByID(s.Ctx, lockId) @@ -348,13 +348,13 @@ func (s *KeeperTestSuite) TestUserConcentratedSuperfluidPositionsBondedAndUnbond _, err = s.App.SuperfluidKeeper.SuperfluidUndelegateAndUnbondLock(s.Ctx, lockId, lock.Owner, lock.Coins[0].Amount) s.Require().NoError(err) - expectedUnbondingPositionIds = append(expectedUnbondingPositionIds, posId) + expectedUnbondingPositionIds = append(expectedUnbondingPositionIds, positionData.ID) expectedUnbondingLockIds = append(expectedUnbondingLockIds, lockId) expectedUnbondingTotalSharesLocked = expectedUnbondingTotalSharesLocked.Add(lock.Coins[0]) } // Create 1 position in pool 2 that is not superfluid delegated. - _, _, _, _, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, clPoolId2, s.TestAccs[0], coins) + _, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, clPoolId2, s.TestAccs[0], coins) s.Require().NoError(err) // Query the bonded positions. diff --git a/x/superfluid/keeper/keeper.go b/x/superfluid/keeper/keeper.go index db6c7bc264d..3670570caa6 100644 --- a/x/superfluid/keeper/keeper.go +++ b/x/superfluid/keeper/keeper.go @@ -18,15 +18,17 @@ type Keeper struct { storeKey sdk.StoreKey paramSpace paramtypes.Subspace - ak authkeeper.AccountKeeper - bk types.BankKeeper - sk types.StakingKeeper - ck types.CommunityPoolKeeper - ek types.EpochKeeper - lk types.LockupKeeper - gk types.GammKeeper - ik types.IncentivesKeeper - clk types.ConcentratedKeeper + ak authkeeper.AccountKeeper + bk types.BankKeeper + sk types.StakingKeeper + ck types.CommunityPoolKeeper + ek types.EpochKeeper + lk types.LockupKeeper + gk types.GammKeeper + ik types.IncentivesKeeper + clk types.ConcentratedKeeper + pmk types.PoolManagerKeeper + vspk types.ValSetPreferenceKeeper lms types.LockupMsgServer } @@ -34,7 +36,7 @@ type Keeper struct { var _ govtypes.StakingKeeper = (*Keeper)(nil) // NewKeeper returns an instance of Keeper. -func NewKeeper(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, ak authkeeper.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, dk types.CommunityPoolKeeper, ek types.EpochKeeper, lk types.LockupKeeper, gk types.GammKeeper, ik types.IncentivesKeeper, lms types.LockupMsgServer, clk types.ConcentratedKeeper) *Keeper { +func NewKeeper(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, ak authkeeper.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, dk types.CommunityPoolKeeper, ek types.EpochKeeper, lk types.LockupKeeper, gk types.GammKeeper, ik types.IncentivesKeeper, lms types.LockupMsgServer, clk types.ConcentratedKeeper, pmk types.PoolManagerKeeper, vspk types.ValSetPreferenceKeeper) *Keeper { // set KeyTable if it has not already been set if !paramSpace.HasKeyTable() { paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) @@ -52,6 +54,8 @@ func NewKeeper(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, ak authkee gk: gk, ik: ik, clk: clk, + pmk: pmk, + vspk: vspk, lms: lms, } diff --git a/x/superfluid/keeper/migrate.go b/x/superfluid/keeper/migrate.go index dedac1f490c..9e6dc12b7c1 100644 --- a/x/superfluid/keeper/migrate.go +++ b/x/superfluid/keeper/migrate.go @@ -46,10 +46,10 @@ const ( // - Create new CL lock and starts unlocking or unlocking where it left off. // // Errors if the lock is not found, if the lock is not a balancer pool lock, or if the lock is not owned by the sender. -func (k Keeper) RouteLockedBalancerToConcentratedMigration(ctx sdk.Context, sender sdk.AccAddress, providedLockId int64, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, poolIdLeaving, poolIdEntering, concentratedLockId uint64, err error) { - synthLockBeforeMigration, migrationType, err := k.routeMigration(ctx, sender, providedLockId, sharesToMigrate) +func (k Keeper) RouteLockedBalancerToConcentratedMigration(ctx sdk.Context, sender sdk.AccAddress, providedLockId int64, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (positionData cltypes.CreateFullRangePositionData, migratedPoolIDs types.MigrationPoolIDs, concentratedLockId uint64, err error) { + synthLockBeforeMigration, migrationType, err := k.getMigrationType(ctx, providedLockId) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, types.MigrationPoolIDs{}, 0, err } // As a hack around to get frontend working, we decided to allow negative values for the provided lock ID to indicate that the user wants to migrate shares that are not locked. @@ -57,19 +57,19 @@ func (k Keeper) RouteLockedBalancerToConcentratedMigration(ctx sdk.Context, send switch migrationType { case SuperfluidBonded: - positionId, amount0, amount1, liquidity, concentratedLockId, poolIdLeaving, poolIdEntering, err = k.migrateSuperfluidBondedBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, synthLockBeforeMigration.SynthDenom, tokenOutMins) + positionData, concentratedLockId, migratedPoolIDs, err = k.migrateSuperfluidBondedBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, synthLockBeforeMigration.SynthDenom, tokenOutMins) case SuperfluidUnbonding: - positionId, amount0, amount1, liquidity, concentratedLockId, poolIdLeaving, poolIdEntering, err = k.migrateSuperfluidUnbondingBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, synthLockBeforeMigration.SynthDenom, tokenOutMins) + positionData, concentratedLockId, migratedPoolIDs, err = k.migrateSuperfluidUnbondingBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, synthLockBeforeMigration.SynthDenom, tokenOutMins) case NonSuperfluid: - positionId, amount0, amount1, liquidity, concentratedLockId, poolIdLeaving, poolIdEntering, err = k.migrateNonSuperfluidLockBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, tokenOutMins) + positionData, concentratedLockId, migratedPoolIDs, err = k.migrateNonSuperfluidLockBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, tokenOutMins) case Unlocked: - positionId, amount0, amount1, liquidity, poolIdLeaving, poolIdEntering, err = k.gk.MigrateUnlockedPositionFromBalancerToConcentrated(ctx, sender, sharesToMigrate, tokenOutMins) + positionData, migratedPoolIDs, err = k.gk.MigrateUnlockedPositionFromBalancerToConcentrated(ctx, sender, sharesToMigrate, tokenOutMins) concentratedLockId = 0 default: - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, fmt.Errorf("unsupported migration type") + return cltypes.CreateFullRangePositionData{}, types.MigrationPoolIDs{}, 0, fmt.Errorf("unsupported migration type") } - return positionId, amount0, amount1, liquidity, poolIdLeaving, poolIdEntering, concentratedLockId, err + return positionData, migratedPoolIDs, concentratedLockId, err } // migrateSuperfluidBondedBalancerToConcentrated migrates a user's superfluid bonded balancer position to a superfluid bonded concentrated liquidity position. @@ -82,46 +82,46 @@ func (k Keeper) migrateSuperfluidBondedBalancerToConcentrated(ctx sdk.Context, sharesToMigrate sdk.Coin, synthDenomBeforeMigration string, tokenOutMins sdk.Coins, -) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, concentratedLockId, poolIdLeaving, poolIdEntering uint64, err error) { - poolIdLeaving, poolIdEntering, preMigrationLock, remainingLockTime, err := k.validateMigration(ctx, sender, originalLockId, sharesToMigrate) +) (cltypes.CreateFullRangePositionData, uint64, types.MigrationPoolIDs, error) { + migratedPoolIDs, preMigrationLock, remainingLockTime, err := k.validateMigration(ctx, sender, originalLockId, sharesToMigrate) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, 0, types.MigrationPoolIDs{}, err } // Get the validator address from the synth denom and ensure it is a valid address. valAddr := strings.Split(synthDenomBeforeMigration, "/")[4] _, err = sdk.ValAddressFromBech32(valAddr) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, 0, types.MigrationPoolIDs{}, err } // Superfluid undelegate the portion of shares the user is migrating from the superfluid delegated position. // If all shares are being migrated, this deletes the connection between the gamm lock and the intermediate account, deletes the synthetic lock, and burns the synthetic osmo. intermediateAccount, err := k.SuperfluidUndelegateToConcentratedPosition(ctx, sender.String(), originalLockId) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, 0, types.MigrationPoolIDs{}, err } // Force unlock, validate the provided sharesToMigrate, and exit the balancer pool. // This will return the coins that will be used to create the concentrated liquidity position. // It also returns the lock object that contains the remaining shares that were not used in this migration. - exitCoins, err := k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) + exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, migratedPoolIDs.LeavingID, preMigrationLock, sharesToMigrate, tokenOutMins, true) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, 0, types.MigrationPoolIDs{}, err } // Create a full range (min to max tick) concentrated liquidity position, lock it, and superfluid delegate it. - positionId, amount0, amount1, liquidity, concentratedLockId, err = k.clk.CreateFullRangePositionLocked(ctx, poolIdEntering, sender, exitCoins, remainingLockTime) + positionData, concentratedLockId, err := k.clk.CreateFullRangePositionLocked(ctx, migratedPoolIDs.EnteringID, sender, exitCoins, remainingLockTime) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, 0, types.MigrationPoolIDs{}, err } err = k.SuperfluidDelegate(ctx, sender.String(), concentratedLockId, intermediateAccount.ValAddr) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, 0, types.MigrationPoolIDs{}, err } - return positionId, amount0, amount1, liquidity, concentratedLockId, poolIdLeaving, poolIdEntering, nil + return positionData, concentratedLockId, migratedPoolIDs, nil } // migrateSuperfluidUnbondingBalancerToConcentrated migrates a user's superfluid unbonding balancer position to a superfluid unbonding concentrated liquidity position. @@ -135,50 +135,50 @@ func (k Keeper) migrateSuperfluidUnbondingBalancerToConcentrated(ctx sdk.Context sharesToMigrate sdk.Coin, synthDenomBeforeMigration string, tokenOutMins sdk.Coins, -) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, concentratedLockId, poolIdLeaving, poolIdEntering uint64, err error) { - poolIdLeaving, poolIdEntering, preMigrationLock, remainingLockTime, err := k.validateMigration(ctx, sender, lockId, sharesToMigrate) +) (cltypes.CreateFullRangePositionData, uint64, types.MigrationPoolIDs, error) { + migratedPoolIDs, preMigrationLock, remainingLockTime, err := k.validateMigration(ctx, sender, lockId, sharesToMigrate) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, 0, types.MigrationPoolIDs{}, err } // Get the validator address from the synth denom and ensure it is a valid address. valAddr := strings.Split(synthDenomBeforeMigration, "/")[4] _, err = sdk.ValAddressFromBech32(valAddr) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, 0, types.MigrationPoolIDs{}, err } // Force unlock, validate the provided sharesToMigrate, and exit the balancer pool. // This will return the coins that will be used to create the concentrated liquidity position. // It also returns the lock object that contains the remaining shares that were not used in this migration. - exitCoins, err := k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) + exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, migratedPoolIDs.LeavingID, preMigrationLock, sharesToMigrate, tokenOutMins, true) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, 0, types.MigrationPoolIDs{}, err } // Create a full range (min to max tick) concentrated liquidity position. - positionId, amount0, amount1, liquidity, concentratedLockId, err = k.clk.CreateFullRangePositionUnlocking(ctx, poolIdEntering, sender, exitCoins, remainingLockTime) + positionData, concentratedLockId, err := k.clk.CreateFullRangePositionUnlocking(ctx, migratedPoolIDs.EnteringID, sender, exitCoins, remainingLockTime) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, 0, types.MigrationPoolIDs{}, err } // The previous gamm intermediary account is now invalid for the new lock, since the underlying denom has changed and intermediary accounts are // created by validator address, denom, and gauge id. // We must therefore create and set a new intermediary account based on the previous validator but with the new lock's denom. - concentratedLockupDenom := cltypes.GetConcentratedLockupDenomFromPoolId(poolIdEntering) + concentratedLockupDenom := cltypes.GetConcentratedLockupDenomFromPoolId(migratedPoolIDs.EnteringID) clIntermediateAccount, err := k.GetOrCreateIntermediaryAccount(ctx, concentratedLockupDenom, valAddr) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, 0, types.MigrationPoolIDs{}, err } // Synthetic lock is created to indicate unbonding position. The synthetic lock will be in unbonding period for remainingLockTime. // Create a new synthetic lockup for the new intermediary account in an unlocking status for the remaining duration. err = k.createSyntheticLockupWithDuration(ctx, concentratedLockId, clIntermediateAccount, remainingLockTime, unlockingStatus) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, 0, types.MigrationPoolIDs{}, err } - return positionId, amount0, amount1, liquidity, concentratedLockId, poolIdLeaving, poolIdEntering, nil + return positionData, concentratedLockId, migratedPoolIDs, nil } // migrateNonSuperfluidLockBalancerToConcentrated migrates a user's non-superfluid locked or unlocking balancer position to an unlocking concentrated liquidity position. @@ -190,34 +190,34 @@ func (k Keeper) migrateNonSuperfluidLockBalancerToConcentrated(ctx sdk.Context, lockId uint64, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins, -) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, concentratedLockId, poolIdLeaving, poolIdEntering uint64, err error) { - poolIdLeaving, poolIdEntering, preMigrationLock, remainingLockTime, err := k.validateMigration(ctx, sender, lockId, sharesToMigrate) +) (cltypes.CreateFullRangePositionData, uint64, types.MigrationPoolIDs, error) { + migratedPoolIDs, preMigrationLock, remainingLockTime, err := k.validateMigration(ctx, sender, lockId, sharesToMigrate) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, 0, types.MigrationPoolIDs{}, err } // Force unlock, validate the provided sharesToMigrate, and exit the balancer pool. // This will return the coins that will be used to create the concentrated liquidity position. // It also returns the lock object that contains the remaining shares that were not used in this migration. - exitCoins, err := k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) + exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, migratedPoolIDs.LeavingID, preMigrationLock, sharesToMigrate, tokenOutMins, true) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, 0, types.MigrationPoolIDs{}, err } // Create a new lock that is unlocking for the remaining time of the old lock. // Regardless of the previous lock's status, we create a new lock that is unlocking. // This is because locking without superfluid is pointless in the context of concentrated liquidity. - positionId, amount0, amount1, liquidity, concentratedLockId, err = k.clk.CreateFullRangePositionUnlocking(ctx, poolIdEntering, sender, exitCoins, remainingLockTime) + positionData, concentratedLockId, err := k.clk.CreateFullRangePositionUnlocking(ctx, migratedPoolIDs.EnteringID, sender, exitCoins, remainingLockTime) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err + return cltypes.CreateFullRangePositionData{}, 0, types.MigrationPoolIDs{}, err } - return positionId, amount0, amount1, liquidity, concentratedLockId, poolIdLeaving, poolIdEntering, nil + return positionData, concentratedLockId, migratedPoolIDs, nil } -// routeMigration determines the status of the provided lock which is used to determine the method for migration. +// getMigrationType determines the status of the provided lock which is used to determine the method for migration. // It also returns the underlying synthetic locks of the provided lock, if any exist. -func (k Keeper) routeMigration(ctx sdk.Context, sender sdk.AccAddress, providedLockId int64, sharesToMigrate sdk.Coin) (synthLockBeforeMigration lockuptypes.SyntheticLock, migrationType MigrationType, err error) { +func (k Keeper) getMigrationType(ctx sdk.Context, providedLockId int64) (synthLockBeforeMigration lockuptypes.SyntheticLock, migrationType MigrationType, err error) { // As a hack around to get frontend working, we decided to allow negative values for the provided lock ID to indicate that the user wants to migrate shares that are not locked. if providedLockId <= 0 { return lockuptypes.SyntheticLock{}, Unlocked, nil @@ -258,40 +258,43 @@ func (k Keeper) routeMigration(ctx sdk.Context, sender sdk.AccAddress, providedL // preMigrationLock: The original lock before migration. // remainingLockTime: The remaining time on the lock before it expires. // err: An error, if any occurred. -func (k Keeper) validateMigration(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin) (poolIdLeaving, poolIdEntering uint64, preMigrationLock *lockuptypes.PeriodLock, remainingLockTime time.Duration, err error) { +func (k Keeper) validateMigration(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin) (types.MigrationPoolIDs, *lockuptypes.PeriodLock, time.Duration, error) { // Defense in depth, ensuring the sharesToMigrate contains gamm pool share prefix. if !strings.HasPrefix(sharesToMigrate.Denom, gammtypes.GAMMTokenPrefix) { - return 0, 0, &lockuptypes.PeriodLock{}, 0, types.SharesToMigrateDenomPrefixError{Denom: sharesToMigrate.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} + return types.MigrationPoolIDs{}, &lockuptypes.PeriodLock{}, 0, types.SharesToMigrateDenomPrefixError{Denom: sharesToMigrate.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} } // Get the balancer poolId by parsing the gamm share denom. - poolIdLeaving, err = gammtypes.GetPoolIdFromShareDenom(sharesToMigrate.Denom) + poolIdLeaving, err := gammtypes.GetPoolIdFromShareDenom(sharesToMigrate.Denom) if err != nil { - return 0, 0, &lockuptypes.PeriodLock{}, 0, err + return types.MigrationPoolIDs{}, &lockuptypes.PeriodLock{}, 0, err } // Ensure a governance sanctioned link exists between the balancer pool and a concentrated pool. - poolIdEntering, err = k.gk.GetLinkedConcentratedPoolID(ctx, poolIdLeaving) + poolIdEntering, err := k.gk.GetLinkedConcentratedPoolID(ctx, poolIdLeaving) if err != nil { - return 0, 0, &lockuptypes.PeriodLock{}, 0, err + return types.MigrationPoolIDs{}, &lockuptypes.PeriodLock{}, 0, err } // Check that lockID corresponds to sender and that the denomination of LP shares corresponds to the poolId. - preMigrationLock, err = k.validateGammLockForSuperfluidStaking(ctx, sender, poolIdLeaving, lockId) + preMigrationLock, err := k.validateGammLockForSuperfluidStaking(ctx, sender, poolIdLeaving, lockId) if err != nil { - return 0, 0, &lockuptypes.PeriodLock{}, 0, err + return types.MigrationPoolIDs{}, &lockuptypes.PeriodLock{}, 0, err } // Before we break the lock, we must note the time remaining on the lock. - remainingLockTime, err = k.getExistingLockRemainingDuration(ctx, preMigrationLock) + remainingLockTime, err := k.getExistingLockRemainingDuration(ctx, preMigrationLock) if err != nil { - return 0, 0, &lockuptypes.PeriodLock{}, 0, err + return types.MigrationPoolIDs{}, &lockuptypes.PeriodLock{}, 0, err } - return poolIdLeaving, poolIdEntering, preMigrationLock, remainingLockTime, nil + return types.MigrationPoolIDs{ + EnteringID: poolIdEntering, + LeavingID: poolIdLeaving, + }, preMigrationLock, remainingLockTime, nil } -// validateSharesToMigrateUnlockAndExitBalancerPool validates the unlocking and exiting of gamm LP tokens from the Balancer pool. It performs the following steps: +// forceUnlockAndExitBalancerPool validates the unlocking and exiting of gamm LP tokens from the Balancer pool. It performs the following steps: // // 1. Completes the unlocking process / deletes synthetic locks for the provided lock. // 2. If shares to migrate are not specified, all shares in the lock are migrated. @@ -299,7 +302,7 @@ func (k Keeper) validateMigration(ctx sdk.Context, sender sdk.AccAddress, lockId // 4. Exits the position in the Balancer pool. // 5. Ensures that exactly two coins are returned. // 6. Any remaining shares that were not migrated are re-locked as a new lock for the remaining time on the lock. -func (k Keeper) validateSharesToMigrateUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (exitCoins sdk.Coins, err error) { +func (k Keeper) forceUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins, exitCoinsLengthIsTwo bool) (exitCoins sdk.Coins, err error) { // validateMigration ensures that the preMigrationLock contains coins of length 1. gammSharesInLock := lock.Coins[0] @@ -318,7 +321,7 @@ func (k Keeper) validateSharesToMigrateUnlockAndExitBalancerPool(ctx sdk.Context return sdk.Coins{}, types.MigratePartialSharesError{SharesToMigrate: sharesToMigrate.Amount.String(), SharesInLock: gammSharesInLock.Amount.String()} } - // Force migrate, which breaks and deletes associated synthetic locks. + // Force migrate, which breaks and deletes associated synthetic locks (if exists). err = k.lk.ForceUnlock(ctx, *lock) if err != nil { return sdk.Coins{}, err @@ -330,8 +333,8 @@ func (k Keeper) validateSharesToMigrateUnlockAndExitBalancerPool(ctx sdk.Context return sdk.Coins{}, err } - // Defense in depth, ensuring we are returning exactly two coins. - if len(exitCoins) != 2 { + // if exit coins length should be two, check exitCoins length + if exitCoinsLengthIsTwo && len(exitCoins) != 2 { return sdk.Coins{}, types.TwoTokenBalancerPoolError{NumberOfTokens: len(exitCoins)} } diff --git a/x/superfluid/keeper/migrate_test.go b/x/superfluid/keeper/migrate_test.go index a67e80002b0..86e54874714 100644 --- a/x/superfluid/keeper/migrate_test.go +++ b/x/superfluid/keeper/migrate_test.go @@ -161,7 +161,7 @@ func (s *KeeperTestSuite) TestRouteLockedBalancerToConcentratedMigration() { balancerDelegationPre, _ := stakingKeeper.GetDelegation(s.Ctx, balancerIntermediaryAcc.GetAccAddress(), valAddr) // Run the migration logic. - positionId, amount0, amount1, liquidityMigrated, poolIdLeaving, poolIdEntering, concentratedLockId, err := superfluidKeeper.RouteLockedBalancerToConcentratedMigration(s.Ctx, poolJoinAcc, int64(originalGammLockId), coinsToMigrate, tc.minExitCoins) + positionData, migratedPools, concentratedLockId, err := superfluidKeeper.RouteLockedBalancerToConcentratedMigration(s.Ctx, poolJoinAcc, int64(originalGammLockId), coinsToMigrate, tc.minExitCoins) if tc.expectedError != nil { s.Require().Error(err) s.Require().ErrorIs(err, tc.expectedError) @@ -171,12 +171,12 @@ func (s *KeeperTestSuite) TestRouteLockedBalancerToConcentratedMigration() { s.AssertEventEmitted(s.Ctx, gammtypes.TypeEvtPoolExited, 1) s.ValidateMigrateResult( - positionId, balancerPooId, poolIdLeaving, clPoolId, poolIdEntering, - tc.percentOfSharesToMigrate, liquidityMigrated, + positionData.ID, balancerPooId, migratedPools.LeavingID, clPoolId, migratedPools.EnteringID, + tc.percentOfSharesToMigrate, positionData.Liquidity, *balancerLock, joinPoolAmt, balancerPoolShareOut, coinsToMigrate, - amount0, amount1, + positionData.Amount0, positionData.Amount1, ) // If the lock was superfluid delegated: @@ -246,7 +246,7 @@ func (s *KeeperTestSuite) TestRouteLockedBalancerToConcentratedMigration() { // Run slashing logic if the test case involves locks and check if the new and old locks are slashed. if !tc.noLock { slashExpected := tc.superfluidDelegated || tc.superfluidUndelegating - s.SlashAndValidateResult(s.Ctx, originalGammLockId, concentratedLockId, poolIdEntering, tc.percentOfSharesToMigrate, valAddr, *balancerLock, slashExpected) + s.SlashAndValidateResult(s.Ctx, originalGammLockId, concentratedLockId, migratedPools.EnteringID, tc.percentOfSharesToMigrate, valAddr, *balancerLock, slashExpected) } }) } @@ -302,9 +302,9 @@ func (s *KeeperTestSuite) TestMigrateSuperfluidBondedBalancerToConcentrated() { coinsToMigrate := balancerPoolShareOut coinsToMigrate.Amount = coinsToMigrate.Amount.ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt() - // RouteMigration is called via the migration message router and is always run prior to the migration itself. + // GetMigrationType is called via the migration message router and is always run prior to the migration itself. // We use it here just to retrieve the synthetic lock before the migration. - synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, int64(originalGammLockId), coinsToMigrate) + synthLockBeforeMigration, migrationType, err := superfluidKeeper.GetMigrationType(s.Ctx, int64(originalGammLockId)) s.Require().NoError(err) s.Require().Equal(migrationType, keeper.SuperfluidBonded) @@ -324,7 +324,7 @@ func (s *KeeperTestSuite) TestMigrateSuperfluidBondedBalancerToConcentrated() { balancerDelegationPre, _ := stakingKeeper.GetDelegation(s.Ctx, balancerIntermediaryAcc.GetAccAddress(), valAddr) // System under test. - positionId, amount0, amount1, liquidityMigrated, concentratedLockId, poolIdLeaving, poolIdEntering, err := superfluidKeeper.MigrateSuperfluidBondedBalancerToConcentrated(s.Ctx, poolJoinAcc, originalGammLockId, coinsToMigrate, synthLockBeforeMigration.SynthDenom, tc.tokenOutMins) + positionData, concentratedLockId, migratedPools, err := superfluidKeeper.MigrateSuperfluidBondedBalancerToConcentrated(s.Ctx, poolJoinAcc, originalGammLockId, coinsToMigrate, synthLockBeforeMigration.SynthDenom, tc.tokenOutMins) if tc.expectedError != nil { s.Require().Error(err) s.Require().ErrorContains(err, tc.expectedError.Error()) @@ -334,12 +334,12 @@ func (s *KeeperTestSuite) TestMigrateSuperfluidBondedBalancerToConcentrated() { s.AssertEventEmitted(s.Ctx, gammtypes.TypeEvtPoolExited, 1) s.ValidateMigrateResult( - positionId, balancerPooId, poolIdLeaving, clPoolId, poolIdEntering, - tc.percentOfSharesToMigrate, liquidityMigrated, + positionData.ID, balancerPooId, migratedPools.LeavingID, clPoolId, migratedPools.EnteringID, + tc.percentOfSharesToMigrate, positionData.Liquidity, *balancerLock, joinPoolAmt, balancerPoolShareOut, coinsToMigrate, - amount0, amount1, + positionData.Amount0, positionData.Amount1, ) if tc.percentOfSharesToMigrate.Equal(sdk.OneDec()) { @@ -395,7 +395,7 @@ func (s *KeeperTestSuite) TestMigrateSuperfluidBondedBalancerToConcentrated() { concentratedLock, err := lockupKeeper.GetLockByID(s.Ctx, concentratedLockId) s.Require().NoError(err) - s.Require().Equal(liquidityMigrated.TruncateInt().String(), concentratedLock.Coins[0].Amount.String(), "expected %s shares, found %s shares", coinsToMigrate.Amount.String(), concentratedLock.Coins[0].Amount.String()) + s.Require().Equal(positionData.Liquidity.TruncateInt().String(), concentratedLock.Coins[0].Amount.String(), "expected %s shares, found %s shares", coinsToMigrate.Amount.String(), concentratedLock.Coins[0].Amount.String()) s.Require().Equal(balancerLock.Duration, concentratedLock.Duration) s.Require().Equal(balancerLock.EndTime, concentratedLock.EndTime) @@ -458,8 +458,8 @@ func (s *KeeperTestSuite) TestMigrateSuperfluidUnbondingBalancerToConcentrated() coinsToMigrate := balancerPoolShareOut coinsToMigrate.Amount = coinsToMigrate.Amount.ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt() - // RouteMigration is called via the migration message router and is always run prior to the migration itself - synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, int64(originalGammLockId), coinsToMigrate) + // GetMigrationType is called via the migration message router and is always run prior to the migration itself + synthLockBeforeMigration, migrationType, err := superfluidKeeper.GetMigrationType(s.Ctx, int64(originalGammLockId)) s.Require().NoError(err) s.Require().Equal(migrationType, keeper.SuperfluidUnbonding) @@ -472,7 +472,7 @@ func (s *KeeperTestSuite) TestMigrateSuperfluidUnbondingBalancerToConcentrated() } // System under test. - positionId, amount0, amount1, liquidityMigrated, concentratedLockId, poolIdLeaving, poolIdEntering, err := superfluidKeeper.MigrateSuperfluidUnbondingBalancerToConcentrated(s.Ctx, poolJoinAcc, originalGammLockId, coinsToMigrate, synthLockBeforeMigration.SynthDenom, tc.tokenOutMins) + positionData, concentratedLockId, migratedPools, err := superfluidKeeper.MigrateSuperfluidUnbondingBalancerToConcentrated(s.Ctx, poolJoinAcc, originalGammLockId, coinsToMigrate, synthLockBeforeMigration.SynthDenom, tc.tokenOutMins) if tc.expectedError != nil { s.Require().Error(err) s.Require().ErrorContains(err, tc.expectedError.Error()) @@ -482,12 +482,12 @@ func (s *KeeperTestSuite) TestMigrateSuperfluidUnbondingBalancerToConcentrated() s.AssertEventEmitted(s.Ctx, gammtypes.TypeEvtPoolExited, 1) s.ValidateMigrateResult( - positionId, balancerPooId, poolIdLeaving, clPoolId, poolIdEntering, - tc.percentOfSharesToMigrate, liquidityMigrated, + positionData.ID, balancerPooId, migratedPools.LeavingID, clPoolId, migratedPools.EnteringID, + tc.percentOfSharesToMigrate, positionData.Liquidity, *balancerLock, joinPoolAmt, balancerPoolShareOut, coinsToMigrate, - amount0, amount1, + positionData.Amount0, positionData.Amount1, ) if tc.percentOfSharesToMigrate.Equal(sdk.OneDec()) { @@ -509,7 +509,7 @@ func (s *KeeperTestSuite) TestMigrateSuperfluidUnbondingBalancerToConcentrated() // Check newly created concentrated lock. concentratedLock, err := lockupKeeper.GetLockByID(s.Ctx, concentratedLockId) s.Require().NoError(err) - s.Require().Equal(liquidityMigrated.TruncateInt().String(), concentratedLock.Coins[0].Amount.String(), "expected %s shares, found %s shares", coinsToMigrate.Amount.String(), concentratedLock.Coins[0].Amount.String()) + s.Require().Equal(positionData.Liquidity.TruncateInt().String(), concentratedLock.Coins[0].Amount.String(), "expected %s shares, found %s shares", coinsToMigrate.Amount.String(), concentratedLock.Coins[0].Amount.String()) // If the original lock was not unlocking, then the new lock should have the same duration and end time (regardless of the fact that an hour has passed since the lock began superfluid unbonding). expectedConcentratedLockDuration := balancerLock.Duration expectedConcentratedLockEndTime := s.Ctx.BlockTime().Add(balancerLock.Duration) @@ -573,14 +573,14 @@ func (s *KeeperTestSuite) TestMigrateNonSuperfluidLockBalancerToConcentrated() { coinsToMigrate := balancerPoolShareOut coinsToMigrate.Amount = coinsToMigrate.Amount.ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt() - // RouteMigration is called via the migration message router and is always run prior to the migration itself - synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, int64(originalGammLockId), coinsToMigrate) + // GetMigrationType is called via the migration message router and is always run prior to the migration itself + synthLockBeforeMigration, migrationType, err := superfluidKeeper.GetMigrationType(s.Ctx, int64(originalGammLockId)) s.Require().NoError(err) s.Require().Equal((lockuptypes.SyntheticLock{}), synthLockBeforeMigration) s.Require().Equal(migrationType, keeper.NonSuperfluid) // System under test. - positionId, amount0, amount1, liquidityMigrated, concentratedLockId, poolIdLeaving, poolIdEntering, err := superfluidKeeper.MigrateNonSuperfluidLockBalancerToConcentrated(s.Ctx, poolJoinAcc, originalGammLockId, coinsToMigrate, tc.tokenOutMins) + positionData, concentratedLockId, migratedPools, err := superfluidKeeper.MigrateNonSuperfluidLockBalancerToConcentrated(s.Ctx, poolJoinAcc, originalGammLockId, coinsToMigrate, tc.tokenOutMins) if tc.expectedError != nil { s.Require().Error(err) s.Require().ErrorContains(err, tc.expectedError.Error()) @@ -590,18 +590,18 @@ func (s *KeeperTestSuite) TestMigrateNonSuperfluidLockBalancerToConcentrated() { s.AssertEventEmitted(s.Ctx, gammtypes.TypeEvtPoolExited, 1) s.ValidateMigrateResult( - positionId, balancerPooId, poolIdLeaving, clPoolId, poolIdEntering, - tc.percentOfSharesToMigrate, liquidityMigrated, + positionData.ID, balancerPooId, migratedPools.LeavingID, clPoolId, migratedPools.EnteringID, + tc.percentOfSharesToMigrate, positionData.Liquidity, *balancerLock, joinPoolAmt, balancerPoolShareOut, coinsToMigrate, - amount0, amount1, + positionData.Amount0, positionData.Amount1, ) // Check newly created concentrated lock. concentratedLock, err := lockupKeeper.GetLockByID(s.Ctx, concentratedLockId) s.Require().NoError(err) - s.Require().Equal(liquidityMigrated.TruncateInt().String(), concentratedLock.Coins[0].Amount.String(), "expected %s shares, found %s shares", coinsToMigrate.Amount.String(), concentratedLock.Coins[0].Amount.String()) + s.Require().Equal(positionData.Liquidity.TruncateInt().String(), concentratedLock.Coins[0].Amount.String(), "expected %s shares, found %s shares", coinsToMigrate.Amount.String(), concentratedLock.Coins[0].Amount.String()) s.Require().Equal(balancerLock.Duration, concentratedLock.Duration) s.Require().Equal(s.Ctx.BlockTime().Add(balancerLock.Duration), concentratedLock.EndTime) @@ -655,14 +655,14 @@ func (s *KeeperTestSuite) TestMigrateUnlockedPositionFromBalancerToConcentrated( coinsToMigrate := balancerPoolShareOut coinsToMigrate.Amount = coinsToMigrate.Amount.ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt() - // RouteMigration is called via the migration message router and is always run prior to the migration itself - synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, 0, coinsToMigrate) + // GetMigrationType is called via the migration message router and is always run prior to the migration itself + synthLockBeforeMigration, migrationType, err := superfluidKeeper.GetMigrationType(s.Ctx, 0) s.Require().NoError(err) s.Require().Equal((lockuptypes.SyntheticLock{}), synthLockBeforeMigration) s.Require().Equal(migrationType, keeper.Unlocked) // System under test. - positionId, amount0, amount1, liquidityMigrated, poolIdLeaving, poolIdEntering, err := gammKeeper.MigrateUnlockedPositionFromBalancerToConcentrated(s.Ctx, poolJoinAcc, coinsToMigrate, tc.tokenOutMins) + positionData, migratedPools, err := gammKeeper.MigrateUnlockedPositionFromBalancerToConcentrated(s.Ctx, poolJoinAcc, coinsToMigrate, tc.tokenOutMins) if tc.expectedError != nil { s.Require().ErrorContains(err, tc.expectedError.Error()) return @@ -671,12 +671,12 @@ func (s *KeeperTestSuite) TestMigrateUnlockedPositionFromBalancerToConcentrated( s.AssertEventEmitted(s.Ctx, gammtypes.TypeEvtPoolExited, 1) s.ValidateMigrateResult( - positionId, balancerPooId, poolIdLeaving, clPoolId, poolIdEntering, - tc.percentOfSharesToMigrate, liquidityMigrated, + positionData.ID, balancerPooId, migratedPools.LeavingID, clPoolId, migratedPools.EnteringID, + tc.percentOfSharesToMigrate, positionData.Liquidity, *balancerLock, joinPoolAmt, balancerPoolShareOut, coinsToMigrate, - amount0, amount1, + positionData.Amount0, positionData.Amount1, ) }) } @@ -777,7 +777,7 @@ func (s *KeeperTestSuite) TestValidateMigration() { } // System under test. - poolIdLeaving, poolIdEntering, preMigrationLock, remainingLockTime, err := superfluidKeeper.ValidateMigration(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate) + migratedPools, preMigrationLock, remainingLockTime, err := superfluidKeeper.ValidateMigration(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate) if tc.expectedError != nil { s.Require().Error(err) s.Require().ErrorContains(err, tc.expectedError.Error()) @@ -785,8 +785,8 @@ func (s *KeeperTestSuite) TestValidateMigration() { } s.Require().NoError(err) - s.Require().Equal(poolIdLeaving, balancerPooId) - s.Require().Equal(poolIdEntering, clPoolId) + s.Require().Equal(migratedPools.LeavingID, balancerPooId) + s.Require().Equal(migratedPools.EnteringID, clPoolId) s.Require().Equal(preMigrationLock.GetID(), originalGammLockId) s.Require().Equal(preMigrationLock.GetCoins(), sdk.NewCoins(balancerPoolShareOut)) s.Require().Equal(preMigrationLock.GetDuration(), remainingLockTime) @@ -794,13 +794,14 @@ func (s *KeeperTestSuite) TestValidateMigration() { } } -func (s *KeeperTestSuite) TestValidateSharesToMigrateUnlockAndExitBalancerPool() { +func (s *KeeperTestSuite) TestForceUnlockAndExitBalancerPool() { defaultJoinTime := s.Ctx.BlockTime() type sendTest struct { overwritePreMigrationLock bool overwriteShares bool overwritePool bool overwritePoolId bool + exitCoinsLengthIsTwo bool percentOfSharesToMigrate sdk.Dec tokenOutMins sdk.Coins expectedError error @@ -813,6 +814,11 @@ func (s *KeeperTestSuite) TestValidateSharesToMigrateUnlockAndExitBalancerPool() percentOfSharesToMigrate: sdk.MustNewDecFromStr("0.4"), expectedError: types.MigratePartialSharesError{SharesToMigrate: "20000000000000000000", SharesInLock: "50000000000000000000"}, }, + "attempt to leave a pool that has more than two denoms": { + percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), + overwritePool: true, + exitCoinsLengthIsTwo: false, + }, "error: lock does not exist": { percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), overwritePreMigrationLock: true, @@ -828,9 +834,10 @@ func (s *KeeperTestSuite) TestValidateSharesToMigrateUnlockAndExitBalancerPool() overwritePoolId: true, expectedError: fmt.Errorf("pool with ID %d does not exist", 2), }, - "error: attempt to leave a pool that has more than two denoms": { + "error: attempt to leave a pool that has more than two denoms with exitCoinsLengthIsTwo true": { percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), overwritePool: true, + exitCoinsLengthIsTwo: true, expectedError: types.TwoTokenBalancerPoolError{NumberOfTokens: 4}, }, "error: happy path (full shares), token out mins is more than exit coins": { @@ -914,7 +921,7 @@ func (s *KeeperTestSuite) TestValidateSharesToMigrateUnlockAndExitBalancerPool() } // System under test - exitCoins, err := superfluidKeeper.ValidateSharesToMigrateUnlockAndExitBalancerPool(ctx, poolJoinAcc, balancerPooId, lock, coinsToMigrate, tc.tokenOutMins) + exitCoins, err := superfluidKeeper.ForceUnlockAndExitBalancerPool(ctx, poolJoinAcc, balancerPooId, lock, coinsToMigrate, tc.tokenOutMins, tc.exitCoinsLengthIsTwo) if tc.expectedError != nil { s.Require().Error(err) s.Require().ErrorContains(err, tc.expectedError.Error()) @@ -939,9 +946,11 @@ func (s *KeeperTestSuite) TestValidateSharesToMigrateUnlockAndExitBalancerPool() s.Require().Equal(expectedSharesStillInOldLock.String(), lock.Coins[0].Amount.String()) } - for _, coin := range exitCoins { - // Check that the exit coin is the same amount that we joined with (with one unit rounding down) - s.Require().Equal(0, defaultErrorTolerance.Compare(tokensIn.AmountOf(coin.Denom).ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt(), coin.Amount)) + if tc.exitCoinsLengthIsTwo { + for _, coin := range exitCoins { + // Check that the exit coin is the same amount that we joined with (with one unit rounding down) + s.Require().Equal(0, defaultErrorTolerance.Compare(tokensIn.AmountOf(coin.Denom).ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt(), coin.Amount)) + } } }) } @@ -1340,11 +1349,11 @@ func (s *KeeperTestSuite) TestFunctional_VaryingPositions_Migrations() { preClaimBalancerPoolBalance := balancerPool.GetTotalPoolLiquidity(s.Ctx) // Run the migration. - _, amount0, amount1, _, _, _, _, err := s.App.SuperfluidKeeper.RouteLockedBalancerToConcentratedMigration(s.Ctx, s.TestAccs[i+1], int64(posInfo.lockId), posInfo.coin, sdk.Coins{}) + positionData, _, _, err := s.App.SuperfluidKeeper.RouteLockedBalancerToConcentratedMigration(s.Ctx, s.TestAccs[i+1], int64(posInfo.lockId), posInfo.coin, sdk.Coins{}) s.Require().NoError(err) // Note how much of amount0 and amount1 was actually created in the CL pool from the migration. - clJoinPoolAmt := sdk.NewCoins(sdk.NewCoin(clPool.GetToken0(), amount0), sdk.NewCoin(clPool.GetToken1(), amount1)) + clJoinPoolAmt := sdk.NewCoins(sdk.NewCoin(clPool.GetToken0(), positionData.Amount0), sdk.NewCoin(clPool.GetToken1(), positionData.Amount1)) // Note owner and balancer pool balances after migration. balancerPool, err = s.App.GAMMKeeper.GetCFMMPool(s.Ctx, balancerPoolId) @@ -1366,8 +1375,8 @@ func (s *KeeperTestSuite) TestFunctional_VaryingPositions_Migrations() { } // Add to the total amounts that were migrated and sent back to the owners. - totalAmount0Migrated = totalAmount0Migrated.Add(amount0) - totalAmount1Migrated = totalAmount1Migrated.Add(amount1) + totalAmount0Migrated = totalAmount0Migrated.Add(positionData.Amount0) + totalAmount1Migrated = totalAmount1Migrated.Add(positionData.Amount1) totalSentBackToOwnersAmount0 = totalSentBackToOwnersAmount0.Add(postClaimOwnerBalance.AmountOf(DefaultCoin0.Denom).Sub(preClaimOwnerBalance.AmountOf(DefaultCoin0.Denom))) totalSentBackToOwnersAmount1 = totalSentBackToOwnersAmount1.Add(postClaimOwnerBalance.AmountOf(DefaultCoin1.Denom).Sub(preClaimOwnerBalance.AmountOf(DefaultCoin1.Denom))) } diff --git a/x/superfluid/keeper/msg_server.go b/x/superfluid/keeper/msg_server.go index c29b02b800b..360be10a0fa 100644 --- a/x/superfluid/keeper/msg_server.go +++ b/x/superfluid/keeper/msg_server.go @@ -172,7 +172,7 @@ func (server msgServer) CreateFullRangePositionAndSuperfluidDelegate(goCtx conte if err != nil { return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err } - positionId, _, _, _, lockId, err := server.keeper.clk.CreateFullRangePositionLocked(ctx, msg.PoolId, address, msg.Coins, server.keeper.sk.GetParams(ctx).UnbondingTime) + positionData, lockId, err := server.keeper.clk.CreateFullRangePositionLocked(ctx, msg.PoolId, address, msg.Coins, server.keeper.sk.GetParams(ctx).UnbondingTime) if err != nil { return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err } @@ -189,11 +189,11 @@ func (server msgServer) CreateFullRangePositionAndSuperfluidDelegate(goCtx conte return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err } - events.EmitCreateFullRangePositionAndSuperfluidDelegateEvent(ctx, lockId, positionId, msg.ValAddr) + events.EmitCreateFullRangePositionAndSuperfluidDelegateEvent(ctx, lockId, positionData.ID, msg.ValAddr) return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{ LockID: lockId, - PositionID: positionId, + PositionID: positionData.ID, }, nil } @@ -205,7 +205,7 @@ func (server msgServer) UnlockAndMigrateSharesToFullRangeConcentratedPosition(go return nil, err } - positionId, amount0, amount1, liquidity, poolIdLeaving, poolIdEntering, clLockId, err := server.keeper.RouteLockedBalancerToConcentratedMigration(ctx, sender, msg.LockId, msg.SharesToMigrate, msg.TokenOutMins) + positionData, migratedPoolIDs, clLockId, err := server.keeper.RouteLockedBalancerToConcentratedMigration(ctx, sender, msg.LockId, msg.SharesToMigrate, msg.TokenOutMins) if err != nil { return nil, err } @@ -213,18 +213,18 @@ func (server msgServer) UnlockAndMigrateSharesToFullRangeConcentratedPosition(go ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.TypeEvtUnlockAndMigrateShares, - sdk.NewAttribute(types.AttributeKeyPoolIdEntering, strconv.FormatUint(poolIdEntering, 10)), - sdk.NewAttribute(types.AttributeKeyPoolIdLeaving, strconv.FormatUint(poolIdLeaving, 10)), + sdk.NewAttribute(types.AttributeKeyPoolIdEntering, strconv.FormatUint(migratedPoolIDs.EnteringID, 10)), + sdk.NewAttribute(types.AttributeKeyPoolIdLeaving, strconv.FormatUint(migratedPoolIDs.LeavingID, 10)), sdk.NewAttribute(types.AttributeConcentratedLockId, strconv.FormatUint(clLockId, 10)), sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender), - sdk.NewAttribute(types.AttributePositionId, strconv.FormatUint(positionId, 10)), - sdk.NewAttribute(types.AttributeAmount0, amount0.String()), - sdk.NewAttribute(types.AttributeAmount1, amount1.String()), - sdk.NewAttribute(types.AttributeLiquidity, liquidity.String()), + sdk.NewAttribute(types.AttributePositionId, strconv.FormatUint(positionData.ID, 10)), + sdk.NewAttribute(types.AttributeAmount0, positionData.Amount0.String()), + sdk.NewAttribute(types.AttributeAmount1, positionData.Amount1.String()), + sdk.NewAttribute(types.AttributeLiquidity, positionData.Liquidity.String()), ), }) - return &types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse{Amount0: amount0, Amount1: amount1, LiquidityCreated: liquidity}, err + return &types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse{Amount0: positionData.Amount0, Amount1: positionData.Amount1, LiquidityCreated: positionData.Liquidity}, err } func (server msgServer) AddToConcentratedLiquiditySuperfluidPosition(goCtx context.Context, msg *types.MsgAddToConcentratedLiquiditySuperfluidPosition) (*types.MsgAddToConcentratedLiquiditySuperfluidPositionResponse, error) { @@ -235,10 +235,21 @@ func (server msgServer) AddToConcentratedLiquiditySuperfluidPosition(goCtx conte return nil, err } - newPositionId, actualAmount0, actualAmount1, newLiquidity, newLockId, err := server.keeper.addToConcentratedLiquiditySuperfluidPosition(ctx, sender, msg.PositionId, msg.TokenDesired0.Amount, msg.TokenDesired1.Amount) + positionData, newLockId, err := server.keeper.addToConcentratedLiquiditySuperfluidPosition(ctx, sender, msg.PositionId, msg.TokenDesired0.Amount, msg.TokenDesired1.Amount) if err != nil { return nil, err } - return &types.MsgAddToConcentratedLiquiditySuperfluidPositionResponse{PositionId: newPositionId, Amount0: actualAmount0, Amount1: actualAmount1, LockId: newLockId, NewLiquidity: newLiquidity}, nil + return &types.MsgAddToConcentratedLiquiditySuperfluidPositionResponse{PositionId: positionData.ID, Amount0: positionData.Amount0, Amount1: positionData.Amount1, LockId: newLockId, NewLiquidity: positionData.Liquidity}, nil +} + +func (server msgServer) UnbondConvertAndStake(goCtx context.Context, msg *types.MsgUnbondConvertAndStake) (*types.MsgUnbondConvertAndStakeResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + totalAmtConverted, err := server.keeper.UnbondConvertAndStake(ctx, msg.LockId, msg.Sender, msg.ValAddr, msg.MinAmtToStake, msg.SharesToConvert) + if err != nil { + return nil, err + } + + return &types.MsgUnbondConvertAndStakeResponse{TotalAmtStaked: totalAmtConverted}, nil } diff --git a/x/superfluid/keeper/msg_server_test.go b/x/superfluid/keeper/msg_server_test.go index 7dee0e9763c..d7b784b09ac 100644 --- a/x/superfluid/keeper/msg_server_test.go +++ b/x/superfluid/keeper/msg_server_test.go @@ -565,7 +565,7 @@ func (s *KeeperTestSuite) TestAddToConcentratedLiquiditySuperfluidPosition_Event if !tc.isLastPositionInPool { s.FundAcc(s.TestAccs[1], defaultFunds) - _, _, _, _, err := concentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, 1, s.TestAccs[1], defaultFunds) + _, err := concentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, 1, s.TestAccs[1], defaultFunds) s.Require().NoError(err) } diff --git a/x/superfluid/keeper/slash.go b/x/superfluid/keeper/slash.go index c70578b3423..93dffb0fddc 100644 --- a/x/superfluid/keeper/slash.go +++ b/x/superfluid/keeper/slash.go @@ -135,7 +135,7 @@ func (k Keeper) prepareConcentratedLockForSlash(ctx sdk.Context, lock *lockuptyp coinsToSlash := sdk.NewCoins(asset0, asset1) // Update the cl positions liquidity to the new amount - _, _, _, _, err = k.clk.UpdatePosition(ctx, position.PoolId, sdk.MustAccAddressFromBech32(position.Address), position.LowerTick, position.UpperTick, slashAmtNeg, position.JoinTime, position.PositionId) + _, err = k.clk.UpdatePosition(ctx, position.PoolId, sdk.MustAccAddressFromBech32(position.Address), position.LowerTick, position.UpperTick, slashAmtNeg, position.JoinTime, position.PositionId) if err != nil { return sdk.AccAddress{}, sdk.Coins{}, err } diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index 768ccc63574..d9c5beb8990 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -2,12 +2,15 @@ package keeper import ( "fmt" + "strings" errorsmod "cosmossdk.io/errors" "github.com/osmosis-labs/osmosis/osmoutils" + gammtypes "github.com/osmosis-labs/osmosis/v17/x/gamm/types" lockuptypes "github.com/osmosis-labs/osmosis/v17/x/lockup/types" "github.com/osmosis-labs/osmosis/v17/x/superfluid/types" + valsettypes "github.com/osmosis-labs/osmosis/v17/x/valset-pref/types" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -128,7 +131,7 @@ func (k Keeper) IncreaseSuperfluidDelegation(ctx sdk.Context, lockID uint64, amo // basic validation for locks to be eligible for superfluid delegation. This includes checking // - that the sender is the owner of the lock // - that the lock is consisted of single coin -func (k Keeper) validateLockForSF(ctx sdk.Context, lock *lockuptypes.PeriodLock, sender string) error { +func (k Keeper) validateLockForSF(lock *lockuptypes.PeriodLock, sender string) error { if lock.Owner != sender { return lockuptypes.ErrNotLockOwner } @@ -146,7 +149,7 @@ func (k Keeper) validateLockForSF(ctx sdk.Context, lock *lockuptypes.PeriodLock, // - lock duration is greater or equal to the unbonding time // - lock should not be already superfluid staked func (k Keeper) validateLockForSFDelegate(ctx sdk.Context, lock *lockuptypes.PeriodLock, sender string) error { - err := k.validateLockForSF(ctx, lock, sender) + err := k.validateLockForSF(lock, sender) if err != nil { return err } @@ -255,7 +258,7 @@ func (k Keeper) undelegateCommon(ctx sdk.Context, sender string, lockID uint64) if err != nil { return types.SuperfluidIntermediaryAccount{}, err } - err = k.validateLockForSF(ctx, lock, sender) + err = k.validateLockForSF(lock, sender) if err != nil { return types.SuperfluidIntermediaryAccount{}, err } @@ -308,62 +311,6 @@ func (k Keeper) SuperfluidUndelegateToConcentratedPosition(ctx sdk.Context, send return k.undelegateCommon(ctx, sender, gammLockID) } -// partialUndelegateCommon acts similarly to undelegateCommon, but undelegates a partial amount of the lock's delegation rather than the full amount. The amount -// that is undelegated is placed in a new lock. This function returns the intermediary account associated with the original lock ID as well as the new lock that was created. -// An error is returned if the amount to undelegate is greater than the locked amount. -func (k Keeper) partialUndelegateCommon(ctx sdk.Context, sender string, lockID uint64, amountToUndelegate sdk.Coin) (intermediaryAcc types.SuperfluidIntermediaryAccount, newlock *lockuptypes.PeriodLock, err error) { //nolint:unused // this is actually used in the function below. - lock, err := k.lk.GetLockByID(ctx, lockID) - if err != nil { - return types.SuperfluidIntermediaryAccount{}, &lockuptypes.PeriodLock{}, err - } - err = k.validateLockForSF(ctx, lock, sender) - if err != nil { - return types.SuperfluidIntermediaryAccount{}, &lockuptypes.PeriodLock{}, err - } - - if amountToUndelegate.Amount.GTE(lock.Coins[0].Amount) { - return types.SuperfluidIntermediaryAccount{}, &lockuptypes.PeriodLock{}, fmt.Errorf("partial undelegate amount must be less than the locked amount") - } - - // get the intermediate account associated with lock id, and delete the connection. - intermediaryAcc, found := k.GetIntermediaryAccountFromLockId(ctx, lockID) - if !found { - return types.SuperfluidIntermediaryAccount{}, &lockuptypes.PeriodLock{}, types.ErrNotSuperfluidUsedLockup - } - - // undelegate the desired lock amount, and burn the minted osmo. - amount, err := k.GetSuperfluidOSMOTokens(ctx, intermediaryAcc.Denom, amountToUndelegate.Amount) - if err != nil { - return types.SuperfluidIntermediaryAccount{}, &lockuptypes.PeriodLock{}, err - } - err = k.forceUndelegateAndBurnOsmoTokens(ctx, amount, intermediaryAcc) - if err != nil { - return types.SuperfluidIntermediaryAccount{}, &lockuptypes.PeriodLock{}, err - } - - // Move the funds from the old lock to a new lock with the remaining amount. - newLock, err := k.lk.SplitLock(ctx, *lock, sdk.NewCoins(amountToUndelegate), true) - if err != nil { - return types.SuperfluidIntermediaryAccount{}, &lockuptypes.PeriodLock{}, err - } - - return intermediaryAcc, &newLock, nil -} - -// partialSuperfluidUndelegate starts undelegating a portion of a superfluid delegated position for the given lock. -// Undelegation is done instantly and the equivalent amount is sent to the module account -// where it is burnt. Note that this method does not include unbonding the lock -// itself. -// nolint: unused -func (k Keeper) partialSuperfluidUndelegate(ctx sdk.Context, sender string, lockID uint64, amountToUndelegate sdk.Coin) error { - intermediaryAcc, newLock, err := k.partialUndelegateCommon(ctx, sender, lockID, amountToUndelegate) - if err != nil { - return err - } - // Create a new synthetic lockup representing the unstaking side. - return k.createSyntheticLockup(ctx, newLock.ID, intermediaryAcc, unlockingStatus) -} - // SuperfluidUnbondLock unbonds the lock that has been used for superfluid staking. // This method would return an error if the underlying lock is not superfluid undelegating. func (k Keeper) SuperfluidUnbondLock(ctx sdk.Context, underlyingLockId uint64, sender string) error { @@ -457,7 +404,7 @@ func (k Keeper) unbondLock(ctx sdk.Context, underlyingLockId uint64, sender stri if err != nil { return 0, err } - err = k.validateLockForSF(ctx, lock, sender) + err = k.validateLockForSF(lock, sender) if err != nil { return 0, err } @@ -656,3 +603,221 @@ func (k Keeper) IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress, fn fn(index+int64(i), delegation) } } + +// UnbondConvertAndStake converts given lock to osmo and stakes it to given validator. +// Supports conversion of 1)superfluid bonded 2)superfluid undelegating 3)vanilla unlocking. +// Liquid gamm shares will not be supported for conversion. +// Delegation is done in the following logic: +// - If valAddr provided, single delegate. +// - If valAddr not provided and valset exists, valsetpref.Delegate +// - If valAddr not provided and valset delegation is not possible, refer back to original lock's superfluid validator if it was a superfluid lock +// - Else: error +func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, valAddr string, + minAmtToStake sdk.Int, sharesToConvert sdk.Coin) (totalAmtConverted sdk.Int, err error) { + senderAddr, err := sdk.AccAddressFromBech32(sender) + if err != nil { + return sdk.Int{}, err + } + + // use getMigrationType method to check status of lock (either superfluid staked, superfluid unbonding, vanilla locked, unlocked) + _, migrationType, err := k.getMigrationType(ctx, int64(lockID)) + if err != nil { + return sdk.Int{}, err + } + + // if superfluid bonded, first change it into superfluid undelegate to burn minted osmo and instantly undelegate. + if migrationType == SuperfluidBonded { + _, err = k.undelegateCommon(ctx, sender, lockID) + if err != nil { + return sdk.Int{}, err + } + } + + if migrationType == SuperfluidBonded || migrationType == SuperfluidUnbonding || migrationType == NonSuperfluid { + totalAmtConverted, err = k.convertLockToStake(ctx, senderAddr, valAddr, lockID, minAmtToStake) + } else if migrationType == Unlocked { // liquid gamm shares without locks + totalAmtConverted, err = k.convertUnlockedToStake(ctx, senderAddr, valAddr, sharesToConvert, minAmtToStake) + } else { // any other types of migration should fail + return sdk.Int{}, fmt.Errorf("unsupported staking conversion type") + } + + if err != nil { + return sdk.Int{}, err + } + + return totalAmtConverted, nil +} + +// convertLockToStake handles locks that are superfluid bonded, superfluid unbonding, vanilla locked(unlocking) locks. +// Deletes all associated state, converts the lock itself to staking delegation by going through exit pool and swap. +func (k Keeper) convertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, lockId uint64, + minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, err error) { + lock, err := k.lk.GetLockByID(ctx, lockId) + if err != nil { + return sdk.Int{}, err + } + + // check lock owner is sender + if lock.Owner != sender.String() { + return sdk.ZeroInt(), types.LockOwnerMismatchError{ + LockId: lock.ID, + LockOwner: lock.Owner, + ProvidedOwner: sender.String(), + } + } + + lockCoin := lock.Coins[0] + + // Ensuring the sharesToMigrate contains gamm pool share prefix. + if !strings.HasPrefix(lockCoin.Denom, gammtypes.GAMMTokenPrefix) { + return sdk.Int{}, types.SharesToMigrateDenomPrefixError{Denom: lockCoin.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} + } + + poolIdLeaving, err := gammtypes.GetPoolIdFromShareDenom(lockCoin.Denom) + if err != nil { + return sdk.Int{}, err + } + + var superfluidValAddr string + interAcc, found := k.GetIntermediaryAccountFromLockId(ctx, lockId) + if found { + superfluidValAddr = interAcc.ValAddr + } + + // Force unlock, validate the provided sharesToStake, and exit the balancer pool. + // we exit with min token out amount zero since we are checking min amount designated to stake later on anyways. + exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, lockCoin, sdk.NewCoins(), false) + if err != nil { + return sdk.Int{}, err + } + + totalAmtConverted, err = k.convertGammSharesToOsmoAndStake(ctx, sender, valAddr, poolIdLeaving, exitCoins, minAmtToStake, superfluidValAddr) + if err != nil { + return sdk.Int{}, err + } + + return totalAmtConverted, nil +} + +// convertUnlockedToStake converts liquid gamm shares to staking delegation. +// minAmtToStake works as slippage bound for the conversion process. +func (k Keeper) convertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, sharesToStake sdk.Coin, + minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, err error) { + if !strings.HasPrefix(sharesToStake.Denom, gammtypes.GAMMTokenPrefix) { + return sdk.Int{}, types.SharesToMigrateDenomPrefixError{Denom: sharesToStake.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} + } + + // Get the balancer poolId by parsing the gamm share denom. + poolIdLeaving, err := gammtypes.GetPoolIdFromShareDenom(sharesToStake.Denom) + if err != nil { + return sdk.Int{}, err + } + + // Exit the balancer pool position. + // we exit with min token out amount zero since we are checking min amount designated to stake later on anyways. + exitCoins, err := k.gk.ExitPool(ctx, sender, poolIdLeaving, sharesToStake.Amount, sdk.NewCoins()) + if err != nil { + return sdk.Int{}, err + } + + totalAmtConverted, err = k.convertGammSharesToOsmoAndStake(ctx, sender, valAddr, poolIdLeaving, exitCoins, minAmtToStake, "") + if err != nil { + return sdk.Int{}, err + } + + return totalAmtConverted, nil +} + +// convertGammSharesToOsmoAndStake converts given gamm shares to osmo by swapping in the given pool +// then stakes it to the designated validator. +// minAmtToStake works as slippage bound, and would error if total amount being staked is less than min amount to stake. +// Depending on user inputs, valAddr and originalSuperfluidValAddr could be an empty string, +// each leading to a different delegation scenario. +func (k Keeper) convertGammSharesToOsmoAndStake( + ctx sdk.Context, + sender sdk.AccAddress, valAddr string, + poolIdLeaving uint64, exitCoins sdk.Coins, minAmtToStake sdk.Int, originalSuperfluidValAddr string, +) (totalAmtCoverted sdk.Int, err error) { + var nonOsmoCoins sdk.Coins + bondDenom := k.sk.BondDenom(ctx) + + // from the exit coins, separate non-bond denom and bond denom. + for _, exitCoin := range exitCoins { + // if coin is not uosmo, add it to non-osmo Coins + if exitCoin.Denom != bondDenom { + nonOsmoCoins = append(nonOsmoCoins, exitCoin) + } + } + originalBondDenomAmt := exitCoins.AmountOf(bondDenom) + + // track how much non-uosmo tokens we have converted to uosmo + totalAmtCoverted = sdk.ZeroInt() + + // iterate over non-bond denom coins and swap them into bond denom + for _, coinToConvert := range nonOsmoCoins { + tokenOutAmt, err := k.pmk.SwapExactAmountIn(ctx, sender, poolIdLeaving, coinToConvert, bondDenom, sdk.ZeroInt()) + if err != nil { + return sdk.Int{}, err + } + + totalAmtCoverted = totalAmtCoverted.Add(tokenOutAmt) + } + + // add the converted amount with the amount of osmo from exit coin to get total amount we would be staking + totalAmtToStake := originalBondDenomAmt.Add(totalAmtCoverted) + + // check if the total amount to stake after all conversion is greater than provided min amount to stake + if totalAmtToStake.LT(minAmtToStake) { + return sdk.Int{}, types.TokenConvertedLessThenDesiredStakeError{ + ActualTotalAmtToStake: totalAmtToStake, + ExpectedTotalAmtToStake: minAmtToStake, + } + } + + err = k.delegateBaseOnValsetPref(ctx, sender, valAddr, originalSuperfluidValAddr, totalAmtToStake) + if err != nil { + return sdk.Int{}, err + } + + return totalAmtToStake, nil +} + +// delegateBaseOnValsetPref delegates based on given input parameters. +// valAddr and originalSuperfluidValAddr can be an empty string depending on user input and original lock's status. +// Delegation is done in the following logic: +// - If valAddr provided, single delegate. +// - If valAddr not provided and valset exists, valsetpref.Delegate +// - If valAddr not provided and valset delegation is not possible, refer back to original lock's superfluid validator if it was a superfluid lock +// - Else: error +func (k Keeper) delegateBaseOnValsetPref(ctx sdk.Context, sender sdk.AccAddress, valAddr, originalSuperfluidValAddr string, totalAmtToStake sdk.Int) error { + bondDenom := k.sk.BondDenom(ctx) + + // if given valAddr is empty, we use delegation preference given from valset-pref module or reference from superfluid staking + if valAddr == "" { + err := k.vspk.DelegateToValidatorSet(ctx, sender.String(), sdk.NewCoin(bondDenom, totalAmtToStake)) + // if valset-pref delegation succeeded without error, end method + if err == nil { + return nil + } + + // if valset-pref delegation errored due to no existing delegation existing, fall back and try using superfluid staked validator + if err == valsettypes.ErrNoDelegation { + valAddr = originalSuperfluidValAddr + } else if err != nil { // for other errors, handle error + return err + } + } + + val, err := k.validateValAddrForDelegate(ctx, valAddr) + if err != nil { + return err + } + + // delegate now! + _, err = k.sk.Delegate(ctx, sender, totalAmtToStake, stakingtypes.Unbonded, val, true) + if err != nil { + return err + } + + return nil +} diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index 7eed33376a0..ee53270b47a 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -6,11 +6,14 @@ import ( abci "github.com/tendermint/tendermint/abci/types" cltypes "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" + "github.com/osmosis-labs/osmosis/v17/x/gamm/pool-models/balancer" + gammtypes "github.com/osmosis-labs/osmosis/v17/x/gamm/types" lockuptypes "github.com/osmosis-labs/osmosis/v17/x/lockup/types" "github.com/osmosis-labs/osmosis/v17/x/superfluid/keeper" "github.com/osmosis-labs/osmosis/v17/x/superfluid/types" errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -1028,6 +1031,712 @@ func (s *KeeperTestSuite) TestRefreshIntermediaryDelegationAmounts() { } } +func (s *KeeperTestSuite) TestUnbondConvertAndStake() { + defaultJoinTime := s.Ctx.BlockTime() + type tc struct { + notSuperfluidDelegated bool + superfluidUndelegating bool + unlocking bool + unlocked bool + testCLLock bool + expectedError error + } + testCases := map[string]tc{ + "lock that is superfluid delegated": {}, + "lock that is superfluid undelegating": { + superfluidUndelegating: true, + }, + "bonded lock, not superfluid delegated": { + notSuperfluidDelegated: true, + }, + "lock that is unlocking": { + unlocking: true, + superfluidUndelegating: true, + }, + "unlocked gamm shares": { + notSuperfluidDelegated: true, + unlocked: true, + }, + "error: concentrated lock should fail": { + testCLLock: true, + expectedError: types.SharesToMigrateDenomPrefixError{ + Denom: "cl/pool/2", + ExpectedDenomPrefix: "gamm/pool/", + }, + }, + } + + for name, tc := range testCases { + s.Run(name, func() { + s.SetupTest() + s.Ctx = s.Ctx.WithBlockTime(defaultJoinTime) + + var ( + lock *lockuptypes.PeriodLock + lockId uint64 + joinPoolAcc sdk.AccAddress + originalValAddr sdk.ValAddress + balancerShareOut sdk.Coin + ) + // we use migration setup for testing with cl lock + if tc.testCLLock { + _, _, lock, _, joinPoolAcc, _, _, balancerShareOut, originalValAddr = s.SetupMigrationTest(s.Ctx, !tc.notSuperfluidDelegated, tc.superfluidUndelegating, tc.unlocking, tc.unlocked, sdk.MustNewDecFromStr("1")) + synthLockBeforeMigration, _, err := s.App.SuperfluidKeeper.GetMigrationType(s.Ctx, int64(lock.ID)) + s.Require().NoError(err) + _, lockId, _, err = s.App.SuperfluidKeeper.MigrateSuperfluidBondedBalancerToConcentrated(s.Ctx, joinPoolAcc, lock.ID, lock.Coins[0], synthLockBeforeMigration.SynthDenom, sdk.NewCoins()) + s.Require().NoError(err) + } else { + // We bundle all migration setup into a single function to avoid repeating the same code for each test case. + _, _, lock, _, joinPoolAcc, _, balancerShareOut, originalValAddr = s.SetupUnbondConvertAndStakeTest(s.Ctx, !tc.notSuperfluidDelegated, tc.superfluidUndelegating, tc.unlocking, tc.unlocked) + lockId = lock.ID + } + + sender := sdk.MustAccAddressFromBech32(joinPoolAcc.String()) + valAddr := s.SetupValidator(stakingtypes.Bonded) + minAmountToStake := sdk.ZeroInt() + sharesToConvert := sdk.NewInt64Coin("foo", 0) + if tc.unlocked { + sharesToConvert = balancerShareOut + } + + // only test with test related denoms + balanceBeforeConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender).FilterDenoms([]string{"foo", "stake", "uosmo"}) + + // system under test + totalAmtConverted, err := s.App.SuperfluidKeeper.UnbondConvertAndStake(s.Ctx, lockId, sender.String(), valAddr.String(), minAmountToStake, sharesToConvert) + if tc.expectedError != nil { + s.Require().Equal(err.Error(), tc.expectedError.Error()) + s.Require().Error(err) + return + } + s.Require().NoError(err) + + // Staking & Delegation check + s.delegationCheck(s.Ctx, sender, originalValAddr, valAddr, totalAmtConverted) + + // Bank check + balanceAfterConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender).FilterDenoms([]string{"foo", "stake", "uosmo"}) + s.Require().True(balanceBeforeConvertLockToStake.IsEqual(balanceAfterConvertLockToStake)) + + // if unlocked, no need to check locks since there is no lock existing + if tc.unlocked { + return + } + + // lock check + s.lockCheck(*lock, valAddr.String(), true) + }) + } +} + +func (s *KeeperTestSuite) TestConvertLockToStake() { + defaultJoinTime := s.Ctx.BlockTime() + type tc struct { + superfluidUndelegating bool + unlocking bool + notSuperfluidDelegated bool + + useMinAmountToStake bool + senderIsNotOwnerOfLock bool + useNonBalancerLock bool + + expectedError error + } + testCases := map[string]tc{ + "lock that is superfluid delegated": {}, + "lock that is superfluid undelegating": { + unlocking: true, + superfluidUndelegating: true, + }, + "lock that is unlocking": { + unlocking: true, + superfluidUndelegating: false, + }, + "bonded lock, not superfluid delegated": { + notSuperfluidDelegated: true, + }, + // error cases + "error: min amount to stake greater than actual amount": { + useMinAmountToStake: true, + expectedError: types.TokenConvertedLessThenDesiredStakeError{ + ActualTotalAmtToStake: sdk.NewInt(8309), + ExpectedTotalAmtToStake: sdk.NewInt(999999999), + }, + }, + "error: use non balancer lock": { + useNonBalancerLock: true, + expectedError: types.SharesToMigrateDenomPrefixError{ + Denom: "foo", + ExpectedDenomPrefix: "gamm/pool/", + }, + }, + "error: sender is not owner of lock ": { + senderIsNotOwnerOfLock: true, + expectedError: types.LockOwnerMismatchError{ + LockId: 1, + LockOwner: s.TestAccs[0].String(), + ProvidedOwner: s.TestAccs[1].String(), + }, + }, + } + + for name, tc := range testCases { + s.Run(name, func() { + s.SetupTest() + s.Ctx = s.Ctx.WithBlockTime(defaultJoinTime) + // We bundle all migration setup into a single function to avoid repeating the same code for each test case. + _, _, lock, _, _, _, _, originalValAddr := s.SetupUnbondConvertAndStakeTest(s.Ctx, !tc.notSuperfluidDelegated, tc.superfluidUndelegating, false, false) + + // testing params + sender := sdk.MustAccAddressFromBech32(lock.Owner) + if tc.senderIsNotOwnerOfLock { + sender = s.TestAccs[1] + } + + if tc.useNonBalancerLock { + nonBalancerShareDenomCoins := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(100))) + s.FundAcc(sender, nonBalancerShareDenomCoins) + newLock, err := s.App.LockupKeeper.CreateLock(s.Ctx, sender, nonBalancerShareDenomCoins, time.Second) + s.Require().NoError(err) + lock = &newLock + } + + valAddr := s.SetupValidator(stakingtypes.Bonded) + minAmountToStake := sdk.ZeroInt() + if tc.useMinAmountToStake { + minAmountToStake = sdk.NewInt(999999999) + } + + balanceBeforeConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) + + // system under test + totalAmtConverted, err := s.App.SuperfluidKeeper.ConvertLockToStake(s.Ctx, sender, valAddr.String(), lock.ID, minAmountToStake) + if tc.expectedError != nil { + s.Require().Error(err) + // TODO: come back to this specific err case + // err check for LockOwnerMismatchError needs further refactoring for all these test cases + // since lock owner is not know-able at the time of test creation + if !tc.senderIsNotOwnerOfLock { + s.Require().Equal(err.Error(), tc.expectedError.Error()) + } + return + } + s.Require().NoError(err) + + // Staking & Delegation check + s.delegationCheck(s.Ctx, sender, originalValAddr, valAddr, totalAmtConverted) + + // Lock check + s.lockCheck(*lock, valAddr.String(), true) + + // Bank check + balanceAfterConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) + s.Require().True(balanceBeforeConvertLockToStake.IsEqual(balanceAfterConvertLockToStake)) + }) + } +} + +func (s *KeeperTestSuite) TestConvertUnlockedToStake() { + defaultJoinTime := s.Ctx.BlockTime() + type tc struct { + usePartialShares bool + useMinAmountToStake bool + useNonGammPrefix bool + expectedError error + } + testCases := map[string]tc{ + "convert unlocked gamm shares": {}, + "convert partial shares": { + usePartialShares: true, + }, + "min amount to stake exceeds exit pool amount": { + useMinAmountToStake: true, + expectedError: types.TokenConvertedLessThenDesiredStakeError{ + ActualTotalAmtToStake: sdk.NewInt(8309), + ExpectedTotalAmtToStake: sdk.NewInt(999999999), + }, + }, + "error: use non gamm prefix": { + useNonGammPrefix: true, + expectedError: types.TokenConvertedLessThenDesiredStakeError{ + ActualTotalAmtToStake: sdk.NewInt(8309), + ExpectedTotalAmtToStake: sdk.NewInt(999999999), + }, + }, + } + + for name, tc := range testCases { + s.Run(name, func() { + s.SetupTest() + s.Ctx = s.Ctx.WithBlockTime(defaultJoinTime) + + // We bundle all migration setup into a single function to avoid repeating the same code for each test case. + _, _, _, _, sender, poolId, shareOut, _ := s.SetupUnbondConvertAndStakeTest(s.Ctx, false, false, false, true) + + // testing params + valAddr := s.SetupValidator(stakingtypes.Bonded) + minAmtToStake := sdk.ZeroInt() + if tc.useMinAmountToStake { + minAmtToStake = sdk.NewInt(9999999999) + } + sharesToStake := shareOut + if tc.usePartialShares { + sharesToStake.Amount = sharesToStake.Amount.Quo(sdk.NewInt(2)) + } + if tc.useNonGammPrefix { + sharesToStake = sdk.NewInt64Coin("foo", 10) + } + + balanceBeforeConvert := s.App.BankKeeper.GetBalance(s.Ctx, sender, shareOut.Denom) + s.Require().True(!balanceBeforeConvert.Amount.IsZero()) + + bondDenom := s.App.StakingKeeper.BondDenom(s.Ctx) + totalPoolLiquidityBeforeConvert, err := s.App.GAMMKeeper.GetTotalPoolLiquidity(s.Ctx, poolId) + s.Require().NoError(err) + bondDenomPoolAmtBeforeConvert := totalPoolLiquidityBeforeConvert.AmountOf(bondDenom) + + var expectedBondDenomAmt sdk.Int + // check expected bond denom pool liquidity amount after conversion(only for non error cases) + if tc.expectedError == nil { + expectedBondDenomAmt = s.getExpectedBondDenomPoolAmtAfterConvert(sender, poolId, sharesToStake) + } + + // system under test + totalAmtConverted, err := s.App.SuperfluidKeeper.ConvertUnlockedToStake(s.Ctx, sender, valAddr.String(), sharesToStake, minAmtToStake) + if tc.expectedError != nil { + s.Require().Error(err) + return + } + s.Require().NoError(err) + + // gamm check + totalPoolLiquidityAfterConvert, err := s.App.GAMMKeeper.GetTotalPoolLiquidity(s.Ctx, poolId) + s.Require().NoError(err) + // check that pool liquidity have reduced + bondDenomPoolAmtAfterConvert := totalPoolLiquidityAfterConvert.AmountOf(bondDenom) + s.Require().True(bondDenomPoolAmtAfterConvert.LT(bondDenomPoolAmtBeforeConvert)) + s.Require().True(expectedBondDenomAmt.Equal(bondDenomPoolAmtAfterConvert)) + + // Staking & Delegation check + s.delegationCheck(s.Ctx, sender, sdk.ValAddress{}, valAddr, totalAmtConverted) + + // Bank check + balanceAfterConvertLockToStake := s.App.BankKeeper.GetBalance(s.Ctx, sender, shareOut.Denom) + if tc.usePartialShares { + s.Require().True(balanceAfterConvertLockToStake.Amount.Equal(sharesToStake.Amount)) + } else { + s.Require().True(balanceAfterConvertLockToStake.IsZero()) + } + }) + } +} + +func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { + type tc struct { + useInvalidValAddr bool + useMinAmtToStake bool + useValSetPrefSingleVal bool + useValSetPrefMultipleVal bool + useSuperfluid bool + + expectedError string + } + testCases := map[string]tc{ + "superfluid staked, provide validator address": {}, + "use val set preference (single validator)": { + useValSetPrefSingleVal: true, + }, + "multiple validator returned from valset pref": { + useValSetPrefMultipleVal: true, + }, + "No validator returned from valset, fall back to superfluid delegation": { + useSuperfluid: true, + }, + "error: invalid val address": { + useInvalidValAddr: true, + expectedError: "invalid Bech32 prefix; expected osmovaloper, got osmo", + }, + "error: min amount to stake exceeds actual amount staking": { + useMinAmtToStake: true, + expectedError: "actual amount converted to stake (8309) is less then minimum amount expected to be staked (999999999)", + }, + } + + for name, tc := range testCases { + s.Run(name, func() { + s.SetupTest() + bondDenom := s.App.StakingKeeper.BondDenom(s.Ctx) + + // use setup helper function to setup pool, fund account with gamm shares + // note that we're not creating any locks here. + _, _, _, _, sender, poolId, shareOut, _ := s.SetupUnbondConvertAndStakeTest(s.Ctx, false, false, false, true) + // exit pool + exitCoins, err := s.App.GAMMKeeper.ExitPool(s.Ctx, sender, poolId, shareOut.Amount, sdk.NewCoins()) + s.Require().NoError(err) + + // test params + originalSuperfluidValAddr := "" + valAddr := s.SetupValidator(stakingtypes.Bonded) + valAddrString := valAddr.String() + if tc.useInvalidValAddr { + valAddrString = s.TestAccs[0].String() + } + + stakeCoin := sdk.NewInt64Coin(bondDenom, 100000) + if tc.useValSetPrefSingleVal || tc.useValSetPrefMultipleVal { + valAddrString = "" + + s.FundAcc(sender, sdk.NewCoins(stakeCoin)) + validator, found := s.App.StakingKeeper.GetValidator(s.Ctx, valAddr) + s.Require().True(found) + + _, err = s.App.StakingKeeper.Delegate(s.Ctx, sender, stakeCoin.Amount, stakingtypes.Unbonded, validator, true) + s.Require().NoError(err) + } + if tc.useSuperfluid { + originalSuperfluidValAddr = valAddrString + valAddrString = "" + } + + // if test case is setting multiple validator, stake one more time to a different validator + if tc.useValSetPrefMultipleVal { + valAddr2 := s.SetupValidator(stakingtypes.Bonded) + stakeCoin := sdk.NewInt64Coin(bondDenom, 100000) + s.FundAcc(sender, sdk.NewCoins(stakeCoin)) + validator, found := s.App.StakingKeeper.GetValidator(s.Ctx, valAddr2) + s.Require().True(found) + _, err = s.App.StakingKeeper.Delegate(s.Ctx, sender, stakeCoin.Amount, stakingtypes.Unbonded, validator, true) + s.Require().NoError(err) + } + + minAmtToStake := sdk.ZeroInt() + if tc.useMinAmtToStake { + minAmtToStake = sdk.NewInt(999999999) + } + + // mark expected shares before swap + nonStakeDenomCoin := exitCoins.FilterDenoms([]string{"foo"})[0] + stakeDenomCoin := exitCoins.AmountOf(bondDenom) + // use cache context to get expected amount after swap without changing test state + cc, _ := s.Ctx.CacheContext() + tokenOutAmt, err := s.App.PoolManagerKeeper.SwapExactAmountIn(cc, sender, poolId, nonStakeDenomCoin, bondDenom, sdk.ZeroInt()) + s.Require().NoError(err) + expectedTotalAmtStaked := tokenOutAmt.Add(stakeDenomCoin) + + // mark pool liquidity + pool, err := s.App.GAMMKeeper.GetPoolAndPoke(s.Ctx, poolId) + s.Require().NoError(err) + poolLiquidityBeforeSwap := pool.GetTotalPoolLiquidity(s.Ctx) + poolBeforeBondDenomAmt := poolLiquidityBeforeSwap.AmountOf("stake") + poolBeforeNonBondDenomAmt := poolLiquidityBeforeSwap.AmountOf("foo") + + // system under test. + totalAmtConverted, err := s.App.SuperfluidKeeper.ConvertGammSharesToOsmoAndStake(s.Ctx, sender, valAddrString, poolId, exitCoins, minAmtToStake, originalSuperfluidValAddr) + if tc.expectedError != "" { + s.Require().Equal(err.Error(), tc.expectedError) + s.Require().Error(err) + return + } + s.Require().NoError(err) + + // check that total Amount converted is equal to (swap result + original stake denom amount) + s.Require().True(expectedTotalAmtStaked.Equal(totalAmtConverted)) + + // check staking + if tc.useValSetPrefMultipleVal { + delegations := s.App.StakingKeeper.GetAllDelegatorDelegations(s.Ctx, sender) + // we used two validators + s.Require().True(len(delegations) == 2) + + delegation0Shares := delegations[0].Shares + delegation1Shares := delegations[1].Shares + + shareDiff := delegation0Shares.Sub(delegation1Shares).Abs() + + // in practice, the share amount between two validators should be equal, + // but due to how we handle truncation and rounding in valset pref, we expect the diff to be under one dec. + s.Require().True(shareDiff.LTE(sdk.OneDec())) + } else { + _, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) + s.Require().True(found) + } + + // check pool + pool, err = s.App.GAMMKeeper.GetPoolAndPoke(s.Ctx, poolId) + s.Require().NoError(err) + poolLiquidityAfterSwap := pool.GetTotalPoolLiquidity(s.Ctx) + poolAfterBondDenomAmt := poolLiquidityAfterSwap.AmountOf("stake") + poolAfterNonBondDenomAmt := poolLiquidityAfterSwap.AmountOf("foo") + // we swapped from non-bond denom to bond denom, + // thus bond denom token in pool should have decreased, non bond denom token should have increased + s.Require().True(poolBeforeBondDenomAmt.GT(poolAfterBondDenomAmt)) + s.Require().True(poolBeforeNonBondDenomAmt.LT(poolAfterNonBondDenomAmt)) + }) + } +} + +func (s *KeeperTestSuite) TestDelegateBaseOnValsetPref() { + type tc struct { + useValAddr bool + haveExistingDelegation bool + useOriginalSuperfluidValAddr bool + + useInvalidValAddr bool + + expectedError string + } + testCases := map[string]tc{ + "provide val address": { + useValAddr: true, + }, + "use valset pref delegation": { + haveExistingDelegation: true, + }, + "using valset pref fail, fallback to using provided original superfluid address": { + useOriginalSuperfluidValAddr: true, + }, + "error: using valset pref fail, no superfluid address provided": { + expectedError: "empty address string is not allowed", + }, + "error: invalid val address provided": { + useInvalidValAddr: true, + expectedError: "ecoding bech32 failed: invalid character not part of charset", + }, + } + + for name, tc := range testCases { + s.Run(name, func() { + s.Setup() + bondDenom := s.App.StakingKeeper.BondDenom(s.Ctx) + stakeAmount := sdk.NewInt(100) + + sender := s.TestAccs[0] + s.FundAcc(sender, sdk.NewCoins(sdk.NewCoin(bondDenom, stakeAmount))) + + var valAddr string + if tc.useValAddr { + valAddr = s.SetupValidator(stakingtypes.Bonded).String() + } + if tc.useInvalidValAddr { + valAddr = s.SetupValidator(stakingtypes.Bonded).String() + "invalid" + } + + var originalSuperfluidValAddr string + if tc.useOriginalSuperfluidValAddr { + originalSuperfluidValAddr = s.SetupValidator(stakingtypes.Bonded).String() + } + + // by having existing delegation, we can test val set pref based delgation + var superfluidStakedValAddr sdk.ValAddress + if tc.haveExistingDelegation { + superfluidStakedValAddr = s.SetupValidator(stakingtypes.Bonded) + + stakeCoin := sdk.NewInt64Coin(bondDenom, 100) + s.FundAcc(sender, sdk.NewCoins(stakeCoin)) + validator, found := s.App.StakingKeeper.GetValidator(s.Ctx, superfluidStakedValAddr) + s.Require().True(found) + _, err := s.App.StakingKeeper.Delegate(s.Ctx, sender, stakeCoin.Amount, stakingtypes.Unbonded, validator, true) + s.Require().NoError(err) + } + + // system under test + err := s.App.SuperfluidKeeper.DelegateBaseOnValsetPref(s.Ctx, sender, valAddr, originalSuperfluidValAddr, stakeAmount) + if tc.expectedError != "" { + s.Require().Error(err) + s.Require().ErrorContains(err, tc.expectedError) + return + } + + s.Require().NoError(err) + + // check delegation + if valAddr != "" || originalSuperfluidValAddr != "" { + // we want to check which ever param that was passed in with value + var delegatedAddr string + if valAddr == "" { + delegatedAddr = originalSuperfluidValAddr + } else { + delegatedAddr = valAddr + } + + val, err := sdk.ValAddressFromBech32(delegatedAddr) + s.Require().NoError(err) + del, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, val) + s.Require().True(found) + s.Require().True(del.Shares.RoundInt().Equal(stakeAmount)) + return + } + + // if we are testing valset-pref case(already deleated), check existing delegation address to see if delegation increased + if tc.haveExistingDelegation { + del, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, superfluidStakedValAddr) + s.Require().True(found) + // should be 200(original delegated amount + newly staked amount) + s.Require().True(del.Shares.RoundInt().Equal(stakeAmount.Mul(sdk.NewInt(2)))) + return + } + }) + } +} + +func (s *KeeperTestSuite) SetupUnbondConvertAndStakeTest(ctx sdk.Context, superfluidDelegated, superfluidUndelegating, unlocking, noLock bool) (joinPoolAmt sdk.Coins, balancerIntermediaryAcc types.SuperfluidIntermediaryAccount, balancerLock *lockuptypes.PeriodLock, poolCreateAcc, poolJoinAcc sdk.AccAddress, balancerPooId uint64, balancerPoolShareOut sdk.Coin, valAddr sdk.ValAddress) { + bankKeeper := s.App.BankKeeper + gammKeeper := s.App.GAMMKeeper + superfluidKeeper := s.App.SuperfluidKeeper + lockupKeeper := s.App.LockupKeeper + stakingKeeper := s.App.StakingKeeper + poolmanagerKeeper := s.App.PoolManagerKeeper + + // Generate and fund two accounts. + // Account 1 will be the account that creates the pool. + // Account 2 will be the account that joins the pool. + delAddrs := CreateRandomAccounts(2) + poolCreateAcc = delAddrs[0] + poolJoinAcc = delAddrs[1] + for _, acc := range delAddrs { + err := simapp.FundAccount(bankKeeper, ctx, acc, defaultAcctFunds) + s.Require().NoError(err) + } + + // Set up a single validator. + valAddr = s.SetupValidator(stakingtypes.Bonded) + + // Create a balancer pool of "stake" and "foo". + msg := balancer.NewMsgCreateBalancerPool(poolCreateAcc, balancer.PoolParams{ + SwapFee: sdk.NewDecWithPrec(1, 2), + ExitFee: sdk.NewDec(0), + }, defaultPoolAssets, defaultFutureGovernor) + balancerPooId, err := poolmanagerKeeper.CreatePool(ctx, msg) + s.Require().NoError(err) + + // Join the balancer pool. + // Note the account balance before and after joining the pool. + balanceBeforeJoin := bankKeeper.GetAllBalances(ctx, poolJoinAcc) + _, _, err = gammKeeper.JoinPoolNoSwap(ctx, poolJoinAcc, balancerPooId, gammtypes.OneShare.MulRaw(50), sdk.Coins{}) + s.Require().NoError(err) + balanceAfterJoin := bankKeeper.GetAllBalances(ctx, poolJoinAcc) + + // The balancer join pool amount is the difference between the account balance before and after joining the pool. + joinPoolAmt, _ = balanceBeforeJoin.SafeSub(balanceAfterJoin) + + // Determine the balancer pool's LP token denomination. + balancerPoolDenom := gammtypes.GetPoolShareDenom(balancerPooId) + + // Register the balancer pool's LP token as a superfluid asset + err = superfluidKeeper.AddNewSuperfluidAsset(ctx, types.SuperfluidAsset{ + Denom: balancerPoolDenom, + AssetType: types.SuperfluidAssetTypeLPShare, + }) + s.Require().NoError(err) + + // Note how much of the balancer pool's LP token the account that joined the pool has. + balancerPoolShareOut = bankKeeper.GetBalance(ctx, poolJoinAcc, balancerPoolDenom) + + // The unbonding duration is the same as the staking module's unbonding duration. + unbondingDuration := stakingKeeper.GetParams(ctx).UnbondingTime + + // Lock the LP tokens for the duration of the unbonding period. + originalGammLockId := uint64(0) + if !noLock { + originalGammLockId = s.LockTokens(poolJoinAcc, sdk.NewCoins(balancerPoolShareOut), unbondingDuration) + } + + // Superfluid delegate the balancer lock if the test case requires it. + // Note the intermediary account that was created. + if superfluidDelegated { + err = superfluidKeeper.SuperfluidDelegate(ctx, poolJoinAcc.String(), originalGammLockId, valAddr.String()) + s.Require().NoError(err) + intermediaryAccConnection := superfluidKeeper.GetLockIdIntermediaryAccountConnection(ctx, originalGammLockId) + balancerIntermediaryAcc = superfluidKeeper.GetIntermediaryAccount(ctx, intermediaryAccConnection) + } + + // Superfluid undelegate the lock if the test case requires it. + if superfluidUndelegating { + err = superfluidKeeper.SuperfluidUndelegate(ctx, poolJoinAcc.String(), originalGammLockId) + s.Require().NoError(err) + } + + // Unlock the balancer lock if the test case requires it. + if unlocking { + // If lock was superfluid staked, we can't unlock via `BeginUnlock`, + // we need to unlock lock via `SuperfluidUnbondLock` + if superfluidUndelegating { + err = superfluidKeeper.SuperfluidUnbondLock(ctx, originalGammLockId, poolJoinAcc.String()) + s.Require().NoError(err) + } else { + lock, err := lockupKeeper.GetLockByID(ctx, originalGammLockId) + s.Require().NoError(err) + _, err = lockupKeeper.BeginUnlock(ctx, originalGammLockId, lock.Coins) + s.Require().NoError(err) + } + } + + balancerLock = &lockuptypes.PeriodLock{} + if !noLock { + balancerLock, err = lockupKeeper.GetLockByID(ctx, originalGammLockId) + s.Require().NoError(err) + } + + s.Require().NoError(err) + return joinPoolAmt, balancerIntermediaryAcc, balancerLock, poolCreateAcc, poolJoinAcc, balancerPooId, balancerPoolShareOut, valAddr +} + +// delegationCheck checks staking related invariants of the test. +// We check the following in this method: +// - if superfluid staked previously, check if the original validator's delegation has been deleted. +// - Cehck if the delegation of the new validator matches what's expected. +func (s *KeeperTestSuite) delegationCheck(ctx sdk.Context, sender sdk.AccAddress, originalValAddr, newValAddr sdk.ValAddress, totalAmtConverted sdk.Int) { + if !originalValAddr.Empty() { + // check if original superfluid staked lock's delgation is successfully deleted + _, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, originalValAddr) + s.Require().False(found) + } + // check if delegation amount matches + delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, newValAddr) + s.Require().True(found) + s.Require().True(totalAmtConverted.ToDec().Equal(delegation.Shares)) + s.Require().True(delegation.Shares.Equal(totalAmtConverted.ToDec())) +} + +// lockCheck checks lock related invariants of the test. +// We check the following in this method: +// - check if old synth lock has been deleted (both staking & unstaking) +// - check if old lock has been successfully deleted. +func (s *KeeperTestSuite) lockCheck(lock lockuptypes.PeriodLock, valAddr string, checkUnstakingSynthLock bool) { + // The synthetic lockup should be deleted. + _, err := s.App.LockupKeeper.GetSyntheticLockup(s.Ctx, lock.ID, keeper.StakingSyntheticDenom(lock.Coins[0].Denom, valAddr)) + s.Require().Error(err) + + // intermediary account should have been deleted + _, err = s.App.LockupKeeper.GetSyntheticLockup(s.Ctx, lock.ID, keeper.UnstakingSyntheticDenom(lock.Coins[0].Denom, valAddr)) + s.Require().Error(err) + + // Lock check + _, err = s.App.LockupKeeper.GetLockByID(s.Ctx, lock.ID) + s.Require().Error(err) +} + +func (s *KeeperTestSuite) getExpectedBondDenomPoolAmtAfterConvert(sender sdk.AccAddress, poolId uint64, sharesToStake sdk.Coin) sdk.Int { + bondDenom := s.App.StakingKeeper.BondDenom(s.Ctx) + cc, _ := s.Ctx.CacheContext() + exitCoins, err := s.App.GAMMKeeper.ExitPool(cc, sender, poolId, sharesToStake.Amount, sdk.NewCoins()) + s.Require().NoError(err) + + var nonOsmoCoin sdk.Coin + for _, exitCoin := range exitCoins { + // if coin is not uosmo, add it to non-osmo Coins + if exitCoin.Denom != bondDenom { + nonOsmoCoin = exitCoin + } + } + _, err = s.App.PoolManagerKeeper.SwapExactAmountIn(cc, sender, poolId, nonOsmoCoin, bondDenom, sdk.ZeroInt()) + s.Require().NoError(err) + expectedLiquidity, err := s.App.GAMMKeeper.GetTotalPoolLiquidity(cc, poolId) + s.Require().NoError(err) + + return expectedLiquidity.AmountOf(bondDenom) +} + // type superfluidRedelegation struct { // lockId uint64 // oldValIndex int64 diff --git a/x/superfluid/types/codec.go b/x/superfluid/types/codec.go index aff8c223d21..4485fbb0af0 100644 --- a/x/superfluid/types/codec.go +++ b/x/superfluid/types/codec.go @@ -22,6 +22,7 @@ func RegisterCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition{}, "osmosis/unlock-and-migrate", nil) cdc.RegisterConcrete(&MsgCreateFullRangePositionAndSuperfluidDelegate{}, "osmosis/full-range-and-sf-delegate", nil) cdc.RegisterConcrete(&MsgAddToConcentratedLiquiditySuperfluidPosition{}, "osmosis/add-to-cl-superfluid-position", nil) + cdc.RegisterConcrete(&MsgUnbondConvertAndStake{}, "osmosis/unbond-convert-and-stake", nil) } func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { @@ -36,6 +37,7 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { &MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition{}, &MsgCreateFullRangePositionAndSuperfluidDelegate{}, &MsgAddToConcentratedLiquiditySuperfluidPosition{}, + &MsgUnbondConvertAndStake{}, ) registry.RegisterImplementations( diff --git a/x/superfluid/types/errors.go b/x/superfluid/types/errors.go index ada05c155d5..61dbf5625af 100644 --- a/x/superfluid/types/errors.go +++ b/x/superfluid/types/errors.go @@ -6,6 +6,8 @@ import ( errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + cltypes "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" ) @@ -115,3 +117,12 @@ type UnexpectedDenomError struct { func (e UnexpectedDenomError) Error() string { return fmt.Sprintf("provided denom (%s) was expected to be formatted as follows: %s", e.ProvidedDenom, e.ExpectedDenom) } + +type TokenConvertedLessThenDesiredStakeError struct { + ActualTotalAmtToStake sdk.Int + ExpectedTotalAmtToStake sdk.Int +} + +func (e TokenConvertedLessThenDesiredStakeError) Error() string { + return fmt.Sprintf("actual amount converted to stake (%s) is less then minimum amount expected to be staked (%s)", e.ActualTotalAmtToStake, e.ExpectedTotalAmtToStake) +} diff --git a/x/superfluid/types/expected_keepers.go b/x/superfluid/types/expected_keepers.go index 9bb7b76d92f..f762177d2bf 100644 --- a/x/superfluid/types/expected_keepers.go +++ b/x/superfluid/types/expected_keepers.go @@ -56,7 +56,7 @@ type GammKeeper interface { ExitPool(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, shareInAmount sdk.Int, tokenOutMins sdk.Coins) (exitCoins sdk.Coins, err error) GetAllMigrationInfo(ctx sdk.Context) (gammmigration.MigrationRecords, error) GetLinkedConcentratedPoolID(ctx sdk.Context, poolIdLeaving uint64) (poolIdEntering uint64, err error) - MigrateUnlockedPositionFromBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, poolIdLeaving, poolIdEntering uint64, err error) + MigrateUnlockedPositionFromBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (positionData cltypes.CreateFullRangePositionData, migratedPoolIDs MigrationPoolIDs, err error) } type BankKeeper interface { @@ -109,10 +109,10 @@ type EpochKeeper interface { type ConcentratedKeeper interface { GetPosition(ctx sdk.Context, positionId uint64) (model.Position, error) SetPosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, lowerTick, upperTick int64, joinTime time.Time, liquidity sdk.Dec, positionId uint64, underlyingLockId uint64) error - UpdatePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, lowerTick, upperTick int64, liquidityDelta sdk.Dec, joinTime time.Time, positionId uint64) (sdk.Int, sdk.Int, bool, bool, error) + UpdatePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, lowerTick, upperTick int64, liquidityDelta sdk.Dec, joinTime time.Time, positionId uint64) (cltypes.UpdatePositionData, error) GetConcentratedPoolById(ctx sdk.Context, poolId uint64) (cltypes.ConcentratedPoolExtension, error) - CreateFullRangePositionLocked(ctx sdk.Context, clPoolId uint64, owner sdk.AccAddress, coins sdk.Coins, remainingLockDuration time.Duration) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, concentratedLockID uint64, err error) - CreateFullRangePositionUnlocking(ctx sdk.Context, clPoolId uint64, owner sdk.AccAddress, coins sdk.Coins, remainingLockDuration time.Duration) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, concentratedLockID uint64, err error) + CreateFullRangePositionLocked(ctx sdk.Context, clPoolId uint64, owner sdk.AccAddress, coins sdk.Coins, remainingLockDuration time.Duration) (positionData cltypes.CreateFullRangePositionData, concentratedLockID uint64, err error) + CreateFullRangePositionUnlocking(ctx sdk.Context, clPoolId uint64, owner sdk.AccAddress, coins sdk.Coins, remainingLockDuration time.Duration) (positionData cltypes.CreateFullRangePositionData, concentratedLockID uint64, err error) GetPositionIdToLockId(ctx sdk.Context, underlyingLockId uint64) (uint64, error) GetFullRangeLiquidityInPool(ctx sdk.Context, poolId uint64) (sdk.Dec, error) PositionHasActiveUnderlyingLock(ctx sdk.Context, positionId uint64) (bool, uint64, error) @@ -121,3 +121,18 @@ type ConcentratedKeeper interface { GetUserPositions(ctx sdk.Context, addr sdk.AccAddress, poolId uint64) ([]model.Position, error) GetLockIdFromPositionId(ctx sdk.Context, positionId uint64) (uint64, error) } + +type PoolManagerKeeper interface { + SwapExactAmountIn( + ctx sdk.Context, + sender sdk.AccAddress, + poolId uint64, + tokenIn sdk.Coin, + tokenOutDenom string, + tokenOutMinAmount sdk.Int, + ) (sdk.Int, error) +} + +type ValSetPreferenceKeeper interface { + DelegateToValidatorSet(ctx sdk.Context, delegatorAddr string, coin sdk.Coin) error +} diff --git a/x/superfluid/types/migration.go b/x/superfluid/types/migration.go new file mode 100644 index 00000000000..80b425f2001 --- /dev/null +++ b/x/superfluid/types/migration.go @@ -0,0 +1,6 @@ +package types + +type MigrationPoolIDs struct { + LeavingID uint64 + EnteringID uint64 +} diff --git a/x/superfluid/types/msg_test.go b/x/superfluid/types/msg_test.go index 9d3d4ab6f20..761a1792d46 100644 --- a/x/superfluid/types/msg_test.go +++ b/x/superfluid/types/msg_test.go @@ -4,6 +4,8 @@ import ( "testing" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/osmosis-labs/osmosis/v17/app/apptesting" "github.com/osmosis-labs/osmosis/v17/x/superfluid/types" @@ -73,3 +75,78 @@ func TestAuthzMsg(t *testing.T) { }) } } + +func TestUnbondConvertAndStakeMsg(t *testing.T) { + pk1 := ed25519.GenPrivKey().PubKey() + addr1 := sdk.AccAddress(pk1.Address()).String() + + valPub := secp256k1.GenPrivKey().PubKey() + valAddr := sdk.ValAddress(valPub.Address()).String() + + testCases := []struct { + name string + msg sdk.Msg + expectedError bool + }{ + { + name: "happy case", + msg: &types.MsgUnbondConvertAndStake{ + LockId: 2, + Sender: addr1, + ValAddr: valAddr, + MinAmtToStake: sdk.NewInt(10), + SharesToConvert: sdk.NewInt64Coin("foo", 10), + }, + }, + { + name: "lock id is 0 should not fail", + msg: &types.MsgUnbondConvertAndStake{ + LockId: 0, + Sender: addr1, + ValAddr: valAddr, + MinAmtToStake: sdk.NewInt(10), + SharesToConvert: sdk.NewInt64Coin("foo", 10), + }, + }, + { + name: "no val address should not fail", + msg: &types.MsgUnbondConvertAndStake{ + LockId: 0, + Sender: addr1, + MinAmtToStake: sdk.NewInt(10), + SharesToConvert: sdk.NewInt64Coin("foo", 10), + }, + }, + { + name: "err: sender is invalid", + msg: &types.MsgUnbondConvertAndStake{ + LockId: 0, + Sender: "abcd", + ValAddr: valAddr, + MinAmtToStake: sdk.NewInt(10), + SharesToConvert: sdk.NewInt64Coin("foo", 10), + }, + expectedError: true, + }, + { + name: "err: min amount to stake is negative", + msg: &types.MsgUnbondConvertAndStake{ + LockId: 0, + Sender: addr1, + MinAmtToStake: sdk.NewInt(10).Neg(), + SharesToConvert: sdk.NewInt64Coin("foo", 10), + }, + expectedError: true, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.expectedError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/superfluid/types/msgs.go b/x/superfluid/types/msgs.go index b752105464d..c6a85aa2195 100644 --- a/x/superfluid/types/msgs.go +++ b/x/superfluid/types/msgs.go @@ -21,6 +21,7 @@ const ( TypeMsgUnlockAndMigrateShares = "unlock_and_migrate_shares" TypeMsgCreateFullRangePositionAndSuperfluidDelegate = "create_full_range_position_and_delegate" TypeMsgAddToConcentratedLiquiditySuperfluidPosition = "add_to_concentrated_liquidity_superfluid_position" + TypeMsgUnbondConvertAndStake = "add_to_concentrated_liquidity_superfluid_position" ) var _ sdk.Msg = &MsgSuperfluidDelegate{} @@ -392,3 +393,44 @@ func (msg MsgAddToConcentratedLiquiditySuperfluidPosition) GetSigners() []sdk.Ac } return []sdk.AccAddress{sender} } + +var _ sdk.Msg = &MsgUnbondConvertAndStake{} + +func NewMsgUnbondConvertAndStake(sender sdk.AccAddress, lockId uint64, valAddr string, minAmtToStake sdk.Int, sharesToConvert sdk.Coin) *MsgUnbondConvertAndStake { + return &MsgUnbondConvertAndStake{ + Sender: sender.String(), + LockId: lockId, + ValAddr: valAddr, + MinAmtToStake: minAmtToStake, + SharesToConvert: sharesToConvert, + } +} + +func (msg MsgUnbondConvertAndStake) Route() string { return RouterKey } +func (msg MsgUnbondConvertAndStake) Type() string { + return TypeMsgUnbondConvertAndStake +} + +func (msg MsgUnbondConvertAndStake) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return fmt.Errorf("Invalid sender address (%s)", err) + } + + if msg.MinAmtToStake.IsNegative() { + return fmt.Errorf("Min amount to stake cannot be negative") + } + return nil +} + +func (msg MsgUnbondConvertAndStake) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgUnbondConvertAndStake) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} diff --git a/x/superfluid/types/query.pb.go b/x/superfluid/types/query.pb.go index f514e0b6b7b..f4ede4ca33f 100644 --- a/x/superfluid/types/query.pb.go +++ b/x/superfluid/types/query.pb.go @@ -1988,6 +1988,7 @@ type QueryClient interface { TotalDelegationByDelegator(ctx context.Context, in *QueryTotalDelegationByDelegatorRequest, opts ...grpc.CallOption) (*QueryTotalDelegationByDelegatorResponse, error) // Returns a list of whitelisted pool ids to unpool. UnpoolWhitelist(ctx context.Context, in *QueryUnpoolWhitelistRequest, opts ...grpc.CallOption) (*QueryUnpoolWhitelistResponse, error) + // Returns all of a user's full range CL positions that are superfluid staked. UserConcentratedSuperfluidPositionsDelegated(ctx context.Context, in *UserConcentratedSuperfluidPositionsDelegatedRequest, opts ...grpc.CallOption) (*UserConcentratedSuperfluidPositionsDelegatedResponse, error) UserConcentratedSuperfluidPositionsUndelegating(ctx context.Context, in *UserConcentratedSuperfluidPositionsUndelegatingRequest, opts ...grpc.CallOption) (*UserConcentratedSuperfluidPositionsUndelegatingResponse, error) } @@ -2191,6 +2192,7 @@ type QueryServer interface { TotalDelegationByDelegator(context.Context, *QueryTotalDelegationByDelegatorRequest) (*QueryTotalDelegationByDelegatorResponse, error) // Returns a list of whitelisted pool ids to unpool. UnpoolWhitelist(context.Context, *QueryUnpoolWhitelistRequest) (*QueryUnpoolWhitelistResponse, error) + // Returns all of a user's full range CL positions that are superfluid staked. UserConcentratedSuperfluidPositionsDelegated(context.Context, *UserConcentratedSuperfluidPositionsDelegatedRequest) (*UserConcentratedSuperfluidPositionsDelegatedResponse, error) UserConcentratedSuperfluidPositionsUndelegating(context.Context, *UserConcentratedSuperfluidPositionsUndelegatingRequest) (*UserConcentratedSuperfluidPositionsUndelegatingResponse, error) } diff --git a/x/superfluid/types/tx.pb.go b/x/superfluid/types/tx.pb.go index e14270d6fbe..1aa375fd1c2 100644 --- a/x/superfluid/types/tx.pb.go +++ b/x/superfluid/types/tx.pb.go @@ -1021,6 +1021,122 @@ func (m *MsgAddToConcentratedLiquiditySuperfluidPositionResponse) GetLockId() ui return 0 } +// ===================== MsgUnbondConvertAndStake +type MsgUnbondConvertAndStake struct { + // lock ID to convert and stake. + // lock id with 0 should be provided if converting liquid gamm shares to stake + LockId uint64 `protobuf:"varint,1,opt,name=lock_id,json=lockId,proto3" json:"lock_id,omitempty" yaml:"lock_id"` + Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + // validator address to delegate to. + // If provided empty string, we use the validators returned from + // valset-preference module. + ValAddr string `protobuf:"bytes,3,opt,name=val_addr,json=valAddr,proto3" json:"val_addr,omitempty"` + // min_amt_to_stake indicates the minimum amount to stake after conversion + MinAmtToStake github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=min_amt_to_stake,json=minAmtToStake,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"min_amt_to_stake" yaml:"min_amt_to_stake"` + // shares_to_convert indicates shares wanted to stake. + // Note that this field is only used for liquid(unlocked) gamm shares. + // For all other cases, this field would be disregarded. + SharesToConvert types.Coin `protobuf:"bytes,5,opt,name=shares_to_convert,json=sharesToConvert,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coin" json:"shares_to_convert" yaml:"shares_to_convert"` +} + +func (m *MsgUnbondConvertAndStake) Reset() { *m = MsgUnbondConvertAndStake{} } +func (m *MsgUnbondConvertAndStake) String() string { return proto.CompactTextString(m) } +func (*MsgUnbondConvertAndStake) ProtoMessage() {} +func (*MsgUnbondConvertAndStake) Descriptor() ([]byte, []int) { + return fileDescriptor_55b645f187d22814, []int{18} +} +func (m *MsgUnbondConvertAndStake) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUnbondConvertAndStake) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUnbondConvertAndStake.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUnbondConvertAndStake) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUnbondConvertAndStake.Merge(m, src) +} +func (m *MsgUnbondConvertAndStake) XXX_Size() int { + return m.Size() +} +func (m *MsgUnbondConvertAndStake) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUnbondConvertAndStake.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUnbondConvertAndStake proto.InternalMessageInfo + +func (m *MsgUnbondConvertAndStake) GetLockId() uint64 { + if m != nil { + return m.LockId + } + return 0 +} + +func (m *MsgUnbondConvertAndStake) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgUnbondConvertAndStake) GetValAddr() string { + if m != nil { + return m.ValAddr + } + return "" +} + +func (m *MsgUnbondConvertAndStake) GetSharesToConvert() types.Coin { + if m != nil { + return m.SharesToConvert + } + return types.Coin{} +} + +type MsgUnbondConvertAndStakeResponse struct { + TotalAmtStaked github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=total_amt_staked,json=totalAmtStaked,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_amt_staked" yaml:"total_amt_staked"` +} + +func (m *MsgUnbondConvertAndStakeResponse) Reset() { *m = MsgUnbondConvertAndStakeResponse{} } +func (m *MsgUnbondConvertAndStakeResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUnbondConvertAndStakeResponse) ProtoMessage() {} +func (*MsgUnbondConvertAndStakeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_55b645f187d22814, []int{19} +} +func (m *MsgUnbondConvertAndStakeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUnbondConvertAndStakeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUnbondConvertAndStakeResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUnbondConvertAndStakeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUnbondConvertAndStakeResponse.Merge(m, src) +} +func (m *MsgUnbondConvertAndStakeResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUnbondConvertAndStakeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUnbondConvertAndStakeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUnbondConvertAndStakeResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*MsgSuperfluidDelegate)(nil), "osmosis.superfluid.MsgSuperfluidDelegate") proto.RegisterType((*MsgSuperfluidDelegateResponse)(nil), "osmosis.superfluid.MsgSuperfluidDelegateResponse") @@ -1040,96 +1156,109 @@ func init() { proto.RegisterType((*MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse)(nil), "osmosis.superfluid.MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse") proto.RegisterType((*MsgAddToConcentratedLiquiditySuperfluidPosition)(nil), "osmosis.superfluid.MsgAddToConcentratedLiquiditySuperfluidPosition") proto.RegisterType((*MsgAddToConcentratedLiquiditySuperfluidPositionResponse)(nil), "osmosis.superfluid.MsgAddToConcentratedLiquiditySuperfluidPositionResponse") + proto.RegisterType((*MsgUnbondConvertAndStake)(nil), "osmosis.superfluid.MsgUnbondConvertAndStake") + proto.RegisterType((*MsgUnbondConvertAndStakeResponse)(nil), "osmosis.superfluid.MsgUnbondConvertAndStakeResponse") } func init() { proto.RegisterFile("osmosis/superfluid/tx.proto", fileDescriptor_55b645f187d22814) } var fileDescriptor_55b645f187d22814 = []byte{ - // 1343 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xcf, 0x73, 0xd3, 0xc6, - 0x17, 0x8f, 0xec, 0x90, 0xc0, 0x86, 0x00, 0xd1, 0x97, 0x1f, 0xc6, 0x5f, 0xb0, 0xcc, 0x42, 0x69, - 0xf8, 0x61, 0x29, 0x86, 0x16, 0x98, 0x9c, 0x88, 0xe3, 0xa1, 0x63, 0x1a, 0x4f, 0x19, 0x11, 0xa6, - 0x33, 0x5c, 0x3c, 0xb2, 0x77, 0x23, 0xd4, 0xc8, 0x5a, 0xa3, 0x95, 0x92, 0x30, 0x3d, 0xb5, 0x3d, - 0x74, 0x86, 0x13, 0xc7, 0xde, 0x7a, 0x6e, 0x0f, 0x1d, 0xfe, 0x84, 0x1e, 0x7a, 0x60, 0x7a, 0xe2, - 0xd8, 0x69, 0x67, 0x42, 0x87, 0x1c, 0x7a, 0xcf, 0xb1, 0xa7, 0xce, 0x4a, 0xab, 0xb5, 0x9c, 0x48, - 0x38, 0x32, 0xee, 0xa1, 0x97, 0x44, 0xbb, 0xfb, 0x7e, 0x7c, 0xde, 0xdb, 0xf7, 0x79, 0xbb, 0x6b, - 0xf0, 0x7f, 0x42, 0xbb, 0x84, 0x5a, 0x54, 0xa3, 0x7e, 0x0f, 0xbb, 0x6b, 0xb6, 0x6f, 0x21, 0xcd, - 0xdb, 0x52, 0x7b, 0x2e, 0xf1, 0x88, 0x2c, 0xf3, 0x45, 0xb5, 0xbf, 0x58, 0x3c, 0x69, 0x12, 0x93, - 0x04, 0xcb, 0x1a, 0xfb, 0x0a, 0x25, 0x8b, 0x73, 0x46, 0xd7, 0x72, 0x88, 0x16, 0xfc, 0xe5, 0x53, - 0x25, 0x93, 0x10, 0xd3, 0xc6, 0x5a, 0x30, 0x6a, 0xfb, 0x6b, 0x1a, 0xf2, 0x5d, 0xc3, 0xb3, 0x88, - 0x13, 0xad, 0x77, 0x02, 0xeb, 0x5a, 0xdb, 0xa0, 0x58, 0xdb, 0xa8, 0xb6, 0xb1, 0x67, 0x54, 0xb5, - 0x0e, 0xb1, 0xa2, 0x75, 0x65, 0xaf, 0xbe, 0x67, 0x75, 0x31, 0xf5, 0x8c, 0x6e, 0x8f, 0x0b, 0x5c, - 0x4c, 0x80, 0xde, 0xff, 0x0c, 0x85, 0xe0, 0x77, 0x12, 0x38, 0xd5, 0xa4, 0xe6, 0x43, 0x31, 0x5f, - 0xc7, 0x36, 0x36, 0x0d, 0x0f, 0xcb, 0x57, 0xc0, 0x14, 0xc5, 0x0e, 0xc2, 0x6e, 0x41, 0x2a, 0x4b, - 0xf3, 0x47, 0x6a, 0x73, 0xbb, 0xdb, 0xca, 0xec, 0x33, 0xa3, 0x6b, 0x2f, 0xc2, 0x70, 0x1e, 0xea, - 0x5c, 0x40, 0x3e, 0x03, 0xa6, 0x6d, 0xd2, 0x59, 0x6f, 0x59, 0xa8, 0x90, 0x2b, 0x4b, 0xf3, 0x93, - 0xfa, 0x14, 0x1b, 0x36, 0x90, 0x7c, 0x16, 0x1c, 0xde, 0x30, 0xec, 0x96, 0x81, 0x90, 0x5b, 0xc8, - 0x33, 0x2b, 0xfa, 0xf4, 0x86, 0x61, 0x2f, 0x21, 0xe4, 0x2e, 0x96, 0x9f, 0xff, 0xf5, 0xf2, 0x6a, - 0x42, 0x76, 0x2b, 0x88, 0x03, 0x80, 0x0a, 0x38, 0x9f, 0x88, 0x4c, 0xc7, 0xb4, 0x47, 0x1c, 0x8a, - 0xe1, 0x57, 0x12, 0x38, 0x33, 0x20, 0xf1, 0xc8, 0x41, 0x63, 0x44, 0xbf, 0x08, 0x19, 0xc4, 0xf3, - 0x09, 0x10, 0x7d, 0xe1, 0x07, 0x5e, 0x00, 0x4a, 0x0a, 0x04, 0x01, 0xf3, 0xeb, 0xfd, 0x30, 0xdb, - 0xc4, 0x41, 0x2b, 0xa4, 0xb3, 0x3e, 0x16, 0x98, 0x17, 0x19, 0xcc, 0x52, 0x22, 0x4c, 0xe6, 0xa7, - 0xc2, 0xc4, 0x12, 0x70, 0x46, 0x18, 0x04, 0xce, 0x9f, 0x24, 0x70, 0x29, 0x25, 0x96, 0x25, 0x67, - 0xcc, 0xa0, 0xe5, 0x1a, 0x98, 0x64, 0xb5, 0x1c, 0x54, 0xc5, 0xcc, 0x8d, 0xb3, 0x6a, 0x58, 0xec, - 0x2a, 0x2b, 0x76, 0x95, 0x17, 0xbb, 0xba, 0x4c, 0x2c, 0xa7, 0xf6, 0xbf, 0x57, 0xdb, 0xca, 0xc4, - 0xee, 0xb6, 0x32, 0x13, 0x3a, 0x60, 0x4a, 0x50, 0x0f, 0x74, 0xe1, 0x27, 0xe0, 0xfa, 0x41, 0xf0, - 0x46, 0x01, 0xc6, 0xc1, 0x48, 0x71, 0x30, 0x70, 0x57, 0x02, 0xe7, 0x9a, 0xd4, 0x64, 0xc2, 0x4b, - 0x0e, 0x7a, 0x3f, 0x2e, 0x18, 0xe0, 0x10, 0x03, 0x47, 0x0b, 0xb9, 0x72, 0xfe, 0xdd, 0x91, 0x2d, - 0xb0, 0xc8, 0x7e, 0x7c, 0xa3, 0xcc, 0x9b, 0x96, 0xf7, 0xc4, 0x6f, 0xab, 0x1d, 0xd2, 0xd5, 0x38, - 0xe7, 0xc3, 0x7f, 0x15, 0x8a, 0xd6, 0x35, 0xef, 0x59, 0x0f, 0xd3, 0x40, 0x81, 0xea, 0xa1, 0xe5, - 0x77, 0xb1, 0xea, 0x0a, 0xab, 0x85, 0x4b, 0x51, 0x2d, 0xb0, 0xf0, 0x2a, 0x86, 0x83, 0x2a, 0x49, - 0xf4, 0xba, 0x15, 0xec, 0x76, 0x6a, 0xcc, 0x22, 0x6b, 0xc7, 0x40, 0xae, 0x51, 0xe7, 0x09, 0xcb, - 0x35, 0xea, 0xf0, 0x65, 0x0e, 0x68, 0x4d, 0x6a, 0x2e, 0xbb, 0xd8, 0xf0, 0xf0, 0x3d, 0xdf, 0xb6, - 0x75, 0xc3, 0x31, 0xf1, 0x03, 0x42, 0x2d, 0xd6, 0xbc, 0xfe, 0xdb, 0xf9, 0x93, 0xaf, 0x81, 0xe9, - 0x1e, 0x21, 0x36, 0x2b, 0x91, 0x49, 0x16, 0x71, 0x4d, 0xde, 0xdd, 0x56, 0x8e, 0x85, 0x48, 0xf9, - 0x02, 0xd4, 0xa7, 0xd8, 0x57, 0x03, 0x2d, 0x7e, 0xc8, 0x92, 0x0d, 0xa3, 0x64, 0xaf, 0xf9, 0xb6, - 0x5d, 0x71, 0x59, 0x2e, 0xc2, 0x94, 0xaf, 0xf5, 0x53, 0xfd, 0x14, 0xdc, 0xce, 0x98, 0x31, 0x91, - 0xfd, 0xd3, 0x20, 0x2c, 0xd2, 0xfa, 0x40, 0xc9, 0xd6, 0xe5, 0x12, 0x00, 0x3d, 0x6e, 0xa0, 0x51, - 0xe7, 0xdc, 0x8a, 0xcd, 0xb0, 0xbe, 0x5e, 0x68, 0x52, 0xf3, 0x91, 0xf3, 0x80, 0x10, 0xfb, 0xf3, - 0x27, 0x96, 0x87, 0x6d, 0x8b, 0x7a, 0x18, 0xb1, 0x61, 0x96, 0xed, 0x88, 0x25, 0x24, 0x37, 0x34, - 0x21, 0x97, 0x58, 0x42, 0x94, 0x28, 0x21, 0xbe, 0xc3, 0xa6, 0x2b, 0x9b, 0x7d, 0xe7, 0x15, 0x36, - 0x01, 0xef, 0x83, 0x72, 0x1a, 0x32, 0x11, 0xf6, 0x65, 0x70, 0x1c, 0x6f, 0x59, 0x1e, 0x46, 0x2d, - 0xce, 0x58, 0x5a, 0x90, 0xca, 0xf9, 0xf9, 0x49, 0x7d, 0x36, 0x9c, 0x5e, 0x09, 0x88, 0x4b, 0xe1, - 0x0f, 0x79, 0x70, 0x27, 0x30, 0x66, 0x87, 0x75, 0xdc, 0xb4, 0x4c, 0xd7, 0xf0, 0xf0, 0xc3, 0x27, - 0x86, 0x8b, 0xe9, 0x2a, 0x11, 0xc9, 0x5e, 0x26, 0x4e, 0x07, 0x3b, 0x1e, 0x5b, 0x43, 0x51, 0xe2, - 0x33, 0xa6, 0x21, 0xde, 0xc7, 0xf2, 0xf1, 0x34, 0xf0, 0x05, 0x28, 0x7a, 0x9b, 0x09, 0xe6, 0x68, - 0x00, 0xa0, 0xe5, 0x91, 0x56, 0x37, 0x44, 0x34, 0xbc, 0xd1, 0x95, 0x79, 0xa3, 0x2b, 0x70, 0x04, - 0x7b, 0x2d, 0x40, 0xfd, 0x38, 0xe5, 0x61, 0xf1, 0x28, 0xe5, 0xe7, 0x12, 0x38, 0xe6, 0x91, 0x75, - 0xec, 0xb4, 0x88, 0xef, 0xb5, 0xba, 0x8c, 0x35, 0x93, 0xc3, 0x58, 0xd3, 0xe0, 0x6e, 0x4e, 0x85, - 0x6e, 0x06, 0xd5, 0x61, 0x26, 0x3a, 0x1d, 0x0d, 0x94, 0x3f, 0xf3, 0xbd, 0xa6, 0xe5, 0xd0, 0x45, - 0x85, 0x6d, 0x7e, 0xb1, 0xbf, 0xf9, 0xa2, 0xf9, 0x44, 0xf8, 0x7f, 0xcd, 0x83, 0xbb, 0xa3, 0xee, - 0x95, 0x28, 0x8c, 0xc7, 0x60, 0xda, 0xe8, 0x12, 0xdf, 0xf1, 0x16, 0xf8, 0xa6, 0xdd, 0x65, 0xf1, - 0xfc, 0xbe, 0xad, 0x5c, 0x3e, 0x00, 0xec, 0x86, 0xe3, 0xf5, 0xb7, 0x8d, 0x9b, 0x81, 0x7a, 0x64, - 0xb0, 0x6f, 0xbb, 0x1a, 0x6c, 0xf2, 0x7b, 0xdb, 0xae, 0x0a, 0xdb, 0x55, 0x79, 0x13, 0xcc, 0xd9, - 0xd6, 0x53, 0xdf, 0x42, 0x96, 0xf7, 0xac, 0xd5, 0x09, 0x3a, 0x01, 0x0a, 0x9b, 0x4f, 0xed, 0x7e, - 0x06, 0x2f, 0x75, 0xdc, 0xe9, 0x97, 0xc8, 0x3e, 0x83, 0x50, 0x3f, 0x21, 0xe6, 0xc2, 0x6e, 0x83, - 0xe4, 0x47, 0xe0, 0xc8, 0x17, 0xc4, 0x72, 0x5a, 0xec, 0x76, 0x18, 0xf4, 0xb4, 0x99, 0x1b, 0x45, - 0x35, 0xbc, 0x3a, 0xaa, 0xd1, 0xd5, 0x51, 0x5d, 0x8d, 0xae, 0x8e, 0xb5, 0x73, 0xbc, 0x3c, 0x4e, - 0x84, 0x2e, 0x84, 0x2a, 0x7c, 0xf1, 0x46, 0x91, 0xf4, 0xc3, 0x6c, 0xcc, 0x84, 0xe1, 0x37, 0xf9, - 0xe0, 0x14, 0x58, 0x42, 0x68, 0x95, 0xc4, 0x37, 0x6c, 0x25, 0xf2, 0xdf, 0xef, 0x69, 0x82, 0x6f, - 0xb7, 0xc1, 0x4c, 0xd4, 0xa1, 0xc4, 0x19, 0x5c, 0x3b, 0xbd, 0xbb, 0xad, 0xc8, 0x51, 0x3f, 0x11, - 0x8b, 0x30, 0xd6, 0xcc, 0x50, 0x8c, 0xa8, 0xb9, 0x61, 0x44, 0x6d, 0x45, 0x8c, 0x40, 0x98, 0x5a, - 0x2e, 0x46, 0x0b, 0xc3, 0x89, 0x77, 0x3e, 0x89, 0x11, 0x91, 0x3a, 0xd4, 0x67, 0x83, 0x89, 0x3a, - 0x1f, 0xef, 0x73, 0x50, 0xe5, 0x49, 0x1d, 0xd1, 0x41, 0x75, 0x8f, 0x83, 0xea, 0xe2, 0x55, 0xc6, - 0xa3, 0x0f, 0x22, 0x1e, 0x19, 0x08, 0x55, 0x3c, 0x52, 0xe9, 0xd8, 0xf1, 0x33, 0x3c, 0x4a, 0x0d, - 0xfc, 0x25, 0x1f, 0x9c, 0x2c, 0x59, 0x76, 0x41, 0x30, 0x69, 0xe4, 0xdd, 0x88, 0x51, 0x30, 0xf7, - 0x2f, 0x52, 0x30, 0x3f, 0x6e, 0x0a, 0xae, 0x83, 0x59, 0x07, 0x6f, 0xb6, 0x04, 0x43, 0x0a, 0x87, - 0x02, 0x0f, 0xf7, 0x32, 0xd3, 0xef, 0x64, 0xe8, 0x61, 0xc0, 0x18, 0xd4, 0x8f, 0x3a, 0x78, 0x53, - 0xe4, 0x3d, 0x7e, 0x60, 0xec, 0xbb, 0x48, 0xec, 0x3d, 0x30, 0x6e, 0xfc, 0x0d, 0x40, 0xbe, 0x49, - 0x4d, 0xd9, 0x05, 0x72, 0xd2, 0xe5, 0x49, 0xdd, 0xff, 0xcc, 0x54, 0x13, 0x5f, 0x46, 0xc5, 0xea, - 0x81, 0x45, 0x45, 0x19, 0x6c, 0x81, 0x93, 0x89, 0x0f, 0xa8, 0x6b, 0x43, 0x4d, 0xf5, 0x85, 0x8b, - 0x37, 0x33, 0x08, 0xa7, 0x79, 0x16, 0xcf, 0x8b, 0x83, 0x78, 0x8e, 0x84, 0x0f, 0xe4, 0x79, 0xdf, - 0x43, 0xe0, 0x7b, 0x09, 0x5c, 0x18, 0xfe, 0xcc, 0xb9, 0x93, 0x21, 0xa8, 0x01, 0xcd, 0xe2, 0xdd, - 0x51, 0x35, 0x05, 0xc2, 0x6f, 0x25, 0x70, 0x36, 0xfd, 0x39, 0xb2, 0x90, 0x62, 0x3f, 0x55, 0xa3, - 0x78, 0x27, 0xab, 0x86, 0x40, 0xf2, 0xb3, 0x04, 0xae, 0x67, 0xba, 0xeb, 0x2f, 0xa7, 0xb8, 0xca, - 0x62, 0xa4, 0xf8, 0xe9, 0x18, 0x8c, 0x88, 0x10, 0xbe, 0x04, 0xa7, 0x92, 0xef, 0xc1, 0xd7, 0x53, - 0xbc, 0x24, 0x4a, 0x17, 0x3f, 0xca, 0x22, 0x2d, 0x9c, 0xff, 0x21, 0x81, 0x8f, 0x47, 0xbb, 0x9e, - 0xae, 0xa4, 0xfa, 0x1b, 0xc1, 0x5a, 0x71, 0x75, 0x9c, 0xd6, 0x06, 0xaa, 0x23, 0xd3, 0x1d, 0x20, - 0xad, 0x3a, 0xb2, 0x18, 0x49, 0xad, 0x8e, 0x51, 0xce, 0xc1, 0xda, 0x83, 0x57, 0x6f, 0x4b, 0xd2, - 0xeb, 0xb7, 0x25, 0xe9, 0xcf, 0xb7, 0x25, 0xe9, 0xc5, 0x4e, 0x69, 0xe2, 0xf5, 0x4e, 0x69, 0xe2, - 0xb7, 0x9d, 0xd2, 0xc4, 0xe3, 0x5b, 0xb1, 0x13, 0x81, 0x3b, 0xac, 0xd8, 0x46, 0x9b, 0x46, 0x03, - 0x6d, 0xa3, 0x7a, 0x5b, 0xdb, 0x1a, 0xf8, 0x65, 0x90, 0x9d, 0x12, 0xed, 0xa9, 0xe0, 0x5e, 0x75, - 0xf3, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd3, 0x81, 0x35, 0xa1, 0x3c, 0x14, 0x00, 0x00, + // 1510 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xcd, 0x6f, 0x13, 0x47, + 0x1b, 0xcf, 0xda, 0x21, 0x81, 0x09, 0x09, 0xc9, 0xbe, 0x7c, 0x18, 0xbf, 0x60, 0x9b, 0xe1, 0x2b, + 0x7c, 0xd8, 0x1b, 0xc3, 0xfb, 0x02, 0xca, 0x89, 0x38, 0x16, 0xaf, 0x02, 0xb1, 0x5e, 0xb4, 0x04, + 0x55, 0xe2, 0x62, 0xad, 0x3d, 0x93, 0x65, 0x9b, 0xdd, 0x1d, 0xb3, 0x33, 0x4e, 0x82, 0x7a, 0x6a, + 0x7b, 0xa8, 0xc4, 0x09, 0xf5, 0xd2, 0xde, 0x7a, 0x6e, 0x0f, 0x15, 0xff, 0x40, 0xa5, 0x1e, 0x7a, + 0x40, 0x3d, 0x71, 0xac, 0x5a, 0x29, 0x54, 0x70, 0xe8, 0x3d, 0x97, 0x5e, 0xab, 0xd9, 0x9d, 0x1d, + 0xaf, 0x93, 0xdd, 0x38, 0x6b, 0xd2, 0x43, 0x2f, 0xe0, 0x9d, 0x79, 0xe6, 0x79, 0x7e, 0xcf, 0xc7, + 0xef, 0x79, 0x66, 0x02, 0xfe, 0x4d, 0xa8, 0x43, 0xa8, 0x45, 0x35, 0xda, 0xed, 0x60, 0x6f, 0xd5, + 0xee, 0x5a, 0x48, 0x63, 0x9b, 0x95, 0x8e, 0x47, 0x18, 0x51, 0x55, 0xb1, 0x59, 0xe9, 0x6d, 0xe6, + 0x8f, 0x9b, 0xc4, 0x24, 0xfe, 0xb6, 0xc6, 0x7f, 0x05, 0x92, 0xf9, 0x19, 0xc3, 0xb1, 0x5c, 0xa2, + 0xf9, 0xff, 0x8a, 0xa5, 0x82, 0x49, 0x88, 0x69, 0x63, 0xcd, 0xff, 0x6a, 0x75, 0x57, 0x35, 0xd4, + 0xf5, 0x0c, 0x66, 0x11, 0x37, 0xdc, 0x6f, 0xfb, 0xda, 0xb5, 0x96, 0x41, 0xb1, 0xb6, 0x5e, 0x6d, + 0x61, 0x66, 0x54, 0xb5, 0x36, 0xb1, 0xc2, 0xfd, 0xe2, 0xce, 0xf3, 0xcc, 0x72, 0x30, 0x65, 0x86, + 0xd3, 0x11, 0x02, 0xe7, 0x63, 0xa0, 0xf7, 0x7e, 0x06, 0x42, 0xf0, 0x6b, 0x05, 0x9c, 0x68, 0x50, + 0xf3, 0x91, 0x5c, 0xaf, 0x63, 0x1b, 0x9b, 0x06, 0xc3, 0xea, 0x15, 0x30, 0x46, 0xb1, 0x8b, 0xb0, + 0x97, 0x53, 0x4a, 0xca, 0xec, 0x91, 0xda, 0xcc, 0xf6, 0x56, 0x71, 0xf2, 0xb9, 0xe1, 0xd8, 0xf3, + 0x30, 0x58, 0x87, 0xba, 0x10, 0x50, 0x4f, 0x81, 0x71, 0x9b, 0xb4, 0xd7, 0x9a, 0x16, 0xca, 0x65, + 0x4a, 0xca, 0xec, 0xa8, 0x3e, 0xc6, 0x3f, 0x97, 0x90, 0x7a, 0x1a, 0x1c, 0x5e, 0x37, 0xec, 0xa6, + 0x81, 0x90, 0x97, 0xcb, 0x72, 0x2d, 0xfa, 0xf8, 0xba, 0x61, 0x2f, 0x20, 0xe4, 0xcd, 0x97, 0x5e, + 0xfc, 0xf1, 0xea, 0x6a, 0x4c, 0x74, 0xcb, 0x48, 0x00, 0x80, 0x45, 0x70, 0x36, 0x16, 0x99, 0x8e, + 0x69, 0x87, 0xb8, 0x14, 0xc3, 0x4f, 0x15, 0x70, 0xaa, 0x4f, 0xe2, 0xb1, 0x8b, 0x0e, 0x10, 0xfd, + 0x3c, 0xe4, 0x10, 0xcf, 0xc6, 0x40, 0xec, 0x4a, 0x3b, 0xf0, 0x1c, 0x28, 0x26, 0x40, 0x90, 0x30, + 0x3f, 0xdb, 0x0d, 0xb3, 0x45, 0x5c, 0xb4, 0x4c, 0xda, 0x6b, 0x07, 0x02, 0xf3, 0x3c, 0x87, 0x59, + 0x88, 0x85, 0xc9, 0xed, 0x94, 0xb9, 0x58, 0x0c, 0xce, 0x10, 0x83, 0xc4, 0xf9, 0xbd, 0x02, 0x2e, + 0x24, 0xf8, 0xb2, 0xe0, 0x1e, 0x30, 0x68, 0xb5, 0x06, 0x46, 0x79, 0x2d, 0xfb, 0x55, 0x31, 0x71, + 0xe3, 0x74, 0x25, 0x28, 0xf6, 0x0a, 0x2f, 0xf6, 0x8a, 0x28, 0xf6, 0xca, 0x22, 0xb1, 0xdc, 0xda, + 0xbf, 0x5e, 0x6f, 0x15, 0x47, 0xb6, 0xb7, 0x8a, 0x13, 0x81, 0x01, 0x7e, 0x08, 0xea, 0xfe, 0x59, + 0xf8, 0x3f, 0x70, 0x7d, 0x3f, 0x78, 0x43, 0x07, 0xa3, 0x60, 0x94, 0x28, 0x18, 0xb8, 0xad, 0x80, + 0x33, 0x0d, 0x6a, 0x72, 0xe1, 0x05, 0x17, 0x7d, 0x18, 0x17, 0x0c, 0x70, 0x88, 0x83, 0xa3, 0xb9, + 0x4c, 0x29, 0xbb, 0xb7, 0x67, 0x73, 0xdc, 0xb3, 0xef, 0xde, 0x16, 0x67, 0x4d, 0x8b, 0x3d, 0xed, + 0xb6, 0x2a, 0x6d, 0xe2, 0x68, 0x82, 0xf3, 0xc1, 0x7f, 0x65, 0x8a, 0xd6, 0x34, 0xf6, 0xbc, 0x83, + 0xa9, 0x7f, 0x80, 0xea, 0x81, 0xe6, 0xbd, 0x58, 0x75, 0x85, 0xd7, 0xc2, 0x85, 0xb0, 0x16, 0xb8, + 0x7b, 0x65, 0xc3, 0x45, 0xe5, 0x38, 0x7a, 0xdd, 0xf2, 0xb3, 0x9d, 0xe8, 0xb3, 0x8c, 0xda, 0x14, + 0xc8, 0x2c, 0xd5, 0x45, 0xc0, 0x32, 0x4b, 0x75, 0xf8, 0x2a, 0x03, 0xb4, 0x06, 0x35, 0x17, 0x3d, + 0x6c, 0x30, 0x7c, 0xaf, 0x6b, 0xdb, 0xba, 0xe1, 0x9a, 0xf8, 0x21, 0xa1, 0x16, 0x6f, 0x5e, 0xff, + 0xec, 0xf8, 0xa9, 0xd7, 0xc0, 0x78, 0x87, 0x10, 0x9b, 0x97, 0xc8, 0x28, 0xf7, 0xb8, 0xa6, 0x6e, + 0x6f, 0x15, 0xa7, 0x02, 0xa4, 0x62, 0x03, 0xea, 0x63, 0xfc, 0xd7, 0x12, 0x9a, 0xbf, 0xcc, 0x83, + 0x0d, 0xc3, 0x60, 0xaf, 0x76, 0x6d, 0xbb, 0xec, 0xf1, 0x58, 0x04, 0x21, 0x5f, 0xed, 0x85, 0xfa, + 0x19, 0xb8, 0x9d, 0x32, 0x62, 0x32, 0xfa, 0x27, 0x41, 0x50, 0xa4, 0xf5, 0xbe, 0x92, 0xad, 0xab, + 0x05, 0x00, 0x3a, 0x42, 0xc1, 0x52, 0x5d, 0x70, 0x2b, 0xb2, 0xc2, 0xfb, 0x7a, 0xae, 0x41, 0xcd, + 0xc7, 0xee, 0x43, 0x42, 0xec, 0x8f, 0x9e, 0x5a, 0x0c, 0xdb, 0x16, 0x65, 0x18, 0xf1, 0xcf, 0x34, + 0xe9, 0x88, 0x04, 0x24, 0x33, 0x30, 0x20, 0x17, 0x78, 0x40, 0x8a, 0x61, 0x40, 0xba, 0x2e, 0x5f, + 0x2e, 0x6f, 0xf4, 0x8c, 0x97, 0xf9, 0x02, 0xbc, 0x0f, 0x4a, 0x49, 0xc8, 0xa4, 0xdb, 0x97, 0xc0, + 0x31, 0xbc, 0x69, 0x31, 0x8c, 0x9a, 0x82, 0xb1, 0x34, 0xa7, 0x94, 0xb2, 0xb3, 0xa3, 0xfa, 0x64, + 0xb0, 0xbc, 0xec, 0x13, 0x97, 0xc2, 0x6f, 0xb3, 0xe0, 0x8e, 0xaf, 0xcc, 0x0e, 0xea, 0xb8, 0x61, + 0x99, 0x9e, 0xc1, 0xf0, 0xa3, 0xa7, 0x86, 0x87, 0xe9, 0x0a, 0x91, 0xc1, 0x5e, 0x24, 0x6e, 0x1b, + 0xbb, 0x8c, 0xef, 0xa1, 0x30, 0xf0, 0x29, 0xc3, 0x10, 0xed, 0x63, 0xd9, 0x68, 0x18, 0xc4, 0x06, + 0x94, 0xbd, 0xcd, 0x04, 0x33, 0xd4, 0x07, 0xd0, 0x64, 0xa4, 0xe9, 0x04, 0x88, 0x06, 0x37, 0xba, + 0x92, 0x68, 0x74, 0x39, 0x81, 0x60, 0xa7, 0x06, 0xa8, 0x1f, 0xa3, 0xc2, 0x2d, 0xe1, 0xa5, 0xfa, + 0x42, 0x01, 0x53, 0x8c, 0xac, 0x61, 0xb7, 0x49, 0xba, 0xac, 0xe9, 0x70, 0xd6, 0x8c, 0x0e, 0x62, + 0xcd, 0x92, 0x30, 0x73, 0x22, 0x30, 0xd3, 0x7f, 0x1c, 0xa6, 0xa2, 0xd3, 0x51, 0xff, 0xf0, 0xff, + 0xbb, 0xac, 0x61, 0xb9, 0x74, 0xbe, 0xc8, 0x93, 0x9f, 0xef, 0x25, 0x5f, 0x36, 0x9f, 0x10, 0xff, + 0xcf, 0x59, 0x70, 0x77, 0xd8, 0x5c, 0xc9, 0xc2, 0x78, 0x02, 0xc6, 0x0d, 0x87, 0x74, 0x5d, 0x36, + 0x27, 0x92, 0x76, 0x97, 0xfb, 0xf3, 0xeb, 0x56, 0xf1, 0xd2, 0x3e, 0x60, 0x2f, 0xb9, 0xac, 0x97, + 0x36, 0xa1, 0x06, 0xea, 0xa1, 0xc2, 0x9e, 0xee, 0xaa, 0x9f, 0xe4, 0x0f, 0xd6, 0x5d, 0x95, 0xba, + 0xab, 0xea, 0x06, 0x98, 0xb1, 0xad, 0x67, 0x5d, 0x0b, 0x59, 0xec, 0x79, 0xb3, 0xed, 0x77, 0x02, + 0x14, 0x34, 0x9f, 0xda, 0xfd, 0x14, 0x56, 0xea, 0xb8, 0xdd, 0x2b, 0x91, 0x5d, 0x0a, 0xa1, 0x3e, + 0x2d, 0xd7, 0x82, 0x6e, 0x83, 0xd4, 0xc7, 0xe0, 0xc8, 0xc7, 0xc4, 0x72, 0x9b, 0xfc, 0x76, 0xe8, + 0xf7, 0xb4, 0x89, 0x1b, 0xf9, 0x4a, 0x70, 0x75, 0xac, 0x84, 0x57, 0xc7, 0xca, 0x4a, 0x78, 0x75, + 0xac, 0x9d, 0x11, 0xe5, 0x31, 0x1d, 0x98, 0x90, 0x47, 0xe1, 0xcb, 0xb7, 0x45, 0x45, 0x3f, 0xcc, + 0xbf, 0xb9, 0x30, 0xfc, 0x3c, 0xeb, 0x4f, 0x81, 0x05, 0x84, 0x56, 0x48, 0x34, 0x61, 0xcb, 0xa1, + 0xfd, 0x5e, 0x4f, 0x93, 0x7c, 0xbb, 0x0d, 0x26, 0xc2, 0x0e, 0x25, 0x67, 0x70, 0xed, 0xe4, 0xf6, + 0x56, 0x51, 0x0d, 0xfb, 0x89, 0xdc, 0x84, 0x91, 0x66, 0x86, 0x22, 0x44, 0xcd, 0x0c, 0x22, 0x6a, + 0x33, 0x64, 0x04, 0xc2, 0xd4, 0xf2, 0x30, 0x9a, 0x1b, 0x4c, 0xbc, 0xb3, 0x71, 0x8c, 0x08, 0x8f, + 0x43, 0x7d, 0xd2, 0x5f, 0xa8, 0x8b, 0xef, 0x5d, 0x06, 0xaa, 0x22, 0xa8, 0x43, 0x1a, 0xa8, 0xee, + 0x30, 0x50, 0x9d, 0xbf, 0xca, 0x79, 0x74, 0x31, 0xe4, 0x91, 0x81, 0x50, 0x99, 0x91, 0x72, 0xdb, + 0x8e, 0xce, 0xf0, 0x30, 0x34, 0xf0, 0xa7, 0xac, 0x3f, 0x59, 0xd2, 0x64, 0x41, 0x32, 0x69, 0xe8, + 0x6c, 0x44, 0x28, 0x98, 0xf9, 0x1b, 0x29, 0x98, 0x3d, 0x68, 0x0a, 0xae, 0x81, 0x49, 0x17, 0x6f, + 0x34, 0x25, 0x43, 0x72, 0x87, 0x7c, 0x0b, 0xf7, 0x52, 0xd3, 0xef, 0x78, 0x60, 0xa1, 0x4f, 0x19, + 0xd4, 0x8f, 0xba, 0x78, 0x43, 0xc6, 0x3d, 0x3a, 0x30, 0x76, 0x5d, 0x24, 0x76, 0x0e, 0x0c, 0xf8, + 0x43, 0x56, 0x0c, 0x6b, 0x7e, 0x65, 0x5d, 0x24, 0xee, 0x3a, 0xf6, 0x18, 0xbf, 0x16, 0x30, 0x63, + 0x0d, 0x47, 0x35, 0x29, 0x83, 0x34, 0xa5, 0x61, 0xca, 0x1e, 0xb7, 0x20, 0x0f, 0x4c, 0x3b, 0x96, + 0xdb, 0x34, 0x1c, 0xc6, 0xe7, 0x0f, 0xe5, 0x30, 0x7c, 0x2f, 0x8e, 0x04, 0xd3, 0x23, 0x55, 0x3a, + 0x4e, 0x05, 0xd6, 0x77, 0xea, 0x83, 0xfa, 0xa4, 0x63, 0xb9, 0x0b, 0x0e, 0x5b, 0x21, 0x81, 0x9b, + 0x5f, 0x2a, 0xd1, 0xa9, 0xd9, 0x0e, 0x82, 0xe0, 0xa7, 0x68, 0x4f, 0x6e, 0x3d, 0x48, 0x9a, 0x9a, + 0x42, 0x03, 0x9f, 0x68, 0x97, 0xf7, 0x39, 0xd1, 0x7a, 0x03, 0x56, 0xe4, 0x60, 0xfe, 0x22, 0xe7, + 0x62, 0xa9, 0x37, 0xd3, 0xfc, 0xf7, 0x94, 0xd0, 0x1c, 0xdc, 0xf2, 0x7c, 0x5f, 0xbe, 0x52, 0xc4, + 0x95, 0x26, 0x26, 0x7f, 0x92, 0x6f, 0x14, 0x4c, 0x33, 0xc2, 0x78, 0xc4, 0x1d, 0x16, 0xc4, 0x00, + 0x89, 0x11, 0x36, 0x74, 0x50, 0x77, 0xea, 0x83, 0xfa, 0x94, 0xbf, 0xb4, 0xe0, 0x30, 0xdf, 0x36, + 0xba, 0xf1, 0xe7, 0x04, 0xc8, 0x36, 0xa8, 0xa9, 0x7a, 0x40, 0x8d, 0xbb, 0x96, 0x57, 0x76, 0xff, + 0x01, 0xa3, 0x12, 0xfb, 0xe6, 0xce, 0x57, 0xf7, 0x2d, 0x2a, 0x1d, 0xde, 0x04, 0xc7, 0x63, 0x9f, + 0xe6, 0xd7, 0x06, 0xaa, 0xea, 0x09, 0xe7, 0x6f, 0xa6, 0x10, 0x4e, 0xb2, 0x2c, 0x1f, 0xae, 0xfb, + 0xb1, 0x1c, 0x0a, 0xef, 0xcb, 0xf2, 0xae, 0x27, 0xe6, 0x37, 0x0a, 0x38, 0x37, 0xf8, 0x01, 0x7d, + 0x27, 0x85, 0x53, 0x7d, 0x27, 0xf3, 0x77, 0x87, 0x3d, 0x29, 0x11, 0x7e, 0xa1, 0x80, 0xd3, 0xc9, + 0x0f, 0xdd, 0xb9, 0x04, 0xfd, 0x89, 0x27, 0xf2, 0x77, 0xd2, 0x9e, 0x90, 0x48, 0x7e, 0x54, 0xc0, + 0xf5, 0x54, 0xaf, 0xc8, 0xc5, 0x04, 0x53, 0x69, 0x94, 0xe4, 0x1f, 0x1c, 0x80, 0x12, 0xe9, 0xc2, + 0x27, 0xe0, 0x44, 0xfc, 0x0b, 0xeb, 0x7a, 0x82, 0x95, 0x58, 0xe9, 0xfc, 0x7f, 0xd2, 0x48, 0x4b, + 0xe3, 0xbf, 0x29, 0xe0, 0xbf, 0xc3, 0x3d, 0x7c, 0x96, 0x13, 0xed, 0x0d, 0xa1, 0x2d, 0xbf, 0x72, + 0x90, 0xda, 0xfa, 0xaa, 0x23, 0xd5, 0xed, 0x32, 0xa9, 0x3a, 0xd2, 0x28, 0x49, 0xac, 0x8e, 0xa1, + 0x6e, 0x58, 0x7e, 0x75, 0xc4, 0x8d, 0xf4, 0xe4, 0xea, 0x88, 0x91, 0xde, 0xa3, 0x3a, 0xf6, 0x18, + 0x37, 0xb5, 0x87, 0xaf, 0xdf, 0x15, 0x94, 0x37, 0xef, 0x0a, 0xca, 0xef, 0xef, 0x0a, 0xca, 0xcb, + 0xf7, 0x85, 0x91, 0x37, 0xef, 0x0b, 0x23, 0xbf, 0xbc, 0x2f, 0x8c, 0x3c, 0xb9, 0x15, 0x19, 0x33, + 0x42, 0x73, 0xd9, 0x36, 0x5a, 0x34, 0xfc, 0xd0, 0xd6, 0xab, 0xb7, 0xb5, 0xcd, 0xbe, 0x3f, 0x78, + 0xf3, 0xd1, 0xd3, 0x1a, 0xf3, 0x9f, 0x0b, 0x37, 0xff, 0x0a, 0x00, 0x00, 0xff, 0xff, 0xca, 0x88, + 0xcf, 0x73, 0x13, 0x17, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1159,6 +1288,9 @@ type MsgClient interface { UnPoolWhitelistedPool(ctx context.Context, in *MsgUnPoolWhitelistedPool, opts ...grpc.CallOption) (*MsgUnPoolWhitelistedPoolResponse, error) UnlockAndMigrateSharesToFullRangeConcentratedPosition(ctx context.Context, in *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition, opts ...grpc.CallOption) (*MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse, error) AddToConcentratedLiquiditySuperfluidPosition(ctx context.Context, in *MsgAddToConcentratedLiquiditySuperfluidPosition, opts ...grpc.CallOption) (*MsgAddToConcentratedLiquiditySuperfluidPositionResponse, error) + // UnbondConvertAndStake breaks all locks / superfluid staked assets, + // converts them to osmo then stakes the osmo to the designated validator. + UnbondConvertAndStake(ctx context.Context, in *MsgUnbondConvertAndStake, opts ...grpc.CallOption) (*MsgUnbondConvertAndStakeResponse, error) } type msgClient struct { @@ -1250,6 +1382,15 @@ func (c *msgClient) AddToConcentratedLiquiditySuperfluidPosition(ctx context.Con return out, nil } +func (c *msgClient) UnbondConvertAndStake(ctx context.Context, in *MsgUnbondConvertAndStake, opts ...grpc.CallOption) (*MsgUnbondConvertAndStakeResponse, error) { + out := new(MsgUnbondConvertAndStakeResponse) + err := c.cc.Invoke(ctx, "/osmosis.superfluid.Msg/UnbondConvertAndStake", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { // Execute superfluid delegation for a lockup @@ -1267,6 +1408,9 @@ type MsgServer interface { UnPoolWhitelistedPool(context.Context, *MsgUnPoolWhitelistedPool) (*MsgUnPoolWhitelistedPoolResponse, error) UnlockAndMigrateSharesToFullRangeConcentratedPosition(context.Context, *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) (*MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse, error) AddToConcentratedLiquiditySuperfluidPosition(context.Context, *MsgAddToConcentratedLiquiditySuperfluidPosition) (*MsgAddToConcentratedLiquiditySuperfluidPositionResponse, error) + // UnbondConvertAndStake breaks all locks / superfluid staked assets, + // converts them to osmo then stakes the osmo to the designated validator. + UnbondConvertAndStake(context.Context, *MsgUnbondConvertAndStake) (*MsgUnbondConvertAndStakeResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -1300,6 +1444,9 @@ func (*UnimplementedMsgServer) UnlockAndMigrateSharesToFullRangeConcentratedPosi func (*UnimplementedMsgServer) AddToConcentratedLiquiditySuperfluidPosition(ctx context.Context, req *MsgAddToConcentratedLiquiditySuperfluidPosition) (*MsgAddToConcentratedLiquiditySuperfluidPositionResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddToConcentratedLiquiditySuperfluidPosition not implemented") } +func (*UnimplementedMsgServer) UnbondConvertAndStake(ctx context.Context, req *MsgUnbondConvertAndStake) (*MsgUnbondConvertAndStakeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UnbondConvertAndStake not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -1467,6 +1614,24 @@ func _Msg_AddToConcentratedLiquiditySuperfluidPosition_Handler(srv interface{}, return interceptor(ctx, in, info, handler) } +func _Msg_UnbondConvertAndStake_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUnbondConvertAndStake) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UnbondConvertAndStake(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.superfluid.Msg/UnbondConvertAndStake", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UnbondConvertAndStake(ctx, req.(*MsgUnbondConvertAndStake)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "osmosis.superfluid.Msg", HandlerType: (*MsgServer)(nil), @@ -1507,6 +1672,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "AddToConcentratedLiquiditySuperfluidPosition", Handler: _Msg_AddToConcentratedLiquiditySuperfluidPosition_Handler, }, + { + MethodName: "UnbondConvertAndStake", + Handler: _Msg_UnbondConvertAndStake_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "osmosis/superfluid/tx.proto", @@ -2248,6 +2417,101 @@ func (m *MsgAddToConcentratedLiquiditySuperfluidPositionResponse) MarshalToSized return len(dAtA) - i, nil } +func (m *MsgUnbondConvertAndStake) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUnbondConvertAndStake) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUnbondConvertAndStake) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.SharesToConvert.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + { + size := m.MinAmtToStake.Size() + i -= size + if _, err := m.MinAmtToStake.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.ValAddr) > 0 { + i -= len(m.ValAddr) + copy(dAtA[i:], m.ValAddr) + i = encodeVarintTx(dAtA, i, uint64(len(m.ValAddr))) + i-- + dAtA[i] = 0x1a + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0x12 + } + if m.LockId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.LockId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MsgUnbondConvertAndStakeResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUnbondConvertAndStakeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUnbondConvertAndStakeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TotalAmtStaked.Size() + i -= size + if _, err := m.TotalAmtStaked.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -2558,6 +2822,41 @@ func (m *MsgAddToConcentratedLiquiditySuperfluidPositionResponse) Size() (n int) return n } +func (m *MsgUnbondConvertAndStake) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.LockId != 0 { + n += 1 + sovTx(uint64(m.LockId)) + } + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ValAddr) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.MinAmtToStake.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.SharesToConvert.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgUnbondConvertAndStakeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.TotalAmtStaked.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -4661,6 +4960,290 @@ func (m *MsgAddToConcentratedLiquiditySuperfluidPositionResponse) Unmarshal(dAtA } return nil } +func (m *MsgUnbondConvertAndStake) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUnbondConvertAndStake: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUnbondConvertAndStake: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LockId", wireType) + } + m.LockId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LockId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValAddr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MinAmtToStake", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MinAmtToStake.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SharesToConvert", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.SharesToConvert.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUnbondConvertAndStakeResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUnbondConvertAndStakeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUnbondConvertAndStakeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalAmtStaked", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TotalAmtStaked.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTx(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/tokenfactory/README.md b/x/tokenfactory/README.md index 4d011d21c71..1419d6d6456 100644 --- a/x/tokenfactory/README.md +++ b/x/tokenfactory/README.md @@ -17,6 +17,42 @@ created denom. Once a denom is created, the original creator is given account, or even setting it to `""`, meaning no account has admin privileges of the asset. +## Bank hooks +Token factory supports better integration with contracts using bank hooks. + +Token factory is integrated with Before Send bank hooks, `TrackBeforeSend` and `BlockBeforeSend`. Both hooks gets called whenever a bank send takes place, the difference between two hooks is that `TrackBeforeSend` would not error and `BlockBeforeSend` errors. Due to this difference `TrackBeforeSend` is useful for cases when a contract needs to track specific send actions of the token factory denom, whilst `BlockBeforeSend` would be more useful for situations when we want to block specific sends using contracts. + +Each Token Factory denom allows the registration of one contract address. This contract is sudo-called every time the aforementioned bank hooks are activated. + +Contracts are able to integrate with these hooks by implementing `BlockBeforeSend` and `TrackBeforeSend` message as the following example: + +```rust +#[entry_point] +pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> StdResult { + match &msg{ + SudoMsg::BlockBeforeSend { from, to, amount} => { + Ok(Response::new().add_attributes(vec![ + ("hook", "block"), + ("from", from), + ("to", to), + ("amount", &amount.to_string()) + ])) + }, + SudoMsg::TrackBeforeSend { from, to, amount} => { + Ok(Response::new().add_attributes(vec![ + ("hook", "track"), + ("from", from), + ("to", to), + ("amount", &amount.to_string()) + ])) + } + } +} +``` + + +Note that since `TrackBeforeSend` hook can also be triggered upon module to module send (which is not gas metered), we internally gas meter `TrackBeforeSend` with a gas limit of 100_000. + ## Messages ### CreateDenom diff --git a/x/tokenfactory/client/cli/tx.go b/x/tokenfactory/client/cli/tx.go index 2e398ef455e..05857403662 100644 --- a/x/tokenfactory/client/cli/tx.go +++ b/x/tokenfactory/client/cli/tx.go @@ -35,14 +35,14 @@ func NewCreateDenomCmd() *cobra.Command { func NewMintCmd() *cobra.Command { return osmocli.BuildTxCli[*types.MsgMint](&osmocli.TxCliDesc{ - Use: "mint [amount] [flags]", + Use: "mint [amount] [mint-to-address] [flags]", Short: "Mint a denom to an address. Must have admin authority to do so.", }) } func NewBurnCmd() *cobra.Command { return osmocli.BuildTxCli[*types.MsgBurn](&osmocli.TxCliDesc{ - Use: "burn [amount] [flags]", + Use: "burn [amount] [burn-from-address] [flags]", Short: "Burn tokens from an address. Must have admin authority to do so.", }) } diff --git a/x/tokenfactory/keeper/admins_test.go b/x/tokenfactory/keeper/admins_test.go index 30582a094e2..043e385f5b6 100644 --- a/x/tokenfactory/keeper/admins_test.go +++ b/x/tokenfactory/keeper/admins_test.go @@ -129,6 +129,15 @@ func (s *KeeperTestSuite) TestMintDenom() { ), expectPass: true, }, + { + desc: "error: try minting non-tokenfactory denom", + mintMsg: *types.NewMsgMintTo( + s.TestAccs[0].String(), + sdk.NewInt64Coin("uosmo", 10), + s.TestAccs[1].String(), + ), + expectPass: false, + }, } { s.Run(fmt.Sprintf("Case %s", tc.desc), func() { _, err := s.msgServer.Mint(sdk.WrapSDKContext(s.Ctx), &tc.mintMsg) @@ -217,6 +226,15 @@ func (s *KeeperTestSuite) TestBurnDenom() { ), expectPass: false, }, + { + desc: "fail case - burn non-tokenfactory denom", + burnMsg: *types.NewMsgBurnFrom( + s.TestAccs[0].String(), + sdk.NewInt64Coin("uosmo", 10), + moduleAdress.String(), + ), + expectPass: false, + }, } { s.Run(fmt.Sprintf("Case %s", tc.desc), func() { _, err := s.msgServer.Burn(sdk.WrapSDKContext(s.Ctx), &tc.burnMsg) diff --git a/x/tokenfactory/keeper/createdenom.go b/x/tokenfactory/keeper/createdenom.go index 054dd059b9d..40dfe9a6707 100644 --- a/x/tokenfactory/keeper/createdenom.go +++ b/x/tokenfactory/keeper/createdenom.go @@ -16,7 +16,7 @@ func (k Keeper) CreateDenom(ctx sdk.Context, creatorAddr string, subdenom string return "", err } - err = k.chargeForCreateDenom(ctx, creatorAddr, subdenom) + err = k.chargeForCreateDenom(ctx, creatorAddr) if err != nil { return "", err } @@ -73,7 +73,7 @@ func (k Keeper) validateCreateDenom(ctx sdk.Context, creatorAddr string, subdeno return denom, nil } -func (k Keeper) chargeForCreateDenom(ctx sdk.Context, creatorAddr string, subdenom string) (err error) { +func (k Keeper) chargeForCreateDenom(ctx sdk.Context, creatorAddr string) (err error) { params := k.GetParams(ctx) // if DenomCreationFee is non-zero, transfer the tokens from the creator diff --git a/x/tokenfactory/types/denoms.go b/x/tokenfactory/types/denoms.go index c705a9b95c5..658399856ef 100644 --- a/x/tokenfactory/types/denoms.go +++ b/x/tokenfactory/types/denoms.go @@ -1,12 +1,10 @@ package types import ( - fmt "fmt" "strings" errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" ) const ( @@ -68,17 +66,3 @@ func DeconstructDenom(denom string) (creator string, subdenom string, err error) return creatorAddr.String(), subdenom, nil } - -// NewTokenFactoryDenomMintCoinsRestriction creates and returns a BankMintingRestrictionFn that only allows minting of -// valid tokenfactory denoms -func NewTokenFactoryDenomMintCoinsRestriction() bankkeeper.BankMintingRestrictionFn { - return func(ctx sdk.Context, coinsToMint sdk.Coins) error { - for _, coin := range coinsToMint { - _, _, err := DeconstructDenom(coin.Denom) - if err != nil { - return fmt.Errorf("does not have permission to mint %s", coin.Denom) - } - } - return nil - } -} diff --git a/x/twap/export_test.go b/x/twap/export_test.go index f8de4fc362e..74375dda78f 100644 --- a/x/twap/export_test.go +++ b/x/twap/export_test.go @@ -24,18 +24,14 @@ func (k Keeper) GetRecordAtOrBeforeTime(ctx sdk.Context, poolId uint64, time tim return k.getRecordAtOrBeforeTime(ctx, poolId, time, asset0Denom, asset1Denom) } -func (k Keeper) GetAllHistoricalTimeIndexedTWAPs(ctx sdk.Context) ([]types.TwapRecord, error) { - return k.getAllHistoricalTimeIndexedTWAPs(ctx) +func (k Keeper) TrackChangedPool(ctx sdk.Context, poolId uint64) { + k.trackChangedPool(ctx, poolId) } func (k Keeper) GetAllHistoricalPoolIndexedTWAPs(ctx sdk.Context) ([]types.TwapRecord, error) { return k.getAllHistoricalPoolIndexedTWAPs(ctx) } -func (k Keeper) TrackChangedPool(ctx sdk.Context, poolId uint64) { - k.trackChangedPool(ctx, poolId) -} - func (k Keeper) GetChangedPools(ctx sdk.Context) []uint64 { return k.getChangedPools(ctx) } diff --git a/x/twap/keeper.go b/x/twap/keeper.go index 92daf2eff97..c59e4520ea1 100644 --- a/x/twap/keeper.go +++ b/x/twap/keeper.go @@ -73,7 +73,7 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { // These are ordered in increasing order, guaranteed by the iterator // that is prefixed by time. - twapRecords, err := k.getAllHistoricalTimeIndexedTWAPs(ctx) + twapRecords, err := k.GetAllHistoricalTimeIndexedTWAPs(ctx) if err != nil { panic(err) } diff --git a/x/twap/keeper_test.go b/x/twap/keeper_test.go index e7a0e73522b..a90c4fd9c54 100644 --- a/x/twap/keeper_test.go +++ b/x/twap/keeper_test.go @@ -383,7 +383,7 @@ func (s *TestSuite) createTestRecordsFromTime(t time.Time) (types.TwapRecord, ty // - 3 records at time t // - 3 records t time t + 1 seconds // all returned records belong to the same pool with poolId -func (s *TestSuite) createTestRecordsFromTimeInPool(t time.Time, poolId uint64) (types.TwapRecord, types.TwapRecord, types.TwapRecord, types.TwapRecord, types.TwapRecord, types.TwapRecord, +func (s *TestSuite) CreateTestRecordsFromTimeInPool(t time.Time, poolId uint64) (types.TwapRecord, types.TwapRecord, types.TwapRecord, types.TwapRecord, types.TwapRecord, types.TwapRecord, types.TwapRecord, types.TwapRecord, types.TwapRecord, types.TwapRecord, types.TwapRecord, types.TwapRecord, ) { baseRecordAB := newEmptyPriceRecord(poolId, t, denom0, denom1) diff --git a/x/twap/store.go b/x/twap/store.go index a363271d82d..b86872da12b 100644 --- a/x/twap/store.go +++ b/x/twap/store.go @@ -52,7 +52,7 @@ func (k Keeper) getChangedPools(ctx sdk.Context) []uint64 { } // storeHistoricalTWAP writes a twap to the store, in all needed indexing. -func (k Keeper) storeHistoricalTWAP(ctx sdk.Context, twap types.TwapRecord) { +func (k Keeper) StoreHistoricalTWAP(ctx sdk.Context, twap types.TwapRecord) { store := ctx.KVStore(k.storeKey) key1 := types.FormatHistoricalTimeIndexTWAPKey(twap.Time, twap.PoolId, twap.Asset0Denom, twap.Asset1Denom) key2 := types.FormatHistoricalPoolIndexTWAPKey(twap.PoolId, twap.Asset0Denom, twap.Asset1Denom, twap.Time) @@ -110,12 +110,12 @@ func (k Keeper) pruneRecordsBeforeTimeButNewest(ctx sdk.Context, lastKeptTime ti continue } - k.deleteHistoricalRecord(ctx, twapToRemove) + k.DeleteHistoricalRecord(ctx, twapToRemove) } return nil } -func (k Keeper) deleteHistoricalRecord(ctx sdk.Context, twap types.TwapRecord) { +func (k Keeper) DeleteHistoricalRecord(ctx sdk.Context, twap types.TwapRecord) { store := ctx.KVStore(k.storeKey) key1 := types.FormatHistoricalTimeIndexTWAPKey(twap.Time, twap.PoolId, twap.Asset0Denom, twap.Asset1Denom) key2 := types.FormatHistoricalPoolIndexTWAPKey(twap.PoolId, twap.Asset0Denom, twap.Asset1Denom, twap.Time) @@ -152,22 +152,34 @@ func (k Keeper) GetAllMostRecentRecordsForPool(ctx sdk.Context, poolId uint64) ( } // getAllHistoricalTimeIndexedTWAPs returns all historical TWAPs indexed by time. -func (k Keeper) getAllHistoricalTimeIndexedTWAPs(ctx sdk.Context) ([]types.TwapRecord, error) { +func (k Keeper) GetAllHistoricalTimeIndexedTWAPs(ctx sdk.Context) ([]types.TwapRecord, error) { return osmoutils.GatherValuesFromStorePrefix(ctx.KVStore(k.storeKey), []byte(types.HistoricalTWAPTimeIndexPrefix), types.ParseTwapFromBz) } // getAllHistoricalPoolIndexedTWAPs returns all historical TWAPs indexed by pool id. -// nolint: unused func (k Keeper) getAllHistoricalPoolIndexedTWAPs(ctx sdk.Context) ([]types.TwapRecord, error) { return osmoutils.GatherValuesFromStorePrefix(ctx.KVStore(k.storeKey), []byte(types.HistoricalTWAPPoolIndexPrefix), types.ParseTwapFromBz) } +// GetAllHistoricalPoolIndexedTWAPsForPoolId returns HistoricalTwapRecord for a pool give poolId. +func (k Keeper) GetAllHistoricalPoolIndexedTWAPsForPoolId(ctx sdk.Context, poolId uint64) ([]types.TwapRecord, error) { + return osmoutils.GatherValuesFromStorePrefix(ctx.KVStore(k.storeKey), types.FormatKeyPoolTwapRecords(poolId), types.ParseTwapFromBz) +} + // StoreNewRecord stores a record, in both the most recent record store and historical stores. func (k Keeper) StoreNewRecord(ctx sdk.Context, twap types.TwapRecord) { store := ctx.KVStore(k.storeKey) key := types.FormatMostRecentTWAPKey(twap.PoolId, twap.Asset0Denom, twap.Asset1Denom) osmoutils.MustSet(store, key, &twap) - k.storeHistoricalTWAP(ctx, twap) + k.StoreHistoricalTWAP(ctx, twap) +} + +// DeleteMostRecentRecord deletes a given record in most recent record store. +// Note that if there are entries in historical indexes for this record, they are not deleted by this method. +func (k Keeper) DeleteMostRecentRecord(ctx sdk.Context, twap types.TwapRecord) { + store := ctx.KVStore(k.storeKey) + key := types.FormatMostRecentTWAPKey(twap.PoolId, twap.Asset0Denom, twap.Asset1Denom) + store.Delete(key) } // getRecordAtOrBeforeTime on a given input (id, t, asset0, asset1) diff --git a/x/twap/store_test.go b/x/twap/store_test.go index e763c7d8115..521d0247bad 100644 --- a/x/twap/store_test.go +++ b/x/twap/store_test.go @@ -273,13 +273,13 @@ func (s *TestSuite) TestPruneRecordsBeforeTimeButNewest() { pool5Min2SBaseMsAB, pool5Min2SBaseMsAC, pool5Min2SBaseMsBC, pool5Min1SBaseMsAB, pool5Min1SBaseMsAC, pool5Min1SBaseMsBC, pool5BaseSecBaseMsAB, pool5BaseSecBaseMsAC, pool5BaseSecBaseMsBC, - pool5Plus1SBaseMsAB, pool5Plus1SBaseMsAC, pool5Plus1SBaseMsBC := s.createTestRecordsFromTimeInPool(baseTime, 5) + pool5Plus1SBaseMsAB, pool5Plus1SBaseMsAC, pool5Plus1SBaseMsBC := s.CreateTestRecordsFromTimeInPool(baseTime, 5) // Create 12 records in the same pool from base time - 1 ms, each record with the difference of 1 second between them pool5Min2SMin1MsAB, pool5Min2SMin1MsAC, pool5Min2SMin1MsBC, pool5Min1SMin1MsAB, pool5Min1SMin1MsAC, pool5Min1SMin1MsBC, pool5BaseSecMin1MsAB, pool5BaseSecMin1MsAC, pool5BaseSecMin1MsBC, - pool5Plus1SMin1MsAB, pool5Plus1SMin1MsAC, pool5Plus1SMin1MsBC := s.createTestRecordsFromTimeInPool(baseTime.Add(-time.Millisecond), 5) + pool5Plus1SMin1MsAB, pool5Plus1SMin1MsAC, pool5Plus1SMin1MsBC := s.CreateTestRecordsFromTimeInPool(baseTime.Add(-time.Millisecond), 5) tests := map[string]struct { // order does not follow any specific pattern @@ -574,3 +574,45 @@ func (s *TestSuite) TestAccumulatorOverflow() { }) } } + +func (s *TestSuite) TestGetAllHistoricalPoolIndexedTWAPsForPooId() { + baseRecord := newEmptyPriceRecord(1, baseTime, denom0, denom1) + tPlusOneRecord := newEmptyPriceRecord(1, tPlusOne, denom0, denom1) + tests := map[string]struct { + recordsToSet []types.TwapRecord + poolId uint64 + expectedRecords []types.TwapRecord + }{ + "set single record": { + poolId: 1, + expectedRecords: []types.TwapRecord{baseRecord}, + }, + "query non-existent pool": { + poolId: 2, + expectedRecords: []types.TwapRecord{}, + }, + "set single record, different pool ID": { + poolId: 2, + expectedRecords: []types.TwapRecord{newEmptyPriceRecord(2, baseTime, denom0, denom1)}, + }, + "set two records": { + poolId: 1, + expectedRecords: []types.TwapRecord{baseRecord, tPlusOneRecord}, + }, + } + + for name, test := range tests { + s.Run(name, func() { + s.SetupTest() + twapKeeper := s.twapkeeper + s.preSetRecords(test.expectedRecords) + + // System under test. + actualRecords, err := twapKeeper.GetAllHistoricalPoolIndexedTWAPsForPoolId(s.Ctx, test.poolId) + s.NoError(err) + + // Assertions. + s.Equal(test.expectedRecords, actualRecords) + }) + } +} diff --git a/x/twap/types/keys.go b/x/twap/types/keys.go index 8b4f4f7225a..a1a003f8064 100644 --- a/x/twap/types/keys.go +++ b/x/twap/types/keys.go @@ -2,7 +2,7 @@ package types import ( "errors" - fmt "fmt" + "fmt" time "time" sdk "github.com/cosmos/cosmos-sdk/types" @@ -46,6 +46,10 @@ var ( // TODO: make utility command to automatically interlace separators +func FormatKeyPoolTwapRecords(poolId uint64) []byte { + return []byte(fmt.Sprintf("%s%d", HistoricalTWAPPoolIndexPrefix, poolId)) +} + func FormatMostRecentTWAPKey(poolId uint64, denom1, denom2 string) []byte { poolIdS := osmoutils.FormatFixedLengthU64(poolId) return []byte(fmt.Sprintf("%s%s%s%s%s%s", mostRecentTWAPsPrefix, poolIdS, KeySeparator, denom1, KeySeparator, denom2)) diff --git a/x/valset-pref/keeper.go b/x/valset-pref/keeper.go index bbdfff2847e..b511a329971 100644 --- a/x/valset-pref/keeper.go +++ b/x/valset-pref/keeper.go @@ -48,7 +48,6 @@ func (k Keeper) GetDelegationPreferences(ctx sdk.Context, delegator string) (typ if err != nil { return types.ValidatorSetPreferences{}, err } - existingDelsValSetFormatted, err := k.GetExistingStakingDelegations(ctx, delAddr) if err != nil { return types.ValidatorSetPreferences{}, err @@ -69,7 +68,7 @@ func (k Keeper) GetExistingStakingDelegations(ctx sdk.Context, delAddr sdk.AccAd existingDelegations := k.stakingKeeper.GetDelegatorDelegations(ctx, delAddr, math.MaxUint16) if len(existingDelegations) == 0 { - return nil, fmt.Errorf("No Existing delegation") + return nil, types.ErrNoDelegation } existingTotalShares := sdk.NewDec(0) diff --git a/x/valset-pref/types/errors.go b/x/valset-pref/types/errors.go new file mode 100644 index 00000000000..0d2b988e047 --- /dev/null +++ b/x/valset-pref/types/errors.go @@ -0,0 +1,7 @@ +package types + +import "errors" + +var ( + ErrNoDelegation = errors.New("No existing delegation") +) diff --git a/x/valset-pref/validator_set.go b/x/valset-pref/validator_set.go index 744171955ad..7fe800f6d59 100644 --- a/x/valset-pref/validator_set.go +++ b/x/valset-pref/validator_set.go @@ -74,7 +74,7 @@ func (k Keeper) DelegateToValidatorSet(ctx sdk.Context, delegatorAddr string, co // get valset formatted delegation either from existing val set preference or existing delegations existingSet, err := k.GetDelegationPreferences(ctx, delegatorAddr) if err != nil { - return fmt.Errorf("error upon getting delegation preference for addr %s", delegatorAddr) + return err } delegator, err := sdk.AccAddressFromBech32(delegatorAddr)