diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 49fa5bd25f..b60aa4e9ed 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -40,6 +40,10 @@ jobs: run: sudo add-apt-repository -y ppa:ethereum/ethereum && sudo apt-get update && sudo apt-get install ethereum - name: install golangcli-lint run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.47.3 + + - name: Get dependencies + run: | + go get -v -t -d ./... - name: Build run: make install diff --git a/Makefile b/Makefile index d82b563eb1..88cbdddbeb 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ smart_contract_file=cmd/ebrelayer/contract/generated/artifacts/contracts/BridgeR BUILD_TAGS ?= ${IMAGE_TAG} BUILD_FLAGS := -ldflags '$(ldflags)' -tags "$(GOTAGS) ${BUILD_TAGS}" -BINARIES=./cmd/sifnoded ./cmd/sifgen ./cmd/ebrelayer +BINARIES=./cmd/sifnoded ./cmd/sifgen ./cmd/ebrelayer ./cmd/siftest # You can regenerate proto_files with # find . -name *.proto | sort | grep -v node_mo | grep -v test/integration | xargs echo diff --git a/app/ante/ante.go b/app/ante/ante.go index e4680f9828..4b5d46fb75 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -1,10 +1,12 @@ package ante import ( - clptypes "github.com/Sifchain/sifnode/x/clp/types" "strings" + adminkeeper "github.com/Sifchain/sifnode/x/admin/keeper" + clptypes "github.com/Sifchain/sifnode/x/clp/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" disptypes "github.com/Sifchain/sifnode/x/dispensation/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -24,8 +26,8 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { sigGasConsumer = ante.DefaultSigVerificationGasConsumer } return sdk.ChainAnteDecorators( - ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first - NewAdjustGasPriceDecorator(), // Custom decorator to adjust gas price for specific msg types + ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first + NewAdjustGasPriceDecorator(options.AdminKeeper), // Custom decorator to adjust gas price for specific msg types ante.NewRejectExtensionOptionsDecorator(), ante.NewMempoolFeeDecorator(), ante.NewValidateBasicDecorator(), @@ -45,15 +47,19 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { // AdjustGasPriceDecorator is a custom decorator to reduce fee prices . type AdjustGasPriceDecorator struct { + adminKeeper adminkeeper.Keeper } // NewAdjustGasPriceDecorator create a new instance of AdjustGasPriceDecorator -func NewAdjustGasPriceDecorator() AdjustGasPriceDecorator { - return AdjustGasPriceDecorator{} +func NewAdjustGasPriceDecorator(adminKeeper adminkeeper.Keeper) AdjustGasPriceDecorator { + return AdjustGasPriceDecorator{adminKeeper: adminKeeper} } // AnteHandle adjusts the gas price based on the tx type. func (r AdjustGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + adminParams := r.adminKeeper.GetParams(ctx) + submitProposalFee := adminParams.SubmitProposalFee + msgs := tx.GetMsgs() if len(msgs) == 1 && (strings.Contains(strings.ToLower(sdk.MsgTypeURL(msgs[0])), strings.ToLower(disptypes.MsgTypeCreateDistribution)) || strings.Contains(strings.ToLower(sdk.MsgTypeURL(msgs[0])), strings.ToLower(disptypes.MsgTypeRunDistribution))) { @@ -80,6 +86,8 @@ func (r AdjustGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate minFee = sdk.NewInt(100000000000000000) // 0.1 } else if strings.Contains(msgTypeURLLower, "transfer") && minFee.LTE(sdk.NewInt(10000000000000000)) { minFee = sdk.NewInt(10000000000000000) // 0.01 + } else if strings.Contains(msgTypeURLLower, "submitproposal") || strings.Contains(msgTypeURLLower, govtypes.TypeMsgSubmitProposal) { + minFee = sdk.NewIntFromBigInt(submitProposalFee.BigInt()) } } if minFee.Equal(sdk.ZeroInt()) { diff --git a/app/ante/ante_test.go b/app/ante/ante_test.go index 255b5d795b..b103c15c83 100644 --- a/app/ante/ante_test.go +++ b/app/ante/ante_test.go @@ -22,7 +22,7 @@ func TestAdjustGasPriceDecorator_AnteHandle(t *testing.T) { app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams()) initTokens := sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction) addrs := sifapp.AddTestAddrs(app, ctx, 6, initTokens) - decorator := ante.AdjustGasPriceDecorator{} + decorator := ante.NewAdjustGasPriceDecorator(app.AdminKeeper) highGasPrice := sdk.DecCoin{ Denom: "rowan", Amount: sdk.MustNewDecFromStr("0.5"), @@ -76,7 +76,7 @@ func TestAdjustGasPriceDecorator_AnteHandle_MinFee(t *testing.T) { app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams()) initTokens := sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction) addrs := sifapp.AddTestAddrs(app, ctx, 6, initTokens) - decorator := ante.AdjustGasPriceDecorator{} + decorator := ante.NewAdjustGasPriceDecorator(app.AdminKeeper) highFee := sdk.NewCoins(sdk.NewCoin("rowan", sdk.NewInt(100000000000000000))) // 0.1 lowFee := sdk.NewCoins(sdk.NewCoin("rowan", sdk.NewInt(10000000000000000))) // 0.01 diff --git a/app/ante/handler_options.go b/app/ante/handler_options.go index 9ff09cf60e..104483ba3b 100644 --- a/app/ante/handler_options.go +++ b/app/ante/handler_options.go @@ -1,6 +1,7 @@ package ante import ( + adminkeeper "github.com/Sifchain/sifnode/x/admin/keeper" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/x/auth/ante" @@ -13,6 +14,7 @@ import ( // HandlerOptions defines the list of module keepers required to run the Sifnode // AnteHandler decorators. type HandlerOptions struct { + AdminKeeper adminkeeper.Keeper AccountKeeper ante.AccountKeeper BankKeeper bankkeeper.Keeper FeegrantKeeper ante.FeegrantKeeper diff --git a/app/app.go b/app/app.go index e7aa316ee8..8c7cde5940 100644 --- a/app/app.go +++ b/app/app.go @@ -623,6 +623,7 @@ func NewSifAppWithBlacklist( app.MountMemoryStores(memKeys) anteHandler, err := sifchainAnte.NewAnteHandler( sifchainAnte.HandlerOptions{ + AdminKeeper: app.AdminKeeper, AccountKeeper: app.AccountKeeper, BankKeeper: app.BankKeeper, StakingKeeper: app.StakingKeeper, diff --git a/app/app_test.go b/app/app_test.go index 4b6f006363..045f0ef666 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -71,7 +71,7 @@ func TestAppUpgrade_CannotDeleteLatestVersion(t *testing.T) { encCfg, EmptyAppOptions{}, func(app *baseapp.BaseApp) { - cms := rootmulti.NewStore(db) + cms := rootmulti.NewStore(db, app.Logger()) cms.SetPruning(storetypes.PruneDefault) app.SetCMS(cms) }, @@ -156,7 +156,7 @@ func TestAppUpgrade_CannotLoadCorruptStoreUsingLatestHeight(t *testing.T) { encCfg, EmptyAppOptions{}, func(app *baseapp.BaseApp) { - cms := rootmulti.NewStore(db) + cms := rootmulti.NewStore(db, app.Logger()) cms.SetPruning(storetypes.PruneDefault) app.SetCMS(cms) }, @@ -190,7 +190,7 @@ func TestAppUpgrade_CannotLoadCorruptStoreUsingLatestHeight(t *testing.T) { encCfg, EmptyAppOptions{}, func(app *baseapp.BaseApp) { - cms := rootmulti.NewStore(db) + cms := rootmulti.NewStore(db, app.Logger()) cms.SetPruning(storetypes.PruneDefault) app.SetCMS(cms) }, diff --git a/app/setup_handlers.go b/app/setup_handlers.go index cf49b26b9d..a2c36c5193 100644 --- a/app/setup_handlers.go +++ b/app/setup_handlers.go @@ -1,17 +1,23 @@ package app import ( + admintypes "github.com/Sifchain/sifnode/x/admin/types" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" m "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/upgrade/types" ) -const releaseVersion = "1.0-beta.13" +const releaseVersion = "1.1.0-beta.rc1" func SetupHandlers(app *SifchainApp) { app.UpgradeKeeper.SetUpgradeHandler(releaseVersion, func(ctx sdk.Context, plan types.Plan, vm m.VersionMap) (m.VersionMap, error) { app.Logger().Info("Running upgrade handler for " + releaseVersion) + + app.AdminKeeper.SetParams(ctx, &admintypes.Params{ + SubmitProposalFee: sdk.NewUintFromString("5000000000000000000000"), + }) + return app.mm.RunMigrations(ctx, app.configurator, vm) }) diff --git a/cmd/siftest/main.go b/cmd/siftest/main.go new file mode 100644 index 0000000000..bd37026686 --- /dev/null +++ b/cmd/siftest/main.go @@ -0,0 +1,146 @@ +package main + +import ( + "fmt" + "os" + + "github.com/Sifchain/sifnode/app" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/config" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/server" + svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func main() { + encodingConfig := app.MakeTestEncodingConfig() + initClientCtx := client.Context{}. + WithCodec(encodingConfig.Marshaler). + WithInterfaceRegistry(encodingConfig.InterfaceRegistry). + WithTxConfig(encodingConfig.TxConfig). + WithLegacyAmino(encodingConfig.Amino). + WithInput(os.Stdin). + WithAccountRetriever(authtypes.AccountRetriever{}). + WithBroadcastMode(flags.BroadcastBlock). + WithHomeDir(app.DefaultNodeHome). + WithViper("") + app.SetConfig(false) + + rootCmd := &cobra.Command{ + Use: "siftest", + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + initClientCtx, err := client.ReadPersistentCommandFlags(initClientCtx, cmd.Flags()) + if err != nil { + return err + } + initClientCtx, err = config.ReadFromClientConfig(initClientCtx) + if err != nil { + return err + } + if err := client.SetCmdClientContextHandler(initClientCtx, cmd); err != nil { + return err + } + return server.InterceptConfigsPreRunHandler(cmd, "", nil) + }, + //RunE: run, + } + + rootCmd.AddCommand(GetVerifyCmd(), GetTestCmd()) + + err := svrcmd.Execute(rootCmd, app.DefaultNodeHome) + if err != nil { + panic(err) + } +} + +func GetTestCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "test", + Short: "Run tests", + RunE: runTest, + } + flags.AddTxFlagsToCmd(cmd) + cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") + return cmd +} + +func GetVerifyCmd() *cobra.Command { + verifyCmd := &cobra.Command{ + Use: "verify", + Short: "Verify transaction results", + } + + verifyCmd.AddCommand(GetVerifyRemove(), GetVerifyAdd(), GetVerifyOpen(), GetVerifyClose()) + + return verifyCmd +} + +/* VerifySwap verifies amounts sent and received from wallet address. + */ +func VerifySwap(clientCtx client.Context, key keyring.Info) { + +} + +func GetVerifyOpen() *cobra.Command { + cmd := &cobra.Command{ + Use: "open-position --height --from --collateralAmount --leverage --collateral-asset --borrow-asset", + Short: "Verify a margin long position open", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Printf("verifying open...\n") + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + collateralAmount := sdk.NewUintFromString(viper.GetString("collateralAmount")) + leverageDec, err := sdk.NewDecFromStr(viper.GetString("leverage")) + if err != nil { + panic(err) + } + + err = VerifyOpenLong(clientCtx, + viper.GetString("from"), + int64(viper.GetUint64("height")), + collateralAmount, + viper.GetString("collateral-asset"), + viper.GetString("borrow-asset"), + leverageDec) + if err != nil { + panic(err) + } + + return nil + }, + } + + flags.AddQueryFlagsToCmd(cmd) + //cmd.Flags().Uint64("height", 0, "height of transaction") + cmd.Flags().String("from", "", "address of transactor") + cmd.Flags().String("collateralAmount", "0", "collateral provided") + cmd.Flags().String("leverage", "0", "leverage") + cmd.Flags().String("collateral-asset", "", "collateral asset") + cmd.Flags().String("borrow-asset", "", "borrow asset") + _ = cmd.MarkFlagRequired("from") + _ = cmd.MarkFlagRequired("collateralAmount") + _ = cmd.MarkFlagRequired("leverage") + _ = cmd.MarkFlagRequired("collateral-asset") + _ = cmd.MarkFlagRequired("height") + return cmd +} + +func VerifyOpenLong(clientCtx client.Context, + from string, + height int64, + collateralAmount sdk.Uint, + collateralAsset, + borrowAsset string, + leverage sdk.Dec) error { + + return nil +} diff --git a/cmd/siftest/readme.md b/cmd/siftest/readme.md new file mode 100644 index 0000000000..d48492e2df --- /dev/null +++ b/cmd/siftest/readme.md @@ -0,0 +1,137 @@ +# Siftest User Guide + +## About + +This can be used after running transactions to verify expected vs real changes. + +## Installation + +```shell +git clone git@github.com:Sifchain/sifnode.git +cd sifnode +make install +``` + +## Verify Add Liquidity + +1. Execute add liquidity transaction +2. Verify by passing in the following arguments + 1. --height [height of transaction] + 2. --from [address of transactor] + 3. --external-asset [external asset of pool] + 4. --nativeAmount [native amount requested to add] + 5. --externalAmount [external amount requested to add] + 6. --node [node to connect to] +```shell +siftest verify add --from sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd --height=43516 --external-asset=ceth --nativeAmount=96176925423929435353999282 --externalAmount=488436982990 --node tcp://localhost:26657 +``` + +Output +```shell +verifying add... + +Wallet native balance before 499999807448801156767565321374116 +Wallet external balance before 499999999999999999999022148656694 + +Wallet native balance after 499999711271875632838129967374834 +Wallet external balance after 499999999999999999998533711673704 + +Wallet native diff -96176925523929435353999282 (expected: -96176925423929435353999282 unexpected: -100000000000000000) +Wallet external diff -488436982990 (expected: -488436982990 unexpected: 0) + +LP units before 192542049745763466715763665 +LP units after 288716849962488234851636499 +LP units diff 96174800216724768135872834 (expected: 96174800216724768135872834) + +Pool share before 1.000000000000000000 +Pool share after 1.000000000000000000 +``` + +## Verify Remove Liquidity + +1. Execute remove liquidity transaction +2. Verify by passing in the following parameters: + 1. --height [height of transaction] + 2. --from [address of transactor] + 3. --external-asset [external asset of pool] + 4. --units [units requested for removal] + 5. --node [node to connect to] +Command +```shell +siftest verify remove --from sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd --units 1000000000000000000 --height=33068 --external-asset=ceth --node tcp://localhost:26657 +``` + +Output +```shell +verifying removal... + +Wallet native balance before 499999807448701559072997868307527 +Wallet external balance before 499999999999999999999022148651615 + +Wallet native balance after 499999807448702459100223367291740 +Wallet external balance after 499999999999999999999022148656694 + +Wallet native diff 900027225498984213 (expected: 1000032419169645384 unexpected: -100005193670661171) +Wallet external diff 5079 (expected: 5079 unexpected: 0) + +LP units before 192542050745763466715763665 +LP units after 192542049745763466715763665 +LP units diff -1000000000000000000 (expected: -1000000000000000000) + +Pool share before 1.000000000000000000 +Pool share after 1.000000000000000000 + +``` + +## Verify Close Position + +1. Execute close margin position +2. Verify by passing in the following params: + 1. --height [height of transaction] + 2. --id [mtp id] + 3. --from [owner of mtp] + 4. --node [node to connect to] + +Run command using height of close transaction and MTP id. +```shell +siftest verify close --from sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd --height=72990 --id=4 --node tcp://localhost:26657 +``` +Output +```shell +verifying close... + +MTP collateral 500000000000 (stake) +MTP leverage 2.000000000000000000 +MTP liability 500000000000 +MTP health 1.988017999020000000 +MTP interest paid custody 539 +MTP interest paid collateral 550 +MTP interest unpaid collateral 0 + +Wallet collateral balance before: 488999999999999500000000000 +Wallet custody balance before: 499999211271873832838129967124878 + +confirmed MTP does not exist at close height 72990 + + +Pool health before 0.999999999999999000 +Pool native custody before 996999999460 +Pool external custody before 0 +Pool native liabilities before 0 +Pool external liabilities before 500000000000 +Pool native depth (including liabilities) before 499999999999999003000000496 +Pool external depth (including liabilities) before 500000000000001000000000000 + +Pool health after 0.999999999999999000 +Pool native custody after 0 +Pool external custody after 0 +Pool native liabilities after 0 +Pool external liabilities after 0 + +Return amount: 494008999461 +Loss: 0 + +Wallet collateral balance after: 488999999999999994008999412 (diff: 494008999412) +Wallet custody balance after: 499999211271873732838129967124882 (diff: -99999999999999996) + +``` \ No newline at end of file diff --git a/cmd/siftest/test.go b/cmd/siftest/test.go new file mode 100644 index 0000000000..bf7f98fe6b --- /dev/null +++ b/cmd/siftest/test.go @@ -0,0 +1,283 @@ +package main + +import ( + "context" + "errors" + "fmt" + "log" + + clpkeeper "github.com/Sifchain/sifnode/x/clp/keeper" + clptypes "github.com/Sifchain/sifnode/x/clp/types" + "github.com/Sifchain/sifnode/x/margin/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/spf13/cobra" +) + +func runTest(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + key, err := txf.Keybase().Key(clientCtx.GetFromName()) + if err != nil { + return err + } + + accountNumber, seq, err := txf.AccountRetriever().GetAccountNumberSequence(clientCtx, key.GetAddress()) + if err != nil { + panic(err) + } + + txf = txf.WithAccountNumber(accountNumber).WithSequence(seq) + err = TestOpenPosition(clientCtx, txf, key) + if err != nil { + panic(err) + } + + txf = txf.WithAccountNumber(accountNumber).WithSequence(seq) + err = TestSwap(clientCtx, txf, key) + if err != nil { + panic(err) + } + + return nil +} + +func TestAddLiquidity(clientCtx client.Context, txf tx.Factory, key keyring.Info) error { + clpQueryClient := clptypes.NewQueryClient(clientCtx) + + poolBefore, err := clpQueryClient.GetPool(context.Background(), &clptypes.PoolReq{Symbol: "ceth"}) + if err != nil { + return err + } + + lpBefore, err := clpQueryClient.GetLiquidityProvider(context.Background(), &clptypes.LiquidityProviderReq{ + Symbol: "ceth", + LpAddress: key.GetAddress().String(), + }) + if err != nil { + // if lp doesn't exist + lpBefore = &clptypes.LiquidityProviderRes{ + LiquidityProvider: &clptypes.LiquidityProvider{ + LiquidityProviderUnits: sdk.ZeroUint(), + }, + } + } + + nativeAdd := poolBefore.Pool.NativeAssetBalance.Quo(sdk.NewUint(1000)) + externalAdd := poolBefore.Pool.ExternalAssetBalance.Quo(sdk.NewUint(1000)) + + msg := clptypes.MsgAddLiquidity{ + Signer: key.GetAddress().String(), + ExternalAsset: &clptypes.Asset{Symbol: "ceth"}, + NativeAssetAmount: nativeAdd, + ExternalAssetAmount: externalAdd, + } + + if _, err := buildAndBroadcast(clientCtx, txf, key, &msg); err != nil { + return err + } + + poolAfter, err := clpQueryClient.GetPool(context.Background(), &clptypes.PoolReq{Symbol: "ceth"}) + if err != nil { + return err + } + + if !poolBefore.Pool.NativeAssetBalance.Equal(poolAfter.Pool.NativeAssetBalance.Sub(nativeAdd)) { + return errors.New(fmt.Sprintf("native balance mismatch afer add (before: %s after: %s)", + poolBefore.Pool.NativeAssetBalance.String(), + poolAfter.Pool.NativeAssetBalance.String())) + } + + if !poolAfter.Pool.ExternalAssetBalance.Sub(externalAdd).Equal(poolBefore.Pool.ExternalAssetBalance) { + return errors.New(fmt.Sprintf("external balance mismatch afer add (added: %s diff: %s)", + externalAdd, + poolAfter.Pool.ExternalAssetBalance.Sub(poolBefore.Pool.ExternalAssetBalance).String())) + } + + pmtpParams, err := clpQueryClient.GetPmtpParams(context.Background(), &clptypes.PmtpParamsReq{}) + if err != nil { + return err + } + + swapFeeParams, err := clpQueryClient.GetSwapFeeParams(context.Background(), &clptypes.SwapFeeParamsReq{}) + if err != nil { + return err + } + + // get native swap fee rate + sellNativeSwapFeeRate := swapFeeParams.DefaultSwapFeeRate + for _, tokenParam := range swapFeeParams.TokenParams { + if tokenParam.Asset == clptypes.GetSettlementAsset().Symbol { + sellNativeSwapFeeRate = tokenParam.SwapFeeRate + break + } + } + + // get external token swap fee rate + buyNativeSwapFeeRate := swapFeeParams.DefaultSwapFeeRate + for _, tokenParam := range swapFeeParams.TokenParams { + if tokenParam.Asset == msg.ExternalAsset.Symbol { + buyNativeSwapFeeRate = tokenParam.SwapFeeRate + break + } + } + + // calculate expected result + newPoolUnits, lpUnits, _, _, err := clpkeeper.CalculatePoolUnits( + poolBefore.Pool.PoolUnits, + poolBefore.Pool.NativeAssetBalance, + poolBefore.Pool.ExternalAssetBalance, + msg.NativeAssetAmount, + msg.ExternalAssetAmount, + sellNativeSwapFeeRate, + buyNativeSwapFeeRate, + pmtpParams.PmtpRateParams.PmtpCurrentRunningRate) + if err != nil { + return err + } + + if !poolAfter.Pool.PoolUnits.Equal(newPoolUnits) { + return errors.New(fmt.Sprintf("pool unit mismatch (expected: %s after: %s)", newPoolUnits.String(), poolAfter.Pool.PoolUnits.String())) + } + + lp, err := clpQueryClient.GetLiquidityProvider(context.Background(), &clptypes.LiquidityProviderReq{ + Symbol: "ceth", + LpAddress: key.GetAddress().String(), + }) + if err != nil { + return err + } + + if !lp.LiquidityProvider.LiquidityProviderUnits.Sub(lpBefore.LiquidityProvider.LiquidityProviderUnits).Equal(lpUnits) { + return errors.New(fmt.Sprintf("liquidity provided unit mismatch (expected: %s received: %s)", + lpUnits.String(), + lp.LiquidityProvider.LiquidityProviderUnits.String()), + ) + } + + return nil +} + +func TestSwap(clientCtx client.Context, txf tx.Factory, key keyring.Info) error { + + bankQueryClient := banktypes.NewQueryClient(clientCtx) + cethBefore, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: key.GetAddress().String(), + Denom: "ceth", + }) + if err != nil { + return err + } + rowanBefore, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: key.GetAddress().String(), + Denom: "rowan", + }) + if err != nil { + return err + } + + clpQueryClient := clptypes.NewQueryClient(clientCtx) + poolBefore, err := clpQueryClient.GetPool(context.Background(), &clptypes.PoolReq{Symbol: "ceth"}) + if err != nil { + return err + } + + msg := clptypes.MsgSwap{ + Signer: key.GetAddress().String(), + SentAsset: &clptypes.Asset{Symbol: "ceth"}, + ReceivedAsset: &clptypes.Asset{Symbol: "rowan"}, + SentAmount: sdk.NewUint(10000), + MinReceivingAmount: sdk.NewUint(0), + } + + if _, err := buildAndBroadcast(clientCtx, txf, key, &msg); err != nil { + return err + } + + cethAfter, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: key.GetAddress().String(), + Denom: "ceth", + }) + if err != nil { + return err + } + rowanAfter, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: key.GetAddress().String(), + Denom: "rowan", + }) + if err != nil { + return err + } + poolAfter, err := clpQueryClient.GetPool(context.Background(), &clptypes.PoolReq{Symbol: "ceth"}) + if err != nil { + return err + } + + rowanDiff := rowanAfter.Balance.Amount.Sub(rowanBefore.Balance.Amount) + // negative + cethDiff := cethAfter.Balance.Amount.Sub(cethBefore.Balance.Amount) + // negative + poolNativeDiff := poolBefore.Pool.NativeAssetBalance.Sub(poolAfter.Pool.NativeAssetBalance) + poolExternalDiff := poolAfter.Pool.ExternalAssetBalance.Sub(poolBefore.Pool.ExternalAssetBalance) + + fmt.Printf("Pool sent diff: %s\n", poolNativeDiff.String()) + fmt.Printf("Pool received diff: %s\n", poolExternalDiff.String()) + fmt.Printf("Address received diff: %s\n", rowanDiff.String()) + fmt.Printf("Address sent diff: %s\n", cethDiff.String()) + + return nil +} + +func TestOpenPosition(clientCtx client.Context, txf tx.Factory, key keyring.Info) error { + msg := types.MsgOpen{ + Signer: key.GetAddress().String(), + CollateralAsset: "rowan", + CollateralAmount: sdk.NewUint(100), + BorrowAsset: "ceth", + Position: types.Position_LONG, + Leverage: sdk.NewDec(2), + } + + res, err := buildAndBroadcast(clientCtx, txf, key, &msg) + if err != nil { + panic(err) + } + + log.Print(res) + + return err +} + +func buildAndBroadcast(clientCtx client.Context, txf tx.Factory, key keyring.Info, msg sdk.Msg) (*sdk.TxResponse, error) { + txb, err := tx.BuildUnsignedTx(txf, msg) + if err != nil { + return nil, err + } + + err = tx.Sign(txf, key.GetName(), txb, true) + if err != nil { + return nil, err + } + + txBytes, err := clientCtx.TxConfig.TxEncoder()(txb.GetTx()) + if err != nil { + return nil, err + } + + res, err := clientCtx. + WithSimulation(true). + WithBroadcastMode("block"). + BroadcastTx(txBytes) + if err != nil { + return nil, err + } + + return res, err +} diff --git a/cmd/siftest/verify.go b/cmd/siftest/verify.go new file mode 100644 index 0000000000..57c51f6425 --- /dev/null +++ b/cmd/siftest/verify.go @@ -0,0 +1,628 @@ +package main + +import ( + "context" + "fmt" + + clpkeeper "github.com/Sifchain/sifnode/x/clp/keeper" + clptypes "github.com/Sifchain/sifnode/x/clp/types" + "github.com/Sifchain/sifnode/x/margin/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func GetVerifyAdd() *cobra.Command { + cmd := &cobra.Command{ + Use: "add --height --from --nativeAmount --externalAmount --external-asset", + Short: "Verify a liquidity add", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Printf("verifying add...\n") + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + nativeAmount := sdk.NewUintFromString(viper.GetString("nativeAmount")) + externalAmount := sdk.NewUintFromString(viper.GetString("externalAmount")) + + err = VerifyAdd(clientCtx, + viper.GetString("from"), + viper.GetUint64("height"), + nativeAmount, + externalAmount, + viper.GetString("external-asset")) + if err != nil { + panic(err) + } + + return nil + }, + } + + flags.AddQueryFlagsToCmd(cmd) + //cmd.Flags().Uint64("height", 0, "height of transaction") + cmd.Flags().String("from", "", "address of transactor") + cmd.Flags().String("nativeAmount", "0", "native amount added") + cmd.Flags().String("externalAmount", "0", "external amount added") + cmd.Flags().String("external-asset", "", "external asset of pool") + _ = cmd.MarkFlagRequired("from") + _ = cmd.MarkFlagRequired("nativeAmount") + _ = cmd.MarkFlagRequired("externalAmount") + _ = cmd.MarkFlagRequired("external-asset") + _ = cmd.MarkFlagRequired("height") + return cmd +} + +func VerifyAdd(clientCtx client.Context, from string, height uint64, nativeAmount, externalAmount sdk.Uint, externalAsset string) error { + // Lookup wallet balances before remove + // Lookup wallet balances after remove + bankQueryClient := banktypes.NewQueryClient(clientCtx.WithHeight(int64(height - 1))) + extBefore, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: from, + Denom: externalAsset, + }) + if err != nil { + return err + } + rowanBefore, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: from, + Denom: "rowan", + }) + if err != nil { + return err + } + + // Lookup LP units before remove + // Lookup LP units after remove + clpQueryClient := clptypes.NewQueryClient(clientCtx.WithHeight(int64(height - 1))) + lpBefore, err := clpQueryClient.GetLiquidityProvider(context.Background(), &clptypes.LiquidityProviderReq{ + Symbol: externalAsset, + LpAddress: from, + }) + if err != nil { + // Use empty LP if this is the first add + lpBefore = &clptypes.LiquidityProviderRes{ + LiquidityProvider: &clptypes.LiquidityProvider{ + LiquidityProviderUnits: sdk.ZeroUint(), + }, + } + } + + // Lookup pool balances before remove + poolBefore, err := clpQueryClient.GetPool(context.Background(), &clptypes.PoolReq{Symbol: externalAsset}) + if err != nil { + return err + } + + pmtpParams, err := clpQueryClient.GetPmtpParams(context.Background(), &clptypes.PmtpParamsReq{}) + if err != nil { + return err + } + + swapFeeParams, err := clpQueryClient.GetSwapFeeParams(context.Background(), &clptypes.SwapFeeParamsReq{}) + if err != nil { + return err + } + + // get native swap fee rate + sellNativeSwapFeeRate := swapFeeParams.DefaultSwapFeeRate + for _, tokenParam := range swapFeeParams.TokenParams { + if tokenParam.Asset == clptypes.GetSettlementAsset().Symbol { + sellNativeSwapFeeRate = tokenParam.SwapFeeRate + break + } + } + + // get external token swap fee rate + buyNativeSwapFeeRate := swapFeeParams.DefaultSwapFeeRate + for _, tokenParam := range swapFeeParams.TokenParams { + if tokenParam.Asset == externalAsset { + buyNativeSwapFeeRate = tokenParam.SwapFeeRate + break + } + } + + // Calculate expected values + nativeAssetDepth := poolBefore.Pool.NativeAssetBalance.Add(poolBefore.Pool.NativeLiabilities) + externalAssetDepth := poolBefore.Pool.ExternalAssetBalance.Add(poolBefore.Pool.ExternalLiabilities) + _ /*newPoolUnits*/, lpUnits, _, _, err := clpkeeper.CalculatePoolUnits( + poolBefore.Pool.PoolUnits, + nativeAssetDepth, + externalAssetDepth, + nativeAmount, + externalAmount, + sellNativeSwapFeeRate, + buyNativeSwapFeeRate, + pmtpParams.PmtpRateParams.PmtpCurrentRunningRate) + if err != nil { + return err + } + + // Lookup wallet balances after + bankQueryClient = banktypes.NewQueryClient(clientCtx.WithHeight(int64(height))) + extAfter, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: from, + Denom: externalAsset, + }) + if err != nil { + return err + } + rowanAfter, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: from, + Denom: "rowan", + }) + if err != nil { + return err + } + + // Lookup LP after + clpQueryClient = clptypes.NewQueryClient(clientCtx.WithHeight(int64(height))) + lpAfter, err := clpQueryClient.GetLiquidityProvider(context.Background(), &clptypes.LiquidityProviderReq{ + Symbol: externalAsset, + LpAddress: from, + }) + if err != nil { + lpAfter = &clptypes.LiquidityProviderRes{ + LiquidityProvider: &clptypes.LiquidityProvider{ + LiquidityProviderUnits: sdk.ZeroUint(), + }, + } + } + + // Verify LP units are increased by lpUnits + // Verify native balance is deducted by nativeAmount + // Verify external balance is deducted by externalAmount + externalDiff := extAfter.Balance.Amount.Sub(extBefore.Balance.Amount) + nativeDiff := rowanAfter.Balance.Amount.Sub(rowanBefore.Balance.Amount) + lpUnitsBeforeInt := sdk.NewIntFromBigInt(lpBefore.LiquidityProvider.LiquidityProviderUnits.BigInt()) + lpUnitsAfterInt := sdk.NewIntFromBigInt(lpAfter.LiquidityProvider.LiquidityProviderUnits.BigInt()) + lpUnitsDiff := lpUnitsAfterInt.Sub(lpUnitsBeforeInt) + + fmt.Printf("\nWallet native balance before %s\n", rowanBefore.Balance.Amount.String()) + fmt.Printf("Wallet external balance before %s\n\n", extBefore.Balance.Amount.String()) + + fmt.Printf("Wallet native balance after %s \n", rowanAfter.Balance.Amount.String()) + fmt.Printf("Wallet external balance after %s \n", extAfter.Balance.Amount.String()) + + fmt.Printf("\nWallet native diff %s (expected: %s unexpected: %s)\n", + nativeDiff.String(), + sdk.NewIntFromBigInt(nativeAmount.BigInt()).Neg().String(), + nativeDiff.Sub(sdk.NewIntFromBigInt(nativeAmount.BigInt()).Neg()).String()) + fmt.Printf("Wallet external diff %s (expected: %s unexpected: %s)\n", + externalDiff.String(), + sdk.NewIntFromBigInt(externalAmount.BigInt()).Neg().String(), + externalDiff.Sub(sdk.NewIntFromBigInt(externalAmount.BigInt()).Neg())) + + fmt.Printf("\nLP units before %s \n", lpBefore.LiquidityProvider.LiquidityProviderUnits.String()) + fmt.Printf("LP units after %s \n", lpAfter.LiquidityProvider.LiquidityProviderUnits.String()) + fmt.Printf("LP units diff %s (expected: %s unexpected: %s)\n", lpUnitsDiff.String(), lpUnits.String(), lpUnitsDiff.Sub(sdk.NewIntFromBigInt(lpUnits.BigInt()))) + + clpQueryClient = clptypes.NewQueryClient(clientCtx.WithHeight(int64(height))) + poolAfter, err := clpQueryClient.GetPool(context.Background(), &clptypes.PoolReq{Symbol: externalAsset}) + if err != nil { + return err + } + + fmt.Printf("\nPool units before %s\n", poolBefore.Pool.PoolUnits.String()) + fmt.Printf("Pool units after %s\n", poolAfter.Pool.PoolUnits.String()) + fmt.Printf("Pool units diff %s\n", sdk.NewIntFromBigInt(poolAfter.Pool.PoolUnits.BigInt()).Sub(sdk.NewIntFromBigInt(poolBefore.Pool.PoolUnits.BigInt()))) + + lpUnitsBeforeDec := sdk.NewDecFromBigInt(lpBefore.LiquidityProvider.LiquidityProviderUnits.BigInt()) + lpUnitsAfterDec := sdk.NewDecFromBigInt(lpAfter.LiquidityProvider.LiquidityProviderUnits.BigInt()) + poolUnitsBeforeDec := sdk.NewDecFromBigInt(poolBefore.Pool.PoolUnits.BigInt()) + poolUnitsAfterDec := sdk.NewDecFromBigInt(poolAfter.Pool.PoolUnits.BigInt()) + poolShareBefore := lpUnitsBeforeDec.Quo(poolUnitsBeforeDec) + poolShareAfter := lpUnitsAfterDec.Quo(poolUnitsAfterDec) + + fmt.Printf("\nPool share before %s\n", poolShareBefore.String()) + fmt.Printf("Pool share after %s\n", poolShareAfter.String()) + + fmt.Printf("\nPool external balance before %s\n", poolBefore.Pool.ExternalAssetBalance.String()) + fmt.Printf("Pool native balance before %s\n", poolBefore.Pool.NativeAssetBalance.String()) + + fmt.Printf("\nPool external balance after %s\n", poolAfter.Pool.ExternalAssetBalance.String()) + fmt.Printf("Pool native balance after %s\n", poolAfter.Pool.NativeAssetBalance.String()) + + poolExternalDiff := sdk.NewIntFromBigInt(poolAfter.Pool.ExternalAssetBalance.BigInt()).Sub(sdk.NewIntFromBigInt(poolBefore.Pool.ExternalAssetBalance.BigInt())) + poolNativeDiff := sdk.NewIntFromBigInt(poolAfter.Pool.NativeAssetBalance.BigInt()).Sub(sdk.NewIntFromBigInt(poolBefore.Pool.NativeAssetBalance.BigInt())) + + fmt.Printf("\nPool external balance diff %s (expected: %s)\n", poolExternalDiff.String(), externalAmount.String()) + fmt.Printf("Pool native balance diff %s (expected: %s)\n", poolNativeDiff.String(), nativeAmount.String()) + + return nil +} + +func GetVerifyRemove() *cobra.Command { + cmd := &cobra.Command{ + Use: "remove --height --from --units --external-asset", + Short: "Verify a removal", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Printf("verifying removal...\n") + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + unitsRemoved := sdk.NewUintFromString(viper.GetString("units")) + + err = VerifyRemove(clientCtx, + viper.GetString("from"), + viper.GetUint64("height"), + unitsRemoved, + viper.GetString("external-asset")) + if err != nil { + panic(err) + } + + return nil + }, + } + + flags.AddQueryFlagsToCmd(cmd) + //cmd.Flags().Uint64("height", 0, "height of transaction") + cmd.Flags().String("from", "", "address of transactor") + cmd.Flags().String("units", "0", "number of units removed") + cmd.Flags().String("external-asset", "", "external asset of pool") + _ = cmd.MarkFlagRequired("from") + _ = cmd.MarkFlagRequired("units") + _ = cmd.MarkFlagRequired("external-asset") + _ = cmd.MarkFlagRequired("height") + return cmd +} + +/* + VerifyRemove verifies amounts received after remove. + --height --from --units --external-asset +*/ +func VerifyRemove(clientCtx client.Context, from string, height uint64, units sdk.Uint, externalAsset string) error { + // Lookup wallet balances before remove + // Lookup wallet balances after remove + bankQueryClient := banktypes.NewQueryClient(clientCtx.WithHeight(int64(height - 1))) + extBefore, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: from, + Denom: externalAsset, + }) + if err != nil { + return err + } + rowanBefore, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: from, + Denom: "rowan", + }) + if err != nil { + return err + } + + // Lookup LP units before remove + // Lookup LP units after remove + clpQueryClient := clptypes.NewQueryClient(clientCtx.WithHeight(int64(height - 1))) + lpBefore, err := clpQueryClient.GetLiquidityProvider(context.Background(), &clptypes.LiquidityProviderReq{ + Symbol: externalAsset, + LpAddress: from, + }) + if err != nil { + return err + } + + // Lookup pool balances before remove + poolBefore, err := clpQueryClient.GetPool(context.Background(), &clptypes.PoolReq{Symbol: externalAsset}) + if err != nil { + return err + } + + // Calculate expected values + nativeAssetDepth := poolBefore.Pool.NativeAssetBalance.Add(poolBefore.Pool.NativeLiabilities) + externalAssetDepth := poolBefore.Pool.ExternalAssetBalance.Add(poolBefore.Pool.ExternalLiabilities) + withdrawNativeAssetAmount, withdrawExternalAssetAmount, _ /*lpUnitsLeft*/ := clpkeeper.CalculateWithdrawalFromUnits(poolBefore.Pool.PoolUnits, + nativeAssetDepth.String(), externalAssetDepth.String(), lpBefore.LiquidityProvider.LiquidityProviderUnits.String(), + units) + + // Lookup wallet balances after + bankQueryClient = banktypes.NewQueryClient(clientCtx.WithHeight(int64(height))) + extAfter, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: from, + Denom: externalAsset, + }) + if err != nil { + return err + } + rowanAfter, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: from, + Denom: "rowan", + }) + if err != nil { + return err + } + + // Lookup LP after + clpQueryClient = clptypes.NewQueryClient(clientCtx.WithHeight(int64(height))) + lpAfter, err := clpQueryClient.GetLiquidityProvider(context.Background(), &clptypes.LiquidityProviderReq{ + Symbol: externalAsset, + LpAddress: from, + }) + if err != nil { + lpAfter = &clptypes.LiquidityProviderRes{ + LiquidityProvider: &clptypes.LiquidityProvider{ + LiquidityProviderUnits: sdk.ZeroUint(), + }, + } + } + + poolAfter, err := clpQueryClient.GetPool(context.Background(), &clptypes.PoolReq{Symbol: externalAsset}) + if err != nil { + return err + } + + // Verify LP units are reduced by --units + // Verify native received amount + // Verify external received amount + externalDiff := extAfter.Balance.Amount.Sub(extBefore.Balance.Amount) + nativeDiff := rowanAfter.Balance.Amount.Sub(rowanBefore.Balance.Amount) + lpUnitsBeforeInt := sdk.NewIntFromBigInt(lpBefore.LiquidityProvider.LiquidityProviderUnits.BigInt()) + lpUnitsAfterInt := sdk.NewIntFromBigInt(lpAfter.LiquidityProvider.LiquidityProviderUnits.BigInt()) + lpUnitsDiff := lpUnitsAfterInt.Sub(lpUnitsBeforeInt) + + fmt.Printf("\nWallet native balance before %s\n", rowanBefore.Balance.Amount.String()) + fmt.Printf("Wallet external balance before %s\n\n", extBefore.Balance.Amount.String()) + + fmt.Printf("Wallet native balance after %s \n", rowanAfter.Balance.Amount.String()) + fmt.Printf("Wallet external balance after %s \n", extAfter.Balance.Amount.String()) + + fmt.Printf("\nWallet native diff %s (expected: %s unexpected: %s)\n", + nativeDiff.String(), + sdk.NewIntFromBigInt(withdrawNativeAssetAmount.BigInt()).String(), + nativeDiff.Sub(sdk.NewIntFromBigInt(withdrawNativeAssetAmount.BigInt())).String()) + fmt.Printf("Wallet external diff %s (expected: %s unexpected: %s)\n", + externalDiff.String(), + sdk.NewIntFromBigInt(withdrawExternalAssetAmount.BigInt()).String(), + externalDiff.Sub(sdk.NewIntFromBigInt(withdrawExternalAssetAmount.BigInt()))) + + fmt.Printf("\nLP units before %s \n", lpBefore.LiquidityProvider.LiquidityProviderUnits.String()) + fmt.Printf("LP units after %s \n", lpAfter.LiquidityProvider.LiquidityProviderUnits.String()) + fmt.Printf("LP units diff %s (expected: -%s)\n", lpUnitsDiff.String(), units.String()) + + lpUnitsBeforeDec := sdk.NewDecFromBigInt(lpBefore.LiquidityProvider.LiquidityProviderUnits.BigInt()) + lpUnitsAfterDec := sdk.NewDecFromBigInt(lpAfter.LiquidityProvider.LiquidityProviderUnits.BigInt()) + poolUnitsBeforeDec := sdk.NewDecFromBigInt(poolBefore.Pool.PoolUnits.BigInt()) + poolUnitsAfterDec := sdk.NewDecFromBigInt(poolAfter.Pool.PoolUnits.BigInt()) + poolShareBefore := lpUnitsBeforeDec.Quo(poolUnitsBeforeDec) + poolShareAfter := lpUnitsAfterDec.Quo(poolUnitsAfterDec) + + fmt.Printf("\nPool share before %s\n", poolShareBefore.String()) + fmt.Printf("Pool share after %s\n", poolShareAfter.String()) + + return nil +} + +func GetVerifyClose() *cobra.Command { + cmd := &cobra.Command{ + Use: "close --height --from --id", + Short: "Verify a margin position close", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Printf("verifying close...\n") + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + err = VerifyClose(clientCtx, + viper.GetString("from"), + int64(viper.GetUint64("height")), + viper.GetUint64("id")) + if err != nil { + panic(err) + } + + return nil + }, + } + + flags.AddQueryFlagsToCmd(cmd) + //cmd.Flags().Uint64("height", 0, "height of transaction") + cmd.Flags().String("from", "", "address of transactor") + cmd.Flags().Uint64("id", 0, "id of mtp") + _ = cmd.MarkFlagRequired("from") + _ = cmd.MarkFlagRequired("height") + _ = cmd.MarkFlagRequired("id") + return cmd +} + +func VerifyClose(clientCtx client.Context, from string, height int64, id uint64) error { + // Lookup MTP + marginQueryClient := types.NewQueryClient(clientCtx.WithHeight(height - 1)) + mtpResponse, err := marginQueryClient.GetMTP(context.Background(), &types.MTPRequest{ + Address: from, + Id: id, + }) + if err != nil { + return sdkerrors.Wrap(err, fmt.Sprintf("error looking up mtp at height %d", height-1)) + } + fmt.Printf("\nMTP custody %s (%s)\n", mtpResponse.Mtp.CustodyAmount.String(), mtpResponse.Mtp.CustodyAsset) + fmt.Printf("MTP collateral %s (%s)\n", mtpResponse.Mtp.CollateralAmount.String(), mtpResponse.Mtp.CollateralAsset) + fmt.Printf("MTP leverage %s\n", mtpResponse.Mtp.Leverage.String()) + fmt.Printf("MTP liability %s\n", mtpResponse.Mtp.Liabilities.String()) + fmt.Printf("MTP health %s\n", mtpResponse.Mtp.MtpHealth) + fmt.Printf("MTP interest paid custody %s\n", mtpResponse.Mtp.InterestPaidCustody.String()) + fmt.Printf("MTP interest paid collateral %s\n", mtpResponse.Mtp.InterestPaidCollateral.String()) + fmt.Printf("MTP interest unpaid collateral %s\n", mtpResponse.Mtp.InterestUnpaidCollateral.String()) + + // lookup wallet before + bankQueryClient := banktypes.NewQueryClient(clientCtx.WithHeight(height - 1)) + collateralBefore, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: from, + Denom: mtpResponse.Mtp.CollateralAsset, + }) + if err != nil { + return err + } + custodyBefore, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: from, + Denom: mtpResponse.Mtp.CustodyAsset, + }) + if err != nil { + return err + } + fmt.Printf("\nWallet collateral balance before: %s\n", collateralBefore.Balance.Amount.String()) + fmt.Printf("Wallet custody balance before: %s\n\n", custodyBefore.Balance.Amount.String()) + // Ensure mtp does not exist after close + marginQueryClient = types.NewQueryClient(clientCtx.WithHeight(height)) + _, err = marginQueryClient.GetMTP(context.Background(), &types.MTPRequest{ + Address: from, + Id: id, + }) + if err != nil { + fmt.Printf("confirmed MTP does not exist at close height %d\n\n", height) + } else { + return sdkerrors.Wrap(err, fmt.Sprintf("error: found mtp at close height %d", height)) + } + + var externalAsset string + if types.StringCompare(mtpResponse.Mtp.CollateralAsset, "rowan") { + externalAsset = mtpResponse.Mtp.CustodyAsset + } else { + externalAsset = mtpResponse.Mtp.CollateralAsset + } + + clpQueryClient := clptypes.NewQueryClient(clientCtx.WithHeight(height - 1)) + poolBefore, err := clpQueryClient.GetPool(context.Background(), &clptypes.PoolReq{Symbol: externalAsset}) + if err != nil { + return err + } + fmt.Printf("\nPool health before %s\n", poolBefore.Pool.Health.String()) + fmt.Printf("Pool native custody before %s\n", poolBefore.Pool.NativeCustody.String()) + fmt.Printf("Pool external custody before %s\n", poolBefore.Pool.ExternalCustody.String()) + fmt.Printf("Pool native liabilities before %s\n", poolBefore.Pool.NativeLiabilities.String()) + fmt.Printf("Pool external liabilities before %s\n", poolBefore.Pool.ExternalLiabilities.String()) + fmt.Printf("Pool native depth (including liabilities) before %s\n", poolBefore.Pool.NativeAssetBalance.Add(poolBefore.Pool.NativeLiabilities).String()) + fmt.Printf("Pool external depth (including liabilities) before %s\n", poolBefore.Pool.ExternalAssetBalance.Add(poolBefore.Pool.ExternalLiabilities).String()) + + clpQueryClient = clptypes.NewQueryClient(clientCtx.WithHeight(height)) + poolAfter, err := clpQueryClient.GetPool(context.Background(), &clptypes.PoolReq{Symbol: externalAsset}) + if err != nil { + return err + } + + expectedPoolNativeCustody := sdk.NewIntFromBigInt(poolBefore.Pool.NativeCustody.BigInt()) + expectedPoolExternalCustody := sdk.NewIntFromBigInt(poolBefore.Pool.ExternalCustody.BigInt()) + expectedPoolNativeLiabilities := sdk.NewIntFromBigInt(poolBefore.Pool.NativeLiabilities.BigInt()) + expectedPoolExternalLiabilities := sdk.NewIntFromBigInt(poolBefore.Pool.ExternalLiabilities.BigInt()) + if types.StringCompare(mtpResponse.Mtp.CustodyAsset, "rowan") { + expectedPoolNativeCustody = expectedPoolNativeCustody.Sub( + sdk.NewIntFromBigInt(mtpResponse.Mtp.CustodyAmount.BigInt()), + ) + expectedPoolExternalLiabilities = expectedPoolExternalLiabilities.Sub( + sdk.NewIntFromBigInt(mtpResponse.Mtp.Liabilities.BigInt()), + ) + } else { + expectedPoolExternalCustody = expectedPoolExternalCustody.Sub( + sdk.NewIntFromBigInt(mtpResponse.Mtp.CustodyAmount.BigInt()), + ) + expectedPoolNativeLiabilities = expectedPoolNativeLiabilities.Sub( + sdk.NewIntFromBigInt(mtpResponse.Mtp.Liabilities.BigInt()), + ) + } + + fmt.Printf("\nPool health after %s\n", poolAfter.Pool.Health.String()) + fmt.Printf("Pool native custody after %s (expected %s)\n", poolAfter.Pool.NativeCustody.String(), expectedPoolNativeCustody.String()) + fmt.Printf("Pool external custody after %s (expected %s)\n", poolAfter.Pool.ExternalCustody.String(), expectedPoolExternalCustody.String()) + fmt.Printf("Pool native liabilities after %s (expected %s)\n", poolAfter.Pool.NativeLiabilities.String(), expectedPoolNativeLiabilities.String()) + fmt.Printf("Pool external liabilities after %s (expected %s)\n", poolAfter.Pool.ExternalLiabilities.String(), expectedPoolExternalLiabilities.String()) + + // Final interest payment + //finalInterest := marginkeeper.CalcMTPInterestLiabilities(mtpResponse.Mtp, pool.Pool.InterestRate, 0, 1) + //mtpCustodyAmount := mtpResponse.Mtp.CustodyAmount.Sub(finalInterest) + // get swap params + clpQueryClient = clptypes.NewQueryClient(clientCtx.WithHeight(height - 1)) + pmtpParams, err := clpQueryClient.GetPmtpParams(context.Background(), &clptypes.PmtpParamsReq{}) + if err != nil { + return err + } + // Calculate expected return + // Swap custody + swapFeeParams, err := clpQueryClient.GetSwapFeeParams(context.Background(), &clptypes.SwapFeeParamsReq{}) + if err != nil { + return err + } + + nativeAsset := types.GetSettlementAsset() + pool := *poolBefore.Pool + + if types.StringCompare(mtpResponse.Mtp.CustodyAsset, nativeAsset) { + pool.NativeCustody = pool.NativeCustody.Sub(mtpResponse.Mtp.CustodyAmount) + pool.NativeAssetBalance = pool.NativeAssetBalance.Add(mtpResponse.Mtp.CustodyAmount) + } else { + pool.ExternalCustody = pool.ExternalCustody.Sub(mtpResponse.Mtp.CustodyAmount) + pool.ExternalAssetBalance = pool.ExternalAssetBalance.Add(mtpResponse.Mtp.CustodyAmount) + } + X, Y, toRowan, _ := pool.ExtractValues(clptypes.Asset{Symbol: mtpResponse.Mtp.CollateralAsset}) + X, Y = pool.ExtractDebt(X, Y, toRowan) + repayAmount, _ := clpkeeper.CalcSwapResult(toRowan, X, mtpResponse.Mtp.CustodyAmount, Y, pmtpParams.PmtpRateParams.PmtpCurrentRunningRate, swapFeeParams.DefaultSwapFeeRate) + + // Repay() + mtp := mtpResponse.Mtp + // nolint:staticcheck,ineffassign + returnAmount, debtP, debtI := sdk.ZeroUint(), sdk.ZeroUint(), sdk.ZeroUint() + Liabilities := mtp.Liabilities + InterestUnpaidCollateral := mtp.InterestUnpaidCollateral + + have := repayAmount + owe := Liabilities.Add(InterestUnpaidCollateral) + + if have.LT(Liabilities) { + //can't afford principle liability + returnAmount = sdk.ZeroUint() + debtP = Liabilities.Sub(have) + debtI = InterestUnpaidCollateral + } else if have.LT(owe) { + // v principle liability; x excess liability + returnAmount = sdk.ZeroUint() + debtP = sdk.ZeroUint() + debtI = Liabilities.Add(InterestUnpaidCollateral).Sub(have) + } else { + // can afford both + returnAmount = have.Sub(Liabilities).Sub(InterestUnpaidCollateral) + debtP = sdk.ZeroUint() + debtI = sdk.ZeroUint() + } + + fmt.Printf("\nReturn amount: %s\n", returnAmount.String()) + fmt.Printf("Loss: %s\n\n", debtP.Add(debtI).String()) + + // lookup wallet balances after close + bankQueryClient = banktypes.NewQueryClient(clientCtx.WithHeight(height)) + collateralAfter, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: from, + Denom: mtpResponse.Mtp.CollateralAsset, + }) + if err != nil { + return err + } + if err != nil { + return err + } + custodyAfter, err := bankQueryClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: from, + Denom: mtpResponse.Mtp.CustodyAsset, + }) + if err != nil { + return err + } + collateralDiff := collateralAfter.Balance.Amount.Sub(collateralBefore.Balance.Amount) + custodyDiff := custodyAfter.Balance.Amount.Sub(custodyBefore.Balance.Amount) + fmt.Printf("Wallet collateral (%s) balance after: %s (diff: %s expected diff: %s)\n", + mtpResponse.Mtp.CollateralAsset, + collateralAfter.Balance.Amount.String(), + collateralDiff.String(), + returnAmount.String()) + fmt.Printf("Wallet custody (%s) balance after: %s (diff: %s)\n\n", mtpResponse.Mtp.CustodyAsset, custodyAfter.Balance.Amount.String(), custodyDiff.String()) + + return nil +} diff --git a/docs/proposals/asymmetric-adds.md b/docs/proposals/asymmetric-adds.md new file mode 100644 index 0000000000..d4069efe45 --- /dev/null +++ b/docs/proposals/asymmetric-adds.md @@ -0,0 +1,143 @@ +# Asymmetric Liquidity Adds + +Sifnoded does not currently support asymmetric liquidity adds. This document proposes a procedure +which would allow asymmetric adds. + +## Symmetric Adds + +When adding symmetrically to a pool the fraction of total pool units owned by the Liquidity Provider (LP) +after the add must equal the amount of native token added to the pool as a fraction of total native asset token in the +pool (after the add): + +``` +l / (P + l) = r / (r + R) +``` + +Where: +``` +l - LP units +P - total pool units (before) +r - amount of native token added +R - native asset pool depth (before) +``` +Rearranging gives: + +``` +(1) l = r * P / R +``` + +## Asymmetric adds + +In the asymmetric case, by definition: + +``` +R/A =/= r/a +``` + +(this includes the case where the division is not defined i.e. when a=0 the division is not defined +in which case the add is considered asymmetric) + +Where: +``` +R - native asset pool depth (before adding liquidity) +A - external asset pool depth (before adding liquidity) +r - amount of native token added +a - amount of external token added +``` +Currently sifnoded blocks asymmetric adds. The following procedure is proposed to enable +asymmetric adds. + +### Proposed method + +In the following formulas: + +``` +p - current ratio shifting running rate +f - swap fee rate +``` + +If the pool is not in the same ratio as the add then either: + +1. Some r must be swapped for a, such that after the swap the add is symmetric +2. Some a must be swapped for r, such that after the swap the add is symmetric + +#### Swap native token for external token + +Swap an amount, s, of native token such that: + +``` +(R + s) / (A - g.s) = (R + r) / (A + a) = (r − s) / (a + g.s) +``` + +where g is the swap formula: + +``` +g.x = (1 - f) * (1 + r) * x * Y / (x + X) +``` + +Solving for s (using mathematica!) gives: + +``` +s = abs((sqrt(pow((-1*f*p*A*r-f*p*A*R-f*A*r-f*A*R+p*A*r+p*A*R+2*a*R+2*A*R), 2)-4*(a+A)*(a*R*R-A*r*R)) + f*p*A*r + f*p*A*R + f*A*r + f*A*R - p*A*r - p*A*R - 2*a*R - 2*A*R) / (2 * (a + A))). +``` + +The number of pool units is then given by the symmetric formula (1): + +``` +l = (r - s) * P / (R + s) +``` + +#### Swap external token for native token + +Swap an amount, s, of native token such that: + +``` +(R - s) / (A + g'.s) = (R + r) / (A + a) = (r + g'.s) / (a - s) +``` + +Where g' is the swap formula: + +``` +g' = (1 - f) * x * Y / ((x + X) * (1 + r)) +``` + +Solving for s (using mathematica!) gives: + +``` +s = abs((sqrt(R*(-1*(a+A))*(-1*f*f*a*R-f*f*A*R-2*f*p*a*R+4*f*p*A*r+2*f*p*A*R+4*f*A*r+4*f*A*R-p*p*a*R-p*p*A*R-4*p*A*r-4*p*A*R-4*A*r-4*A*R)) + f*a*R + f*A*R + p*a*R - 2*p*A*r - p*A*R - 2*A*r - 2*A*R) / (2 * (p + 1) * (r + R))) +``` + +The number of pool units is then given by the symmetric formula (1): + +``` +l = (a - s) * P / (A + s) +``` + +### Equivalence with swapping + +Any procedure which assigns LP units should guarantee that if an LP adds (x,y) then removes all their +liquidity from the pool, receiving (x',y') then it is never the case that x' > x and y' > y (all else being equal i.e. +no LPD, rewards etc.). Furthermore +assuming (without loss of generality) that x' =< x, if instead of adding to the pool then removing all liquidity +the LP had swapped (x - x'), giving them a total of y'' (i.e. y'' = y + g.(x - x')), then y'' should equal y'. (Certainly y' cannot be greater than y'' otherwise +the LP has achieved a cheap swap.) + +In the case of the proposed add liquidity procedure the amount the LP would receive by adding then removing would equal the amounts +of each token after the internal swap (at this stage the add is symmetric and with symmetric adds x' = x and y' = y), that is: +``` +(2) x' = x - s +(3) y' = y + g.s +``` +Plugging these into the equation for y'', y'' = y + g.(x - x'): +``` +y'' = y + g.(x - x') + = y + g.s by rearranging (2) and substituting + = y' by substituting (3) +``` +### Liquidity Protection + +Since the add liquidity process involves swapping then the Liquidity protection procedure must be applied. + +## References + +Detailed derivation of formulas https://hackmd.io/NjvaZY1qQiS17s_uEgZmTw?both diff --git a/docs/proposals/parameterized-swap-fees.md b/docs/proposals/parameterized-swap-fees.md new file mode 100644 index 0000000000..49998b716f --- /dev/null +++ b/docs/proposals/parameterized-swap-fees.md @@ -0,0 +1,114 @@ +# Parameterized swap fee rates + +This is a proposal to remove the minimum swap fee and introduce parameterized swap fee rates. + +## Proposed behaviour + +The swap formulas should be reverted to how they were before the introduction of min fees. + +When swapping to rowan: + +``` +raw_XYK_output = x * Y / (x + X) +adjusted_output = raw_XYK_output / (1 + r) + +(1) fee = f * adjusted_output +y = adjusted_output - fee +``` + +Swapping from rowan: + +``` +raw_XYK_output = x * Y / (x + X) +adjusted_output = raw_XYK_output * (1 + r) + +(2) fee = f * adjusted_output +y = adjusted_output - fee +``` + +Where: + +``` +X - input depth (balance + liabilities) +Y - output depth (balance + liabilities) +x - input amount +y - output amount +r - current ratio shifting running rate +f - swap fee rate, this must satisfy 0 =< f =< 1 +``` + +The admin account must be able to specify a default swap fee rate and specify override values for specific tokens. See the CLI section for commands for setting and querying the swap fee params. + +The swap fee rate of the **sell** token must be used in the swap calculations. + +When swapping between two non native tokens, TKN1:TKN2, the system performs two swaps, TKN1:rowan followed by rowan:TKN2. In this case the swap fee rate of TKN1 must be used for both swaps. + +The swaps occuring during an open or close of a margin position use the default swap fee rate. + +## Events + +There are no new events or updates to existing events. + +## CLI + +### Setting + +```bash +sifnoded tx clp set-swap-fee-params \ + --from sif \ + --path ./swap-fee-params.json \ + --keyring-backend test \ + --fees 100000000000000000rowan \ + --chain-id localnet \ + -y +``` + +```json +{ + "default_swap_fee_rate": "0.003", + "token_params": [ + { + "asset": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + "swap_fee_rate": "12" + }, + { + "asset": "cusdc", + "swap_fee_rate": "800" + }, + { + "asset": "rowan", + "swap_fee_rate": "12" + } + ] +} +``` + +### Querying + +```bash +sifnoded q clp swap-fee-params --output json +``` + +```json +{ + "default_swap_fee_rate": "0.003000000000000000", + "token_params": [ + { + "asset": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + "swap_fee_rate": "12" + }, + { + "asset": "cusdc", + "swap_fee_rate": "800" + }, + { + "asset": "rowan", + "swap_fee_rate": "12" + } + ] +} +``` + +## References + +Product spec https://hackmd.io/MhRTYAsfR2qtP83jvmDdmQ diff --git a/docs/tutorials/asymmetric-adds.md b/docs/tutorials/asymmetric-adds.md new file mode 100644 index 0000000000..536f940215 --- /dev/null +++ b/docs/tutorials/asymmetric-adds.md @@ -0,0 +1,215 @@ +This tutorial demonstrates the ability to add asymmetrically to a pool. +It also shows how adding asymmetrically to a +pool then removing liquidity is equivalent to performing a swap, that is the liquidity +provider does not achieve a cheap swap by adding then removing from the pool. + +1. Start and run the chain: + +```bash +make init +make run +``` + +2. Create a pool: + +```bash +sifnoded tx clp create-pool \ + --from sif \ + --keyring-backend test \ + --symbol ceth \ + --nativeAmount 2000000000000000000 \ + --externalAmount 2000000000000000000 \ + --fees 100000000000000000rowan \ + --chain-id localnet \ + -y +``` + +3. Confirm pool has been created: + +```bash +sifnoded q clp pools --output json | jq +``` + +returns: + +```json +{ + "pools": [ + { + "external_asset": { + "symbol": "ceth" + }, + "native_asset_balance": "2000000000000000000", + "external_asset_balance": "2000000000000000000", + "pool_units": "2000000000000000000", + "swap_price_native": "1.000000000000000000", + "swap_price_external": "1.000000000000000000", + "reward_period_native_distributed": "0", + "external_liabilities": "0", + "external_custody": "0", + "native_liabilities": "0", + "native_custody": "0", + "health": "0.000000000000000000", + "interest_rate": "0.000000000000000000", + "last_height_interest_rate_computed": "0", + "unsettled_external_liabilities": "0", + "unsettled_native_liabilities": "0", + "block_interest_native": "0", + "block_interest_external": "0" + } + ], + "clp_module_address": "sif1pjm228rsgwqf23arkx7lm9ypkyma7mzr3y2n85", + "height": "7", + "pagination": { + "next_key": null, + "total": "0" + } +} +``` + +3. Query akasha balances: + +```bash +sifnoded q bank balances $(sifnoded keys show akasha -a --keyring-backend=test) +``` + +ceth: 500000000000000000000000 +rowan: 500000000000000000000000 + +3. Add liquidity asymmetrically from akasha account to the ceth pool + +```bash +sifnoded tx clp add-liquidity \ + --from akasha \ + --keyring-backend test \ + --symbol ceth \ + --nativeAmount 1000000000000000000 \ + --externalAmount 0 \ + --fees 100000000000000000rowan \ + --chain-id localnet \ + -y +``` + +4. Query akasha balances: + +``` +sifnoded q bank balances $(sifnoded keys show akasha -a --keyring-backend=test) +``` + +ceth: 500000000000000000000000 +rowan: 499998900000000000000000 + + +4. Query ceth lps: + +```bash +sifnoded q clp lplist ceth +``` + +4. Remove the liquidity added by akasha in the previous step + +```bash +sifnoded tx clp remove-liquidity \ + --from akasha \ + --keyring-backend test \ + --symbol ceth \ + --wBasis 10000 \ + --asymmetry 0 \ + --fees 100000000000000000rowan \ + --chain-id localnet \ + -y +``` + +5. Query akasha balances: + +```bash +sifnoded q bank balances $(sifnoded keys show akasha -a --keyring-backend=test) +``` + +ceth: 500000366455407949029238 +rowan: 499999349683111923543856 + +akasha started with 500000000000000000000000rowan and now has 499999349683111923543856rowan. So akasha has +500000000000000000000000rowan - 499999349683111923543856rowan = 650316888076456144rowan less rowan. +200000000000000000rowan was spent on tx fees. So 650316888076456144rowan - 200000000000000000rowan = 450316888076456144rowan +was given to the pool by akasha. In return akasha has gained 500000366455407949029238 - 500000000000000000000000 = 366455407949029238ceth +from the pool. + +6. Check akash's gains/losses are reflected in the pool balances + +``` +sifnoded q clp pool ceth +``` + +external_asset_balance: "1633544592050970762" +native_asset_balance: "2450316888076456144" + +We can confirm that what akasha has lost the pool has gained and vice versa + +native_asset_balance = original_balance + amount_added_by_akasha + = 2000000000000000000rowan + 450316888076456144rowan + = 2450316888076456144rowan + +Which equals the queried native asset pool balance. + +external_asset_balance = original_balance - amount_gained_by_akasha + = 2000000000000000000ceth - 366455407949029238ceth + = 1633544592050970762ceth + +Which equals the queried external asset pool balance. + +Has akasha had a "cheap" swap? How much would akasha have if instead of adding and removing from the pool +they had simply swapped 450316888076456144rowan for ceth? + +7. Reset the chain + +```bash +make init +make run +``` + +8. Recreate the ceth pool + +```bash +sifnoded tx clp create-pool \ + --from sif \ + --keyring-backend test \ + --symbol ceth \ + --nativeAmount 2000000000000000000 \ + --externalAmount 2000000000000000000 \ + --fees 100000000000000000rowan \ + --chain-id localnet \ + -y +``` + +9. Swap 450316888076456144rowan for ceth from akasha: + +```bash +sifnoded tx clp swap \ + --from akasha \ + --keyring-backend test \ + --sentSymbol rowan \ + --receivedSymbol ceth \ + --sentAmount 450316888076456144 \ + --minReceivingAmount 0 \ + --fees 100000000000000000rowan \ + --chain-id localnet \ + -y +``` + +5. Query akasha balances: + +```bash +sifnoded q bank balances $(sifnoded keys show akasha -a --keyring-backend=test) +``` + +ceth: 500000366455407949029237 +rowan: 499999449683111923543856 + +akasha has swapped 450316888076456144rowan for 366455407949029237ceth. +By adding then removing from the pool, akasha gained 366455407949029238ceth and provided 450316888076456144rowan to the pool. +So both actions are almost identical, except akasha gains 1ceth more by adding then removing from the pool rather than swapping. +This is a rounding error. Which means, as expected, adding asymmetrically then removing +liquidity is equivalent to swapping. + + diff --git a/docs/tutorials/parameterized_swap_fees.md b/docs/tutorials/parameterized_swap_fees.md new file mode 100644 index 0000000000..ad50745066 --- /dev/null +++ b/docs/tutorials/parameterized_swap_fees.md @@ -0,0 +1,203 @@ +# Paramaterized swap fees + +This tutorial demonstrates the behaviour of the parameterized swap fee functionality. + +1. Start and run the chain: + +```bash +make init +make run +``` + +2. Create a pool: + +```bash +sifnoded tx clp create-pool \ + --from sif \ + --keyring-backend test \ + --symbol ceth \ + --nativeAmount 2000000000000000000 \ + --externalAmount 2000000000000000000 \ + --fees 100000000000000000rowan \ + --broadcast-mode block \ + --chain-id localnet \ + -y +``` + +3. Confirm pool has been created: + +```bash +sifnoded q clp pools --output json | jq +``` + +returns: + +```json +{ + "pools": [ + { + "external_asset": { + "symbol": "ceth" + }, + "native_asset_balance": "2000000000000000000", + "external_asset_balance": "2000000000000000000", + "pool_units": "2000000000000000000", + "swap_price_native": "1.000000000000000000", + "swap_price_external": "1.000000000000000000", + "reward_period_native_distributed": "0", + "external_liabilities": "0", + "external_custody": "0", + "native_liabilities": "0", + "native_custody": "0", + "health": "0.000000000000000000", + "interest_rate": "0.000000000000000000", + "last_height_interest_rate_computed": "0", + "unsettled_external_liabilities": "0", + "unsettled_native_liabilities": "0", + "block_interest_native": "0", + "block_interest_external": "0" + } + ], + "clp_module_address": "sif1pjm228rsgwqf23arkx7lm9ypkyma7mzr3y2n85", + "height": "5", + "pagination": { + "next_key": null, + "total": "0" + } +} +``` + +4. Query the current swap fee params: + +```bash +sifnoded q clp swap-fee-params --output json | jq +``` + +```json +{ + "default_swap_fee_rate": "0.003000000000000000", + "token_params": [] +} +``` + +5. Set new swap fee params + +```bash +sifnoded tx clp set-swap-fee-params \ + --from sif \ + --keyring-backend test \ + --chain-id localnet \ + --broadcast-mode block \ + --fees 100000000000000000rowan \ + -y \ + --path <( echo '{ + "default_swap_fee_rate": "0.003", + "token_params": [{ + "asset": "ceth", + "swap_fee_rate": "0.004" + }, + { + "asset": "rowan", + "swap_fee_rate": "0.002" + } + ] + }' ) +``` + + +6. Check swap fee params have been updated: + +```bash +sifnoded q clp swap-fee-params --output json | jq +``` + +```json +{ + "default_swap_fee_rate": "0.003000000000000000", + "token_params": [ + { + "asset": "ceth", + "min_swap_fee": "0", + "swap_fee_rate": "0.004000000000000000" + }, + { + "asset": "rowan", + "min_swap_fee": "600000000000", + "swap_fee_rate": "0.002000000000000000" + } + ] +} +``` + +7. Do a swap: + +```bash +sifnoded tx clp swap \ + --from sif \ + --keyring-backend test \ + --sentSymbol ceth \ + --receivedSymbol rowan \ + --sentAmount 200000000000000 \ + --minReceivingAmount 0 \ + --fees 100000000000000000rowan \ + --chain-id localnet \ + --broadcast-mode block \ + --output json \ + -y | jq '.logs[0].events[] | select(.type=="swap_successful").attributes[] | select(.key=="swap_amount" or .key=="liquidity_fee")' +``` + +Returns: + +```json +{ + "key": "swap_amount", + "value": "199180081991801" +} +{ + "key": "liquidity_fee", + "value": "799920007999" +} + +``` + +We've swapped ceth for rowan, so the ceth swap fee rate, `0.004`, should have been used. So the expected `swap_amount` and `liquidity_fee` are: + +``` +adjusted_output = x * Y / ((x + X)(1 + r)) + = 200000000000000 * 2000000000000000000 / ((200000000000000 + 2000000000000000000) * (1 + 0)) + = 199980001999800 + +liquidity_fee = f * adjusted_output, min_swap_fee + = 0.004 * 199980001999800 + = 799920007999 + +y = adjusted_amount - liquidity_fee + = 199980001999800 - 799920007999 + = 199180081991801 +``` + +Which match the vales returned by the swap command. + +8. Confirm that setting swap fee rate greater than one fails: + +```bash +sifnoded tx clp set-swap-fee-params \ + --from sif \ + --keyring-backend test \ + --chain-id localnet \ + --broadcast-mode block \ + --fees 100000000000000000rowan \ + -y \ + --path <( echo '{ + "default_swap_fee_rate": "0.003", + "token_params": [{ + "asset": "ceth", + "swap_fee_rate": "1.2" + }, + { + "asset": "rowan", + "swap_fee_rate": "0.002" + } + ] + }' ) +``` diff --git a/go.mod b/go.mod index 177df57ab5..cc7b54a963 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/BurntSushi/toml v1.2.0 github.com/MakeNowJust/heredoc v1.0.0 github.com/cespare/cp v1.1.1 // indirect - github.com/cosmos/cosmos-sdk v0.45.6 + github.com/cosmos/cosmos-sdk v0.45.9 github.com/cosmos/ibc-go/v2 v2.0.2 github.com/deckarep/golang-set v1.8.0 // indirect github.com/ethereum/go-ethereum v1.10.25 @@ -58,10 +58,10 @@ require ( github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/coinbase/rosetta-sdk-go v0.7.0 // indirect - github.com/confio/ics23/go v0.6.6 // indirect + github.com/confio/ics23/go v0.7.0 // indirect github.com/cosmos/btcutil v1.0.4 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect - github.com/cosmos/iavl v0.17.3 // indirect + github.com/cosmos/iavl v0.19.3 // indirect github.com/cosmos/ledger-cosmos-go v0.11.1 // indirect github.com/cosmos/ledger-go v0.9.2 // indirect github.com/creachadair/taskgroup v0.3.2 // indirect @@ -142,6 +142,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.8.0 // indirect golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect + golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect golang.org/x/net v0.0.0-20220726230323-06994584191e // indirect golang.org/x/sys v0.0.0-20220727055044-e65921a090b8 // indirect golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect @@ -157,6 +158,7 @@ require ( require pgregory.net/rapid v0.4.7 replace ( + github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0 github.com/cosmos/ibc-go/v2 => github.com/Sifchain/ibc-go/v2 v2.0.3-issue.850 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 github.com/libp2p/go-buffer-pool => github.com/libp2p/go-buffer-pool v0.1.0 diff --git a/go.sum b/go.sum index cea2ea24c0..7db1508b9d 100644 --- a/go.sum +++ b/go.sum @@ -183,8 +183,6 @@ github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE github.com/coinbase/rosetta-sdk-go v0.6.10/go.mod h1:J/JFMsfcePrjJZkwQFLh+hJErkAmdm9Iyy3D5Y0LfXo= github.com/coinbase/rosetta-sdk-go v0.7.0 h1:lmTO/JEpCvZgpbkOITL95rA80CPKb5CtMzLaqF2mCNg= github.com/coinbase/rosetta-sdk-go v0.7.0/go.mod h1:7nD3oBPIiHqhRprqvMgPoGxe/nyq3yftRmpsy29coWE= -github.com/confio/ics23/go v0.6.6 h1:pkOy18YxxJ/r0XFDCnrl4Bjv6h4LkBSpLS6F38mrKL8= -github.com/confio/ics23/go v0.6.6/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= @@ -203,13 +201,16 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cosmos/btcutil v1.0.4 h1:n7C2ngKXo7UC9gNyMNLbzqz7Asuf+7Qv4gnX/rOdQ44= github.com/cosmos/btcutil v1.0.4/go.mod h1:Ffqc8Hn6TJUdDgHBwIZLtrLQC1KdJ9jGJl/TvgUaxbU= github.com/cosmos/cosmos-sdk v0.44.5/go.mod h1:maUA6m2TBxOJZkbwl0eRtEBgTX37kcaiOWU5t1HEGaY= -github.com/cosmos/cosmos-sdk v0.45.6 h1:bnYLOcDp0cKWMLeUTTJIttq6xxRep52ulPxXC3BCfuQ= -github.com/cosmos/cosmos-sdk v0.45.6/go.mod h1:bPeeVMEtVvH3y3xAGHVbK+/CZlpaazzh77hG8ZrcJpI= +github.com/cosmos/cosmos-sdk v0.45.9 h1:Z4s1EZL/mfM8uSSZr8WmyEbWp4hqbWVI5sAIFR432KY= +github.com/cosmos/cosmos-sdk v0.45.9/go.mod h1:Z5M4TX7PsHNHlF/1XanI2DIpORQ+Q/st7oaeufEjnvU= +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= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= -github.com/cosmos/iavl v0.17.3 h1:s2N819a2olOmiauVa0WAhoIJq9EhSXE9HDBAoR9k+8Y= github.com/cosmos/iavl v0.17.3/go.mod h1:prJoErZFABYZGDHka1R6Oay4z9PrNeFFiMKHDAMOi4w= +github.com/cosmos/iavl v0.19.3 h1:cESO0OwTTxQm5rmyESKW+zESheDUYI7CcZDWWDwnuxg= +github.com/cosmos/iavl v0.19.3/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw= github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4= github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= @@ -1039,6 +1040,8 @@ 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-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= 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= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= diff --git a/integrationtest/integration_test.go b/integrationtest/integration_test.go new file mode 100644 index 0000000000..1c325cf20a --- /dev/null +++ b/integrationtest/integration_test.go @@ -0,0 +1,509 @@ +package integrationtest + +import ( + "encoding/json" + "fmt" + "os" + "testing" + + sifapp "github.com/Sifchain/sifnode/app" + clpkeeper "github.com/Sifchain/sifnode/x/clp/keeper" + "github.com/Sifchain/sifnode/x/clp/test" + clptypes "github.com/Sifchain/sifnode/x/clp/types" + ethtest "github.com/Sifchain/sifnode/x/ethbridge/test" + marginkeeper "github.com/Sifchain/sifnode/x/margin/keeper" + margintypes "github.com/Sifchain/sifnode/x/margin/types" + tokenregistrytypes "github.com/Sifchain/sifnode/x/tokenregistry/types" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + flag "github.com/spf13/pflag" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/proto/tendermint/types" +) + +type TestCase struct { + Name string + Setup struct { + Accounts []banktypes.Balance + Margin *margintypes.GenesisState + RewardsParams clptypes.RewardParams + ProtectionParams clptypes.LiquidityProtectionParams + ShiftingParams clptypes.PmtpParams + ProviderParams clptypes.ProviderDistributionParams + } + Messages []sdk.Msg +} + +func TC1(t *testing.T) TestCase { + externalAsset := "cusdc" + address := "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd" + + externalAssetBalance, ok := sdk.NewIntFromString("1000000000000000") + require.True(t, ok) + nativeAssetBalance, ok := sdk.NewIntFromString("1000000000000000000000000000") + require.True(t, ok) + balances := []banktypes.Balance{ + { + Address: address, + Coins: sdk.Coins{ + sdk.NewCoin(externalAsset, externalAssetBalance), + sdk.NewCoin("rowan", nativeAssetBalance), + }, + }, + } + + tc := TestCase{ + Name: "tc1", + Setup: struct { + Accounts []banktypes.Balance + Margin *margintypes.GenesisState + RewardsParams clptypes.RewardParams + ProtectionParams clptypes.LiquidityProtectionParams + ShiftingParams clptypes.PmtpParams + ProviderParams clptypes.ProviderDistributionParams + }{ + Accounts: balances, + ShiftingParams: *clptypes.GetDefaultPmtpParams(), + }, + Messages: []sdk.Msg{ + &clptypes.MsgCreatePool{ + Signer: address, + ExternalAsset: &clptypes.Asset{Symbol: "cusdc"}, + NativeAssetAmount: sdk.NewUintFromString("1000000000000000000000"), // 1000rowan + ExternalAssetAmount: sdk.NewUintFromString("1000000000"), // 1000cusdc + }, + &clptypes.MsgAddLiquidity{ + Signer: address, + ExternalAsset: &clptypes.Asset{Symbol: externalAsset}, + NativeAssetAmount: sdk.NewUintFromString("1000000000000000000000"), // 1000rowan + ExternalAssetAmount: sdk.NewUintFromString("1000000000"), + }, + &margintypes.MsgOpen{ + Signer: address, + CollateralAsset: "rowan", + CollateralAmount: sdk.NewUintFromString("10000000000000000000"), // 10rowan + BorrowAsset: externalAsset, + Position: margintypes.Position_LONG, + Leverage: sdk.NewDec(2), + }, + &clptypes.MsgSwap{ + Signer: address, + SentAsset: &clptypes.Asset{Symbol: externalAsset}, + ReceivedAsset: &clptypes.Asset{Symbol: clptypes.NativeSymbol}, + SentAmount: sdk.NewUintFromString("10000"), + MinReceivingAmount: sdk.NewUint(0), + }, + &clptypes.MsgRemoveLiquidity{ + Signer: address, + ExternalAsset: &clptypes.Asset{Symbol: externalAsset}, + WBasisPoints: sdk.NewInt(5000), + Asymmetry: sdk.NewInt(0), + }, + &margintypes.MsgClose{ + Signer: address, + Id: 1, + }, + }, + } + + return tc +} + +func TC2(t *testing.T) TestCase { + sifapp.SetConfig(false) + externalAsset := "cusdc" + address := "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd" + addresses, _ := ethtest.CreateTestAddrs(1) + + externalAssetBalance, ok := sdk.NewIntFromString("1000000000000000000") // 1,000,000,000,000.000000 + require.True(t, ok) + nativeAssetBalance, ok := sdk.NewIntFromString("1000000000000000000000000000000") // 1000,000,000,000.000000000000000000 + require.True(t, ok) + balances := []banktypes.Balance{ + { + Address: address, + Coins: sdk.Coins{ + sdk.NewCoin("atom", externalAssetBalance), + sdk.NewCoin(externalAsset, externalAssetBalance), + sdk.NewCoin("rowan", nativeAssetBalance), + }, + }, + { + Address: addresses[0].String(), + Coins: sdk.Coins{ + sdk.NewCoin("atom", externalAssetBalance), + sdk.NewCoin(externalAsset, externalAssetBalance), + sdk.NewCoin("rowan", nativeAssetBalance), + }, + }, + } + allocation := sdk.NewUintFromString("1000000000000000000000000") + defaultMultiplier := sdk.NewDec(1) + + tc := TestCase{ + Name: "tc2", + Setup: struct { + Accounts []banktypes.Balance + Margin *margintypes.GenesisState + RewardsParams clptypes.RewardParams + ProtectionParams clptypes.LiquidityProtectionParams + ShiftingParams clptypes.PmtpParams + ProviderParams clptypes.ProviderDistributionParams + }{ + Accounts: balances, + ShiftingParams: *clptypes.GetDefaultPmtpParams(), + RewardsParams: clptypes.RewardParams{ + LiquidityRemovalLockPeriod: 0, + LiquidityRemovalCancelPeriod: 0, + RewardPeriods: []*clptypes.RewardPeriod{ + &clptypes.RewardPeriod{ + RewardPeriodId: "1", + RewardPeriodStartBlock: 1, + RewardPeriodEndBlock: 1000, + RewardPeriodAllocation: &allocation, + RewardPeriodPoolMultipliers: []*clptypes.PoolMultiplier{}, + RewardPeriodDefaultMultiplier: &defaultMultiplier, + RewardPeriodDistribute: false, + RewardPeriodMod: 1, + }, + }, + RewardPeriodStartTime: "", + }, + ProviderParams: clptypes.ProviderDistributionParams{ + DistributionPeriods: []*clptypes.ProviderDistributionPeriod{ + &clptypes.ProviderDistributionPeriod{ + DistributionPeriodBlockRate: sdk.NewDecWithPrec(7, 6), + DistributionPeriodStartBlock: 1, + DistributionPeriodEndBlock: 1000, + DistributionPeriodMod: 1, + }, + }, + }, + }, + Messages: []sdk.Msg{ + &clptypes.MsgCreatePool{ + Signer: address, + ExternalAsset: &clptypes.Asset{Symbol: "atom"}, + NativeAssetAmount: sdk.NewUintFromString("1000000000000000000000000000"), // 1000,000,000rowan + ExternalAssetAmount: sdk.NewUintFromString("1000000000000000"), // 1000,000,000atom + }, + &margintypes.MsgOpen{ + Signer: address, + CollateralAsset: "atom", + CollateralAmount: sdk.NewUintFromString("500000000"), // 500atom + BorrowAsset: "rowan", + Position: margintypes.Position_LONG, + Leverage: sdk.NewDec(10), + }, + &margintypes.MsgOpen{ + Signer: addresses[0].String(), + CollateralAsset: "rowan", + CollateralAmount: sdk.NewUintFromString("500000000000000000000000"), // 500,000rowan + BorrowAsset: "atom", + Position: margintypes.Position_LONG, + Leverage: sdk.NewDec(5), + }, /* + &clptypes.MsgAddLiquidity{ + Signer: address, + ExternalAsset: &clptypes.Asset{Symbol: externalAsset}, + NativeAssetAmount: sdk.NewUintFromString("1000000000000000000000"), // 1000rowan + ExternalAssetAmount: sdk.NewUintFromString("1000000000"), + },*/ + }, + } + + return tc +} + +func TC3(t *testing.T) TestCase { + sifapp.SetConfig(false) + externalAsset := "cusdc" + address := "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd" + addresses, _ := ethtest.CreateTestAddrs(2) + + externalAssetBalance, ok := sdk.NewIntFromString("1000000000000000000") // 1,000,000,000,000.000000 + require.True(t, ok) + nativeAssetBalance, ok := sdk.NewIntFromString("1000000000000000000000000000000") // 1000,000,000,000.000000000000000000 + require.True(t, ok) + balances := []banktypes.Balance{ + { + Address: address, + Coins: sdk.Coins{ + sdk.NewCoin("atom", externalAssetBalance), + sdk.NewCoin(externalAsset, externalAssetBalance), + sdk.NewCoin("rowan", nativeAssetBalance), + }, + }, + { + Address: addresses[0].String(), + Coins: sdk.Coins{ + sdk.NewCoin("atom", externalAssetBalance), + sdk.NewCoin(externalAsset, externalAssetBalance), + sdk.NewCoin("rowan", nativeAssetBalance), + }, + }, + { + Address: addresses[1].String(), + Coins: sdk.Coins{ + sdk.NewCoin("atom", externalAssetBalance), + sdk.NewCoin(externalAsset, externalAssetBalance), + sdk.NewCoin("rowan", nativeAssetBalance), + }, + }, + } + allocation := sdk.NewUintFromString("2000000000000000000000000") + defaultMultiplier := sdk.NewDec(1) + + tc := TestCase{ + Name: "tc3", + Setup: struct { + Accounts []banktypes.Balance + Margin *margintypes.GenesisState + RewardsParams clptypes.RewardParams + ProtectionParams clptypes.LiquidityProtectionParams + ShiftingParams clptypes.PmtpParams + ProviderParams clptypes.ProviderDistributionParams + }{ + Accounts: balances, + ShiftingParams: *clptypes.GetDefaultPmtpParams(), + RewardsParams: clptypes.RewardParams{ + LiquidityRemovalLockPeriod: 0, + LiquidityRemovalCancelPeriod: 0, + RewardPeriods: []*clptypes.RewardPeriod{ + &clptypes.RewardPeriod{ + RewardPeriodId: "1", + RewardPeriodStartBlock: 1, + RewardPeriodEndBlock: 1000, + RewardPeriodAllocation: &allocation, + RewardPeriodPoolMultipliers: []*clptypes.PoolMultiplier{}, + RewardPeriodDefaultMultiplier: &defaultMultiplier, + RewardPeriodDistribute: false, + RewardPeriodMod: 1, + }, + }, + RewardPeriodStartTime: "", + }, + ProviderParams: clptypes.ProviderDistributionParams{ + DistributionPeriods: []*clptypes.ProviderDistributionPeriod{ + &clptypes.ProviderDistributionPeriod{ + DistributionPeriodBlockRate: sdk.NewDecWithPrec(7, 6), + DistributionPeriodStartBlock: 1, + DistributionPeriodEndBlock: 1000, + DistributionPeriodMod: 1, + }, + }, + }, + }, + Messages: []sdk.Msg{ + &clptypes.MsgCreatePool{ + Signer: address, + ExternalAsset: &clptypes.Asset{Symbol: "atom"}, + NativeAssetAmount: sdk.NewUintFromString("1000000000000000000000000000"), // 1000,000,000rowan + ExternalAssetAmount: sdk.NewUintFromString("1000000000000000"), // 1000,000,000atom + }, + &clptypes.MsgCreatePool{ + Signer: address, + ExternalAsset: &clptypes.Asset{Symbol: "cusdc"}, + NativeAssetAmount: sdk.NewUintFromString("1000000000000000000000000000"), // 1000,000,000rowan + ExternalAssetAmount: sdk.NewUintFromString("1000000000000000"), // 1000,000,000cusdc + }, + &margintypes.MsgOpen{ + Signer: address, + CollateralAsset: "cusdc", + CollateralAmount: sdk.NewUintFromString("1000000000"), // 1000atom + BorrowAsset: "rowan", + Position: margintypes.Position_LONG, + Leverage: sdk.NewDec(10), + }, + &margintypes.MsgOpen{ + Signer: addresses[0].String(), + CollateralAsset: "cusdc", + CollateralAmount: sdk.NewUintFromString("5000000000"), // 5000 + BorrowAsset: "rowan", + Position: margintypes.Position_LONG, + Leverage: sdk.NewDec(5), + }, + &margintypes.MsgOpen{ + Signer: addresses[1].String(), + CollateralAsset: "rowan", + CollateralAmount: sdk.NewUintFromString("500000000000000000000000"), // 500,000 + BorrowAsset: "atom", + Position: margintypes.Position_LONG, + Leverage: sdk.NewDec(3), + }, + &clptypes.MsgSwap{ + Signer: address, + SentAsset: &clptypes.Asset{Symbol: "atom"}, + ReceivedAsset: &clptypes.Asset{Symbol: clptypes.NativeSymbol}, + SentAmount: sdk.NewUintFromString("5000000000"), // 5000 + MinReceivingAmount: sdk.NewUint(0), + }, /* + &clptypes.MsgAddLiquidity{ + Signer: address, + ExternalAsset: &clptypes.Asset{Symbol: externalAsset}, + NativeAssetAmount: sdk.NewUintFromString("1000000000000000000000"), // 1000rowan + ExternalAssetAmount: sdk.NewUintFromString("1000000000"), + },*/ + }, + } + + return tc +} + +func TestIntegration(t *testing.T) { + overwriteFlag := flag.Bool("overwrite", false, "Overwrite test output") + flag.Parse() + + tt := []TestCase{ + TC1(t), TC2(t), TC3(t), + } + + for _, tc := range tt { + t.Run(tc.Name, func(t *testing.T) { + ctx, app := test.CreateTestAppClpFromGenesis(false, func(app *sifapp.SifchainApp, genesisState sifapp.GenesisState) sifapp.GenesisState { + // Initialise token registry + trGs := &tokenregistrytypes.GenesisState{ + Registry: &tokenregistrytypes.Registry{ + Entries: []*tokenregistrytypes.RegistryEntry{ + {Denom: "atom", BaseDenom: "atom", Decimals: 6, Permissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}}, + {Denom: "cusdc", BaseDenom: "cusdc", Decimals: 6, Permissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}}, + {Denom: "rowan", BaseDenom: "rowan", Decimals: 18, Permissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}}, + }, + }, + } + bz, _ := app.AppCodec().MarshalJSON(trGs) + genesisState["tokenregistry"] = bz + + // Initialise wallet + bankGs := banktypes.DefaultGenesisState() + bankGs.Balances = append(bankGs.Balances, tc.Setup.Accounts...) + bz, _ = app.AppCodec().MarshalJSON(bankGs) + genesisState["bank"] = bz + + // Set enabled margin pools + marginGs := margintypes.DefaultGenesis() + marginGs.Params.Pools = append(marginGs.Params.Pools, []string{"cusdc", "atom"}...) + bz, _ = app.AppCodec().MarshalJSON(marginGs) + genesisState["margin"] = bz + + return genesisState + }) + + app.ClpKeeper.SetRewardParams(ctx, &tc.Setup.RewardsParams) + app.ClpKeeper.SetLiquidityProtectionParams(ctx, &tc.Setup.ProtectionParams) + app.ClpKeeper.SetPmtpParams(ctx, &tc.Setup.ShiftingParams) + app.ClpKeeper.SetProviderDistributionParams(ctx, &tc.Setup.ProviderParams) + + clpSrv := clpkeeper.NewMsgServerImpl(app.ClpKeeper) + marginSrv := marginkeeper.NewMsgServerImpl(app.MarginKeeper) + + for i, msg := range tc.Messages { + ctx = ctx.WithBlockHeight(int64(i)) + app.BeginBlocker(ctx, abci.RequestBeginBlock{Header: types.Header{Height: ctx.BlockHeight()}}) + switch msg := msg.(type) { + case *clptypes.MsgCreatePool: + _, err := clpSrv.CreatePool(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + case *clptypes.MsgAddLiquidity: + _, err := clpSrv.AddLiquidity(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + case *clptypes.MsgRemoveLiquidity: + _, err := clpSrv.RemoveLiquidity(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + case *clptypes.MsgSwap: + _, err := clpSrv.Swap(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + case *margintypes.MsgOpen: + _, err := marginSrv.Open(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + case *margintypes.MsgClose: + _, err := marginSrv.Close(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + } + endBlock(t, app, ctx, ctx.BlockHeight()) + } + + // Check balances + results := getResults(t, app, ctx, tc) + if *overwriteFlag { + writeResults(t, tc, results) + } else { + expected, err := getExpected(tc) + require.NoError(t, err) + require.EqualValues(t, expected, &results) + } + }) + } + +} + +func endBlock(t *testing.T, app *sifapp.SifchainApp, ctx sdk.Context, height int64) { + app.EndBlocker(ctx, abci.RequestEndBlock{Height: height}) + //app.Commit() + + // Check invariants + res, stop := app.ClpKeeper.BalanceModuleAccountCheck()(ctx) + require.False(t, stop, res) +} + +type TestResults struct { + Accounts map[string]sdk.Coins `json:"accounts"` + Pools map[string]clptypes.Pool + LPs map[string]clptypes.LiquidityProvider +} + +func getResults(t *testing.T, app *sifapp.SifchainApp, ctx sdk.Context, tc TestCase) TestResults { + pools := app.ClpKeeper.GetPools(ctx) + + lps, err := app.ClpKeeper.GetAllLiquidityProviders(ctx) + require.NoError(t, err) + + results := TestResults{ + Accounts: make(map[string]sdk.Coins, len(tc.Setup.Accounts)), + Pools: make(map[string]clptypes.Pool, len(pools)), + LPs: make(map[string]clptypes.LiquidityProvider, len(lps)), + } + + for _, account := range tc.Setup.Accounts { + // Lookup account balances + addr, err := sdk.AccAddressFromBech32(account.Address) + require.NoError(t, err) + balances := app.BankKeeper.GetAllBalances(ctx, addr) + results.Accounts[account.Address] = balances + } + + for _, pool := range pools { + results.Pools[pool.ExternalAsset.Symbol] = *pool + } + + for _, lp := range lps { + results.LPs[string(clptypes.GetLiquidityProviderKey(lp.Asset.Symbol, lp.LiquidityProviderAddress))] = *lp + } + + return results +} + +func writeResults(t *testing.T, tc TestCase, results TestResults) { + bz, err := json.MarshalIndent(results, "", "\t") + fmt.Printf("%s", bz) + require.NoError(t, err) + + filename := "output/" + tc.Name + ".json" + + err = os.WriteFile(filename, bz, 0600) + require.NoError(t, err) +} + +func getExpected(tc TestCase) (*TestResults, error) { + bz, err := os.ReadFile("output/" + tc.Name + ".json") + if err != nil { + return nil, err + } + var results TestResults + err = json.Unmarshal(bz, &results) + if err != nil { + return nil, err + } + return &results, nil +} diff --git a/integrationtest/output/results.json b/integrationtest/output/results.json new file mode 100644 index 0000000000..4df8508845 --- /dev/null +++ b/integrationtest/output/results.json @@ -0,0 +1 @@ +{"accounts":{"sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd":[{"denom":"cusdc","amount":"999998975129996"},{"denom":"rowan","amount":"999999002243793715591658472"}]},"Pools":{"cusdc":{"external_asset":{"symbol":"cusdc"},"native_asset_balance":"997756206284408341528","external_asset_balance":"1024870004","pool_units":"1000000000000000000000","swap_price_native":"3.851417996402505993","swap_price_external":"0.259644629830901241","reward_period_native_distributed":"0","external_liabilities":"0","external_custody":"0","native_liabilities":"0","native_custody":"0","health":"0.995049498561352358","interest_rate":"0.400000000000000000","last_height_interest_rate_computed":5,"unsettled_external_liabilities":"0","unsettled_native_liabilities":"0","block_interest_native":"0","block_interest_external":"13699020"}},"LPs":{"\u0001cusdc_sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd":{"asset":{"symbol":"cusdc"},"liquidity_provider_units":"1000000000000000000000","liquidity_provider_address":"sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd"}}} \ No newline at end of file diff --git a/integrationtest/output/tc1.json b/integrationtest/output/tc1.json new file mode 100644 index 0000000000..eb2a85c155 --- /dev/null +++ b/integrationtest/output/tc1.json @@ -0,0 +1,47 @@ +{ + "accounts": { + "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd": [ + { + "denom": "cusdc", + "amount": "999998994370679" + }, + { + "denom": "rowan", + "amount": "999999000005078262924594065" + } + ] + }, + "Pools": { + "cusdc": { + "external_asset": { + "symbol": "cusdc" + }, + "native_asset_balance": "999994921737075405935", + "external_asset_balance": "1005629321", + "pool_units": "1000000000000000000000", + "swap_price_native": "0.987727340533795089", + "swap_price_external": "1.012425149089800862", + "reward_period_native_distributed": "0", + "external_liabilities": "0", + "external_custody": "0", + "native_liabilities": "0", + "native_custody": "0", + "health": "0.990098960118728966", + "interest_rate": "0.500000000000000000", + "last_height_interest_rate_computed": 5, + "unsettled_external_liabilities": "0", + "unsettled_native_liabilities": "2023304519940209643", + "block_interest_native": "0", + "block_interest_external": "4390203" + } + }, + "LPs": { + "\u0001cusdc_sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd": { + "asset": { + "symbol": "cusdc" + }, + "liquidity_provider_units": "1000000000000000000000", + "liquidity_provider_address": "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd" + } + } +} \ No newline at end of file diff --git a/integrationtest/output/tc2.json b/integrationtest/output/tc2.json new file mode 100644 index 0000000000..ba1d7beb9f --- /dev/null +++ b/integrationtest/output/tc2.json @@ -0,0 +1,65 @@ +{ + "accounts": { + "sif15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqhns3lt": [ + { + "denom": "atom", + "amount": "1000000000000000000" + }, + { + "denom": "cusdc", + "amount": "1000000000000000000" + }, + { + "denom": "rowan", + "amount": "999999500000000000000000000000" + } + ], + "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd": [ + { + "denom": "atom", + "amount": "998999999500000000" + }, + { + "denom": "cusdc", + "amount": "1000000000000000000" + }, + { + "denom": "rowan", + "amount": "999000014013414589440803461377" + } + ] + }, + "Pools": { + "atom": { + "external_asset": { + "symbol": "atom" + }, + "native_asset_balance": "1000487089285600288982742163", + "external_asset_balance": "999004488135149", + "pool_units": "1000000000000000000000000000", + "swap_price_native": "1.000007907317368468", + "swap_price_external": "0.999992092745156704", + "reward_period_native_distributed": "2000000000000000000000", + "external_liabilities": "500000000", + "external_custody": "996011864851", + "native_liabilities": "500000000000000000000000", + "native_custody": "897299810270213796460", + "health": "0.999499996303992980", + "interest_rate": "0.200000000000000000", + "last_height_interest_rate_computed": 2, + "unsettled_external_liabilities": "0", + "unsettled_native_liabilities": "0", + "block_interest_native": "89729273457704882289", + "block_interest_external": "0" + } + }, + "LPs": { + "\u0001atom_sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd": { + "asset": { + "symbol": "atom" + }, + "liquidity_provider_units": "1000000000000000000000000000", + "liquidity_provider_address": "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd" + } + } +} \ No newline at end of file diff --git a/integrationtest/output/tc3.json b/integrationtest/output/tc3.json new file mode 100644 index 0000000000..76dfd28272 --- /dev/null +++ b/integrationtest/output/tc3.json @@ -0,0 +1,108 @@ +{ + "accounts": { + "sif15ky9du8a2wlstz6fpx3p4mqpjyrm5cgp29yyze": [ + { + "denom": "atom", + "amount": "1000000000000000000" + }, + { + "denom": "cusdc", + "amount": "1000000000000000000" + }, + { + "denom": "rowan", + "amount": "999999500000000000000000000000" + } + ], + "sif15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqhns3lt": [ + { + "denom": "atom", + "amount": "1000000000000000000" + }, + { + "denom": "cusdc", + "amount": "999999995000000000" + }, + { + "denom": "rowan", + "amount": "1000000000000000000000000000000" + } + ], + "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd": [ + { + "denom": "atom", + "amount": "999000019869683995" + }, + { + "denom": "cusdc", + "amount": "998999999000000000" + }, + { + "denom": "rowan", + "amount": "998000075438258362685196025756" + } + ] + }, + "Pools": { + "atom": { + "external_asset": { + "symbol": "atom" + }, + "native_asset_balance": "1000465000271862905348776264", + "external_asset_balance": "999232805249606", + "pool_units": "1000000000000000000000000000", + "swap_price_native": "0.998253512801981415", + "swap_price_external": "1.001749542752037403", + "reward_period_native_distributed": "5000513370199634618000", + "external_liabilities": "0", + "external_custody": "747325066399", + "native_liabilities": "500000000000000000000000", + "native_custody": "0", + "health": "0.999500487522686271", + "interest_rate": "0.500000000000000000", + "last_height_interest_rate_computed": 5, + "unsettled_external_liabilities": "0", + "unsettled_native_liabilities": "0", + "block_interest_native": "0", + "block_interest_external": "223827155962" + }, + "cusdc": { + "external_asset": { + "symbol": "cusdc" + }, + "native_asset_balance": "999961984349043871787275403", + "external_asset_balance": "1000006000000000", + "pool_units": "1000000000000000000000000000", + "swap_price_native": "1.000044017032843971", + "swap_price_external": "0.999955984904569929", + "reward_period_native_distributed": "4999486629800365382000", + "external_liabilities": "6000000000", + "external_custody": "0", + "native_liabilities": "0", + "native_custody": "7577120730537667922577", + "health": "0.999994000071999136", + "interest_rate": "0.400000000000000000", + "last_height_interest_rate_computed": 5, + "unsettled_external_liabilities": "0", + "unsettled_native_liabilities": "0", + "block_interest_native": "2153417486774893264327", + "block_interest_external": "0" + } + }, + "LPs": { + "\u0001atom_sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd": { + "asset": { + "symbol": "atom" + }, + "liquidity_provider_units": "1000000000000000000000000000", + "liquidity_provider_address": "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd" + }, + "\u0001cusdc_sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd": { + "asset": { + "symbol": "cusdc" + }, + "liquidity_provider_units": "1000000000000000000000000000", + "liquidity_provider_address": "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd" + } + } +} \ No newline at end of file diff --git a/integrationtest/readme.md b/integrationtest/readme.md new file mode 100644 index 0000000000..b71dfb8ac8 --- /dev/null +++ b/integrationtest/readme.md @@ -0,0 +1,57 @@ +# Integration Test Framework + +This test framework takes a test case, +which specifies how to setup the chain from scratch, +and a sequence of different types of messages to execute. + +Results of the test case are compared to previous results stored. + +Setup: +* Token registry entries +* Account balances +* Margin Params +* Clp Params +* Admin accounts + +Message spec: + +```go +createPoolMsg: clptypes.MsgCreatePool{ + Signer: address, + ExternalAsset: &clptypes.Asset{Symbol: "cusdc"}, + NativeAssetAmount: sdk.NewUintFromString("1000000000000000000000"), // 1000rowan + ExternalAssetAmount: sdk.NewUintFromString("1000000000"), // 1000cusdc +}, +openPositionMsg: margintypes.MsgOpen{ + Signer: address, + CollateralAsset: "rowan", + CollateralAmount: sdk.NewUintFromString("10000000000000000000"), // 10rowan + BorrowAsset: externalAsset, + Position: margintypes.Position_LONG, + Leverage: sdk.NewDec(2), +}, +swapMsg: clptypes.MsgSwap{ + Signer: address, + SentAsset: &clptypes.Asset{Symbol: externalAsset}, + ReceivedAsset: &clptypes.Asset{Symbol: clptypes.NativeSymbol}, + SentAmount: sdk.NewUintFromString("10000"), + MinReceivingAmount: sdk.NewUint(0), +}, +addLiquidityMsg: clptypes.MsgAddLiquidity{ + Signer: address, + ExternalAsset: &clptypes.Asset{Symbol: externalAsset}, + NativeAssetAmount: sdk.NewUintFromString("1000000000000000000000"), // 1000rowan + ExternalAssetAmount: sdk.NewUintFromString("1000000000"), +}, +removeLiquidityMsg: clptypes.MsgRemoveLiquidity{ + Signer: address, + ExternalAsset: &clptypes.Asset{Symbol: externalAsset}, + WBasisPoints: sdk.NewInt(5000), + Asymmetry: sdk.NewInt(0), +}, +closePositionMsg: margintypes.MsgClose{ + Signer: address, + Id: 1, +}, +``` + diff --git a/proto/sifnode/admin/v1/query.proto b/proto/sifnode/admin/v1/query.proto index ae4bb660f8..82e17b4822 100644 --- a/proto/sifnode/admin/v1/query.proto +++ b/proto/sifnode/admin/v1/query.proto @@ -10,10 +10,17 @@ option go_package = "github.com/Sifchain/sifnode/x/admin/types"; // Query defines the gRPC querier service. service Query { rpc ListAccounts(ListAccountsRequest) returns (ListAccountsResponse) {} + rpc GetParams(GetParamsRequest) returns (GetParamsResponse) {} } message ListAccountsRequest {} message ListAccountsResponse { repeated AdminAccount keys = 2; +} + +message GetParamsRequest {} + +message GetParamsResponse { + Params params = 1; } \ No newline at end of file diff --git a/proto/sifnode/admin/v1/tx.proto b/proto/sifnode/admin/v1/tx.proto index e2ed7cf297..c197cca4ee 100644 --- a/proto/sifnode/admin/v1/tx.proto +++ b/proto/sifnode/admin/v1/tx.proto @@ -9,6 +9,7 @@ option go_package = "github.com/Sifchain/sifnode/x/admin/types"; service Msg { rpc AddAccount(MsgAddAccount) returns (MsgAddAccountResponse) {} rpc RemoveAccount(MsgRemoveAccount) returns (MsgRemoveAccountResponse) {} + rpc SetParams(MsgSetParams) returns (MsgSetParamsResponse) {} } message MsgAddAccount { @@ -23,4 +24,13 @@ message MsgRemoveAccount { AdminAccount account = 2; } -message MsgRemoveAccountResponse {} \ No newline at end of file +message MsgRemoveAccountResponse {} + +message MsgSetParams { + string signer = 1; + Params params = 2; +} + +message MsgSetParamsResponse { + +} \ No newline at end of file diff --git a/proto/sifnode/admin/v1/types.proto b/proto/sifnode/admin/v1/types.proto index d771831943..a861550e27 100644 --- a/proto/sifnode/admin/v1/types.proto +++ b/proto/sifnode/admin/v1/types.proto @@ -20,4 +20,11 @@ enum AdminType { message AdminAccount { AdminType admin_type = 1; string admin_address = 2; +} + +message Params { + string submit_proposal_fee = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; } \ No newline at end of file diff --git a/proto/sifnode/clp/v1/params.proto b/proto/sifnode/clp/v1/params.proto index 038fbb7b49..c52bb210c7 100644 --- a/proto/sifnode/clp/v1/params.proto +++ b/proto/sifnode/clp/v1/params.proto @@ -97,7 +97,7 @@ message ProviderDistributionParams { } message SwapFeeParams { - string swap_fee_rate = 1 [ + string default_swap_fee_rate = 1 [ (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false ]; @@ -106,8 +106,8 @@ message SwapFeeParams { message SwapFeeTokenParams { string asset = 1; - string min_swap_fee = 2 [ - (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + string swap_fee_rate = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false ]; } \ No newline at end of file diff --git a/proto/sifnode/clp/v1/querier.proto b/proto/sifnode/clp/v1/querier.proto index dce8aaa75c..8cf4106d3b 100644 --- a/proto/sifnode/clp/v1/querier.proto +++ b/proto/sifnode/clp/v1/querier.proto @@ -196,7 +196,7 @@ message ProviderDistributionParamsRes { message SwapFeeParamsReq {} message SwapFeeParamsRes { - string swap_fee_rate = 1 [ + string default_swap_fee_rate = 1 [ (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false ]; diff --git a/proto/sifnode/clp/v1/tx.proto b/proto/sifnode/clp/v1/tx.proto index 35410a0ee6..4f6c47bf08 100644 --- a/proto/sifnode/clp/v1/tx.proto +++ b/proto/sifnode/clp/v1/tx.proto @@ -273,7 +273,7 @@ message MsgAddProviderDistributionPeriodResponse {} message MsgUpdateSwapFeeParamsRequest { string signer = 1 [ (gogoproto.moretags) = "yaml:\"signer\"" ]; - string swap_fee_rate = 2 [ + string default_swap_fee_rate = 2 [ (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false ]; diff --git a/proto/sifnode/ethbridge/v1/query.proto b/proto/sifnode/ethbridge/v1/query.proto index d86f7e65d2..3637c33225 100644 --- a/proto/sifnode/ethbridge/v1/query.proto +++ b/proto/sifnode/ethbridge/v1/query.proto @@ -31,6 +31,7 @@ service Query { // sequence rpc PropheciesCompleted(QueryPropheciesCompletedRequest) returns (QueryPropheciesCompletedResponse) {} + rpc GetPauseStatus(QueryPauseRequest) returns (QueryPauseResponse); } // QueryEthProphecyRequest payload for EthProphecy rpc query @@ -108,4 +109,11 @@ message QueryGlobalSequenceBlockNumberResponse {uint64 block_number = 1;} message QueryBlacklistRequest {} -message QueryBlacklistResponse {repeated string addresses = 1;} \ No newline at end of file +message QueryBlacklistResponse { + repeated string addresses = 1; +} + +message QueryPauseRequest{} +message QueryPauseResponse{ + bool is_paused =1; +} \ No newline at end of file diff --git a/proto/sifnode/ethbridge/v1/tx.proto b/proto/sifnode/ethbridge/v1/tx.proto index 51916033f3..9c5bb85f81 100644 --- a/proto/sifnode/ethbridge/v1/tx.proto +++ b/proto/sifnode/ethbridge/v1/tx.proto @@ -24,6 +24,14 @@ service Msg { rpc UpdateConsensusNeeded(MsgUpdateConsensusNeeded) returns (MsgUpdateConsensusNeededResponse); rpc SetBlacklist(MsgSetBlacklist) returns (MsgSetBlacklistResponse); + rpc SetPause(MsgPause) returns (MsgPauseResponse); +} + +message MsgPause { + string signer = 1 [ (gogoproto.moretags) = "yaml:\"signer\"" ]; + bool is_paused = 2 ; +} +message MsgPauseResponse{ } // MsgLock defines a message for locking coins and triggering a related event diff --git a/proto/sifnode/ethbridge/v1/types.proto b/proto/sifnode/ethbridge/v1/types.proto index ef4f662c54..0ac536e2ff 100644 --- a/proto/sifnode/ethbridge/v1/types.proto +++ b/proto/sifnode/ethbridge/v1/types.proto @@ -69,3 +69,7 @@ message GenesisState { repeated string peggy_tokens = 2; string crosschain_fee_receive_account = 3; } + +message Pause { + bool is_paused = 1; +} diff --git a/test/integration/framework/src/siftool/command.py b/test/integration/framework/src/siftool/command.py index c43321d8bf..bb07b9228c 100644 --- a/test/integration/framework/src/siftool/command.py +++ b/test/integration/framework/src/siftool/command.py @@ -139,6 +139,14 @@ def mktempfile(self, parent_dir: Optional[str] = None) -> str: args = ["mktemp", "-p", parent_dir] if not self.is_mac() else ["mktemp", os.path.join(parent_dir, "siftool.XXXXXX")] return exactly_one(stdout_lines(self.execst(args))) + @contextlib.contextmanager + def with_temp_file(self, parent_dir: Optional[str] = None): + tmp = self.mktempfile(parent_dir=parent_dir) + try: + yield tmp + finally: + self.rm(tmp) + def chmod(self, path: str, mode_str: str, recursive: bool = False): args = ["chmod"] + (["-r"] if recursive else []) + [mode_str, path] self.execst(args) diff --git a/test/integration/framework/src/siftool/common.py b/test/integration/framework/src/siftool/common.py index 6fb20d25a1..9d367e967a 100644 --- a/test/integration/framework/src/siftool/common.py +++ b/test/integration/framework/src/siftool/common.py @@ -4,12 +4,16 @@ import subprocess import string import random +import time import yaml import urllib.request -from typing import Optional, Mapping, Sequence, IO, Union, Iterable, List +from typing import Optional, Mapping, Sequence, Set, IO, Union, Iterable, List, Any, Callable, Dict ANY_ADDR = "0.0.0.0" +LOCALHOST = "127.0.0.1" +JsonObj = Any +JsonDict = Dict[str, JsonObj] def stdout(res): @@ -24,7 +28,7 @@ def stdout_lines(res): def joinlines(lines): return "".join([x + os.linesep for x in lines]) -def zero_or_one(items): +def zero_or_one(items: Sequence[Any]) -> Any: if len(items) == 0: return None elif len(items) > 1: @@ -32,13 +36,13 @@ def zero_or_one(items): else: return items[0] -def exactly_one(items): +def exactly_one(items: Union[Sequence[Any], Set[Any]]) -> Any: if len(items) == 0: raise ValueError("Zero items") elif len(items) > 1: raise ValueError("Multiple items") else: - return items[0] + return next(iter(items)) def find_by_value(list_of_dicts, field, value): return [t for t in list_of_dicts if t[field] == value] @@ -47,6 +51,17 @@ def random_string(length): chars = string.ascii_letters + string.digits return "".join([chars[random.randrange(len(chars))] for _ in range(length)]) +# Choose m out of n in random order +def random_choice(m: int, n: int, rnd: Optional[random.Random] = None): + rnd = rnd if rnd is not None else random + a = [x for x in range(n)] + result = [] + for i in range(m): + idx = rnd.randrange(len(a)) + result.append(a[idx]) + a.pop(idx) + return result + def project_dir(*paths): return os.path.abspath(os.path.join(os.path.normpath(os.path.join(os.path.dirname(__file__), *([os.path.pardir]*5))), *paths)) @@ -106,6 +121,7 @@ def disable_noisy_loggers(): logging.getLogger("websockets").setLevel(logging.WARNING) logging.getLogger("web3").setLevel(logging.WARNING) logging.getLogger("asyncio").setLevel(logging.WARNING) + logging.getLogger("eth_hash").setLevel(logging.WARNING) def basic_logging_setup(): import sys @@ -132,6 +148,35 @@ def template_transform(s, d): return s s = s[0:m.start(2)] + d[m[3]] + s[m.end(2):] +def wait_for_enter_key_pressed(): + try: + input("Press ENTER to exit...") + except EOFError: + log = logging.getLogger(__name__) + log.error("Cannot wait for ENTER keypress since standard input is closed. Instead, this program will now wait " + "for 100 years and you will have to kill it manually. If you get this message when running in recent " + "versions of pycharm, enable 'Emulate terminal in output console' in run configuration.") + time.sleep(3155760000) + +def retry(function: Callable, sleep_time: Optional[int] = 5, retries: Optional[int] = 0, + log: Optional[logging.Logger] = None +) -> Callable: + def wrapper(*args, **kwargs): + retries_left = retries + while True: + try: + return function(*args, **kwargs) + except Exception as e: + if retries_left == 0: + raise e + if log is not None: + log.debug("Retriable exception for {}: args: {}, kwargs: {}, exception: {}".format(repr(function), repr(args), repr(kwargs), repr(e))) + if sleep_time > 0: + time.sleep(sleep_time) + retries_left -= 1 + continue + return wrapper + on_peggy2_branch = not os.path.exists(project_dir("smart-contracts", "truffle-config.js")) diff --git a/test/integration/framework/src/siftool/cosmos.py b/test/integration/framework/src/siftool/cosmos.py index f851baeb8d..36f794ddb2 100644 --- a/test/integration/framework/src/siftool/cosmos.py +++ b/test/integration/framework/src/siftool/cosmos.py @@ -7,6 +7,9 @@ Balance = Mapping[str, int] CompatBalance = Union[LegacyBalance, Balance] Address = str +Bank = Mapping[Address, Balance] +BechAddress = str +KeyName = str # Name of key in the keyring def balance_normalize(bal: CompatBalance = None) -> Balance: @@ -71,6 +74,24 @@ def balance_exceeds(bal: Balance, min_changes: Balance) -> bool: return have_all +def balance_sum_by_address(*maps_of_balances: Bank) -> Bank: + totals = {} + for item in maps_of_balances: + for address, balance in item.items(): + if address not in totals: + totals[address] = {} + totals[address] = balance_add(totals[address], balance) + return totals + + +_iso_time_patterm = re.compile("(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.)(\\d+)Z$") + +def parse_iso_timestamp(strtime: str): + m = _iso_time_patterm.match(strtime) + assert m + strtime = m[1] + (m[2] + "000")[:3] + "+00:00" + return datetime.datetime.fromisoformat(strtime) + # # This is for Akash, but might be useful for other cosmos-based chains as well. (If not, it should be moved to separate diff --git a/test/integration/framework/src/siftool/eth.py b/test/integration/framework/src/siftool/eth.py index 9ce0bd8a96..5fdfa01d7b 100644 --- a/test/integration/framework/src/siftool/eth.py +++ b/test/integration/framework/src/siftool/eth.py @@ -58,7 +58,7 @@ def web3_wait_for_connection_up(w3_conn: web3.Web3, polling_time: int = 1, timeo raise Exception("Timeout when trying to connect to {}".format(w3_conn.provider.endpoint_uri)) time.sleep(polling_time) -def validate_address_and_private_key(addr: Optional[Address], private_key: PrivateKey +def validate_address_and_private_key(addr: Optional[Address], private_key: Optional[PrivateKey] ) -> Tuple[Address, Optional[PrivateKey]]: a = web3.Web3().eth.account addr = web3.Web3.toChecksumAddress(addr) if addr else None diff --git a/test/integration/framework/src/siftool/hardhat.py b/test/integration/framework/src/siftool/hardhat.py index 84fecdbbf8..a2ab79c370 100644 --- a/test/integration/framework/src/siftool/hardhat.py +++ b/test/integration/framework/src/siftool/hardhat.py @@ -115,8 +115,11 @@ def __npx_hardhat(self, args, npx_env: Optional[Mapping[str, str]] = None, pipe: return self.hardhat.project.npx(args, cwd=self.hardhat.project.smart_contracts_dir, env=env, pipe=pipe) class HardhatAbiProvider: - def __init__(self, cmd, deployed_contract_addresses): + def __init__(self, cmd: Command, abi_files_root: str, deployed_contract_addresses: Mapping[str, eth.Address]): + assert abi_files_root + assert deployed_contract_addresses self.cmd = cmd + self.abi_files_root = abi_files_root self.deployed_contract_addresses = deployed_contract_addresses def get_descriptor(self, sc_name): @@ -131,7 +134,7 @@ def get_descriptor(self, sc_name): "CommissionToken": ["Mocks"], "RandomTrollToken": ["Mocks"], }.get(sc_name, []) + [f"{sc_name}.sol", f"{sc_name}.json"] - path = os.path.join(self.cmd.project.project_dir("smart-contracts/artifacts/contracts"), *relpath) + path = os.path.join(self.abi_files_root, *relpath) tmp = json.loads(self.cmd.read_text_file(path)) abi = tmp["abi"] bytecode = tmp["bytecode"] diff --git a/test/integration/framework/src/siftool/inflate_tokens.py b/test/integration/framework/src/siftool/inflate_tokens.py index 92c0416d79..3421a77e55 100644 --- a/test/integration/framework/src/siftool/inflate_tokens.py +++ b/test/integration/framework/src/siftool/inflate_tokens.py @@ -17,6 +17,7 @@ class InflateTokens: def __init__(self, ctx: test_utils.EnvCtx): self.ctx = ctx + self.wait_for_account_change_timeout = 120 self.excluded_token_symbols = ["erowan"] # TODO peggy1 only # Only transfer this tokens in a batch for Peggy1. See #2397. You would need to adjust this if @@ -173,7 +174,8 @@ def transfer_from_eth_to_sifnode(self, from_eth_addr, to_sif_addr, tokens_to_tra previous_block = self.ctx.eth.w3_conn.eth.block_number self.ctx.advance_blocks() log.info("Ethereum blocks advanced by {}".format(self.ctx.eth.w3_conn.eth.block_number - previous_block)) - self.ctx.wait_for_sif_balance_change(to_sif_addr, sif_balances_before, min_changes=sent_amounts, polling_time=5) + self.ctx.sifnode.wait_for_balance_change(to_sif_addr, sif_balances_before, min_changes=sent_amounts, + polling_time=5, timeout=0, change_timeout=self.wait_for_account_change_timeout) # Distributes from intermediate_sif_account to each individual account def distribute_tokens_to_wallets(self, from_sif_account, tokens_to_transfer, amount_in_tokens, target_sif_accounts, amount_eth_gwei): @@ -186,13 +188,14 @@ def distribute_tokens_to_wallets(self, from_sif_account, tokens_to_transfer, amo remaining = send_amounts while remaining: batch_size = len(remaining) - if (self.max_sifnoded_batch_size > 0) and (batch_size > self.max_sifnoded_batch_size): - batch_size = self.max_sifnoded_batch_size + if (self.ctx.sifnode.max_send_batch_size > 0) and (batch_size > self.ctx.sifnode.max_send_batch_size): + batch_size = self.ctx.sifnode.max_send_batch_size batch = remaining[:batch_size] remaining = remaining[batch_size:] sif_balance_before = self.ctx.get_sifchain_balance(sif_acct) self.ctx.send_from_sifchain_to_sifchain(from_sif_account, sif_acct, batch) - self.ctx.wait_for_sif_balance_change(sif_acct, sif_balance_before, min_changes=batch, polling_time=2) + self.ctx.sifnode.wait_for_balance_change(sif_acct, sif_balance_before, min_changes=batch, + polling_time=2, timeout=0, change_timeout=self.wait_for_account_change_timeout) progress_current += batch_size log.debug("Distributing tokens to wallets: {:0.0f}% done".format((progress_current/progress_total) * 100)) @@ -225,7 +228,7 @@ def transfer(self, requested_tokens: Sequence[TokenDict], token_amount: int, # Calculate how much rowan we need to fund intermediate account with. This is only an estimation at this point. # We need to take into account that we might need to break transfers in batches. The number of tokens is the # number of ERC20 tokens plus one for ETH, rounded up. 5 is a safety factor - number_of_batches = 1 if self.max_sifnoded_batch_size == 0 else (len(requested_tokens) + 1) // self.max_sifnoded_batch_size + 1 + number_of_batches = 1 if self.ctx.sifnode.max_send_batch_size == 0 else (len(requested_tokens) + 1) // self.ctx.sifnode.max_send_batch_size + 1 fund_rowan = [5 * test_utils.sifnode_funds_for_transfer_peggy1 * n_accounts * number_of_batches, "rowan"] log.debug("Estimated number of batches needed to transfer tokens from intermediate sif account to target sif wallet: {}".format(number_of_batches)) log.debug("Estimated rowan funding needed for intermediate account: {}".format(fund_rowan)) @@ -292,7 +295,7 @@ def transfer_eth(self, from_eth_addr: eth.Address, amount_gwei: int, target_sif_ def run(*args): - # This script should be run with ENV_FILE set to a file containing definitions for OPERATOR_ADDRESS, + # This script should be run with SIFTOOL_ENV_FILE set to a file containing definitions for OPERATOR_ADDRESS, # ROWAN_SOURCE eth. Depending on if you're running it on Peggy1 or Peggy2 the format might be different. # See get_env_ctx() for details. assert not on_peggy2_branch, "Not supported yet on peggy2.0 branch" diff --git a/test/integration/framework/src/siftool/main.py b/test/integration/framework/src/siftool/main.py index 6b1bd302e6..9c6492b285 100755 --- a/test/integration/framework/src/siftool/main.py +++ b/test/integration/framework/src/siftool/main.py @@ -2,7 +2,7 @@ import sys import time -from siftool import test_utils, run_env, cosmos +from siftool import test_utils, run_env, cosmos, diagnostics, sifchain, frontend, test_utils2 from siftool.run_env import Integrator, UIStackEnvironment, Peggy2Environment, IBCEnvironment, IntegrationTestsEnvironment from siftool.project import Project, killall, force_kill_processes from siftool.common import * @@ -33,7 +33,12 @@ def main(argv): project = cmd.project log = siftool_logger(__name__) argparser = argparse.ArgumentParser() - if what == "project-init": + if what == "venv": + log.info("Using Python {}.{}.{}".format(sys.version_info.major, sys.version_info.minor, sys.version_info.micro)) + log.info("sys.path={}".format(repr(sys.path))) + log.info("Project root: {}".format(project.project_dir())) + log.info("Project virtual environment location: {}".format(project.get_project_venv_dir())) + elif what == "project-init": project.init() elif what == "clean": project.clean() @@ -183,6 +188,28 @@ def main(argv): signer_addr, signer_private_key = siftool.eth.web3_create_account() ethereum_chain_id = 9999 geth.init(ethereum_chain_id, [signer_addr], datadir) + elif what == "dump-block-times": + argparser.add_argument("--node", type=str, required=True) + argparser.add_argument("--file", type=str, required=True) + argparser.add_argument("--from-block", type=int) + argparser.add_argument("--to-block", type=int) + args = argparser.parse_args(argv[1:]) + sifnoded = sifchain.Sifnoded(cmd, node=args.node) + from_block = args.from_block if args.from_block is not None else 1 + to_block = args.to_block if args.to_block is not None else sifnoded.get_current_block() + block_times = diagnostics.get_block_times(sifnoded, from_block, to_block) + block_times = [(block_times[i][0], (block_times[i][1] - block_times[i - 1][1]).total_seconds()) + for i in range(1, len(block_times))] + lines = ["{},{:.3f}".format(t[0], t[1]) for t in block_times] + with open(args.file, "w") as f: + f.write(joinlines(lines)) + elif what == "create-wallets": + argparser.add_argument("count", type=int) + argparser.add_argument("--home", type=str) + args = argparser.parse_args(argv[1:]) + test_utils2.PredefinedWallets.create(cmd, args.count, args.home) + elif what == "run-ui": + frontend.run_local_ui(cmd) else: raise Exception("Missing/unknown command") diff --git a/test/integration/framework/src/siftool/project.py b/test/integration/framework/src/siftool/project.py index 11c13acdbe..69baff9a85 100644 --- a/test/integration/framework/src/siftool/project.py +++ b/test/integration/framework/src/siftool/project.py @@ -124,8 +124,10 @@ def run_peggy2_js_tests(self): pass # Top-level "make install" should build everything, such as after git clone. If it does not, it's a bug. - def make_all(self): - self.cmd.execst(["make"], cwd=project_dir(), pipe=False) + # "Official" way is "make clean && make install" + def make_all(self, output_dir: Optional[str] = None): + env = None if output_dir is None else {"GOBIN": output_dir} + self.cmd.execst(["make", "install"], cwd=project_dir(), pipe=False, env=env) # IntegrationEnvironment # TODO Merge @@ -139,9 +141,10 @@ def make_go_binaries(self): # TODO Merge # Main Makefile requires GOBIN to be set to an absolute path. Compiled executables ebrelayer, sifgen and # sifnoded will be written there. The directory will be created if it doesn't exist yet. - def make_go_binaries_2(self): + def make_go_binaries_2(self, feature_toggles: Optional[Iterable[str]] = None): # Original: cd smart-contracts; make -C .. install - self.cmd.execst(["make", "install"], cwd=project_dir(), pipe=False) + extra_env = {feature: "1" for feature in feature_toggles} + self.cmd.execst(["make", "install"], cwd=project_dir(), pipe=False, env=extra_env) def install_smart_contracts_dependencies(self): self.cmd.execst(["make", "clean-smartcontracts"], cwd=self.smart_contracts_dir) # = rm -rf build .openzeppelin @@ -169,7 +172,7 @@ def get_peruser_config_dir(self): return self.cmd.get_user_home(".config", "siftool") def get_user_env_vars(self): - env_file = os.environ["ENV_FILE"] + env_file = os.environ["SIFTOOL_ENV_FILE"] return json.loads(self.cmd.read_text_file(env_file)) def read_peruser_config_file(self, name): @@ -197,7 +200,8 @@ def __rm_hardhat_compiled_files(self): def __rm_peggy2_compiled_go_stubs(self): # Peggy2: generated Go stubs (by smart-contracts/Makefile) - self.__rm(project_dir("cmd", "ebrelayer", "contract", "generated")) + if on_peggy2_branch: + self.__rm(project_dir("cmd", "ebrelayer", "contract", "generated")) self.__rm(project_dir(".proto-gen")) def __rm_run_env_files(self): @@ -302,9 +306,11 @@ def npm_install(self, path: str, disable_cache: bool = False): cache = cache[:max_cache_items] self.cmd.write_text_file(cache_index, json.dumps(cache)) + def get_project_venv_dir(self): + return project_dir("test", "integration", "framework", "venv") + def project_python(self): - project_venv_dir = project_dir("test", "integration", "framework", "venv") - return os.path.join(project_venv_dir, "bin", "python3") + return os.path.join(self.get_project_venv_dir(), "bin", "python3") def _ensure_build_dirs(self): for d in ["build", "build/repos", "build/generated"]: @@ -398,6 +404,7 @@ def reset(self): self.__rm_sifnode_binaries() self.__rm(os.path.join(self.cmd.get_user_home(), ".sifnoded")) self.__rm_peggy2_compiled_go_stubs() + self.__rm_run_env_files() self.npm_install(self.smart_contracts_dir) self.make_go_binaries_2() diff --git a/test/integration/framework/src/siftool/run_env.py b/test/integration/framework/src/siftool/run_env.py index b98204d3bb..4a88a65bf3 100644 --- a/test/integration/framework/src/siftool/run_env.py +++ b/test/integration/framework/src/siftool/run_env.py @@ -143,7 +143,7 @@ def sifchain_init_integration(self, sifnode, validator_moniker, validator_mnemon # This was deleted in commit f00242302dd226bc9c3060fb78b3de771e3ff429 from sifchain_start_daemon.sh because # it was not working. But we assume that we want to keep it. - sifnode.sifnoded_exec(["add-genesis-validators", valoper], sifnoded_home=sifnode.home) + sifnode.sifnoded_exec(["add-genesis-validators", valoper] + sifnode._home_args()) # Add sifnodeadmin to ~/.sifnoded sifnode0 = Sifnoded(self) @@ -157,7 +157,7 @@ def sifchain_init_integration(self, sifnode, validator_moniker, validator_mnemon return adminuser_addr - def sifnoded_peggy2_init_validator(self, sifnode, validator_moniker, validator_mnemonic, evm_network_descriptor, validator_power, chain_dir_base): + def sifnoded_peggy2_init_validator(self, sifnode, validator_moniker, validator_mnemonic, evm_network_descriptor, validator_power): # Add validator key to test keyring # This effectively copies key for validator_moniker from what sifgen creates in /tmp/sifnodedNetwork/validators # to ~/.sifnoded (note absence of explicit sifnoded_home, therefore it's ~/.sifnoded) @@ -194,10 +194,10 @@ def sifgen_create_network(self, chain_id: str, validator_count: int, networks_di (["--mint-amount", cosmos.balance_format(mint_amount)] if mint_amount else []) self.execst(args) - def wait_for_sif_account(self, netdef_json, validator1_address): - # TODO Replace with test_utilities.wait_for_sif_account / wait_for_sif_account_up - return self.execst(["python3", os.path.join(self.project.test_integration_dir, "src/py/wait_for_sif_account.py"), - netdef_json, validator1_address], env={"USER1ADDR": "nothing"}) + # def wait_for_sif_account(self, netdef_json, validator1_address): + # # TODO Replace with test_utilities.wait_for_sif_account / wait_for_sif_account_up + # return self.execst(["python3", os.path.join(self.project.test_integration_dir, "src/py/wait_for_sif_account.py"), + # netdef_json, validator1_address], env={"USER1ADDR": "nothing"}) def wait_for_sif_account_up(self, address: cosmos.Address, tcp_url: str = None, timeout: int = 90): # TODO Deduplicate: this is also in run_ebrelayer() @@ -319,7 +319,7 @@ def stack_save_snapshot(self): self.cmd.execst(["sifnoded", "validate-genesis"]) log.info("Starting test chain...") - sifnoded_proc = self.cmd.sifnoded_start(minimum_gas_prices=[0.5, ROWAN]) # TODO sifnoded_home=??? + sifnoded_proc = self.cmd.sifnoded_start(minimum_gas_prices=(0.5, ROWAN)) # TODO sifnoded_home=??? # sifnoded must be up before continuing self.cmd.sif_wait_up("localhost", 1317) @@ -608,7 +608,7 @@ def run(self): # Start sifnoded sifnoded_proc = sifnode.sifnoded_start(tcp_url=self.tcp_url, minimum_gas_prices=(0.5, ROWAN), - log_file=sifnoded_log_file) + log_file=sifnoded_log_file, trace=True) # TODO: wait for sifnoded to come up before continuing # in sifchain_start_daemon.sh: "sleep 10" @@ -690,7 +690,7 @@ def run_ebrelayer(self, netdef_json, validator1_address, validator_moniker, vali # TODO Deduplicate while not self.cmd.tcp_probe_connect("localhost", 26657): time.sleep(1) - self.cmd.wait_for_sif_account(netdef_json, validator1_address) + self.cmd.wait_for_sif_account_up(validator1_address) time.sleep(10) self.remove_and_add_sifnoded_keys(validator_moniker, validator_mnemonic) # Creates ~/.sifnoded/keyring-tests/xxxx.address ebrelayer_proc = Ebrelayer(self.cmd).init(self.tcp_url, self.ethereum_websocket_address, bridge_registry_sc_addr, @@ -759,7 +759,8 @@ def restart_processes(self): networks_dir = project_dir("deploy/networks") chaindir = os.path.join(networks_dir, f"validators/{self.chainnet}/{validator_moniker}") sifnoded_home = os.path.join(chaindir, ".sifnoded") - sifnoded_proc = self.cmd.sifnoded_start(tcp_url=self.tcp_url, minimum_gas_prices=[0.5, ROWAN], sifnoded_home=sifnoded_home) + sifnoded_proc = self.cmd.sifnoded_start(tcp_url=self.tcp_url, minimum_gas_prices=(0.5, ROWAN), + sifnoded_home=sifnoded_home, trace=True) bridge_token_sc_addr, bridge_registry_sc_addr, bridge_bank_sc_addr = \ self.cmd.get_bridge_smart_contract_addresses(self.network_id) @@ -1119,7 +1120,7 @@ def init_sifchain(self, sifnoded_network_dir: str, sifnoded_log_file: TextIO, evm_network_descriptor = 1 # TODO Why not hardhat_chain_id? sifnoded_home = os.path.join(chain_dir_base, validator_moniker, ".sifnoded") sifnode = Sifnoded(self.cmd, home=sifnoded_home) - self.cmd.sifnoded_peggy2_init_validator(sifnode, validator_moniker, validator_mnemonic, evm_network_descriptor, validator_power, chain_dir_base) + self.cmd.sifnoded_peggy2_init_validator(sifnode, validator_moniker, validator_mnemonic, evm_network_descriptor, validator_power) # TODO Needs to be fixed when we support more than 1 validator validator0 = exactly_one(validators) @@ -1127,7 +1128,7 @@ def init_sifchain(self, sifnoded_network_dir: str, sifnoded_log_file: TextIO, validator0_address = validator0["address"] chain_dir = os.path.join(chain_dir_base, validator0["moniker"]) - sifnode = Sifnoded(self.cmd, home=validator0_home) + sifnode = Sifnoded(self.cmd, home=validator0_home, chain_id=self.chain_id) # Create an ADMIN account on sifnode with name admin_account_name (e.g. "sifnodeadmin") admin_account_address = sifnode.peggy2_add_account(admin_account_name, admin_account_mint_amounts, is_admin=True) @@ -1162,7 +1163,7 @@ def init_sifchain(self, sifnoded_network_dir: str, sifnoded_log_file: TextIO, gas_prices = (0.5, ROWAN) # @TODO Detect if sifnoded is already running, for now it fails silently and we wait forever in wait_for_sif_account_up sifnoded_exec_args = sifnode.build_start_cmd(tcp_url=tcp_url, minimum_gas_prices=gas_prices, - log_format_json=True) + log_format_json=True, log_level="debug") sifnoded_proc = self.cmd.spawn_asynchronous_process(sifnoded_exec_args, log_file=sifnoded_log_file) time_before = time.time() @@ -1347,7 +1348,7 @@ def format_sif_account(sif_account): # TODO Inconsistent format of deployed smart contract addresses (this was intentionally carried over from # devenv to preserve compatibility with devenv users) - # TODO Convert to out "unified" json file format + # TODO Convert to our "unified" json file format # TODO Do we want "0x" prefixes here for private keys? dot_env = dict_merge({ diff --git a/test/integration/framework/src/siftool/sifchain.py b/test/integration/framework/src/siftool/sifchain.py index 9f0daa7973..ed3a7b8b9f 100644 --- a/test/integration/framework/src/siftool/sifchain.py +++ b/test/integration/framework/src/siftool/sifchain.py @@ -1,9 +1,11 @@ import base64 +import contextlib import json import time import grpc import re -import web3 +import toml +import web3 # TODO Remove dependency from typing import Mapping, Any, Tuple, AnyStr from siftool import command, cosmos, eth from siftool.common import * @@ -44,7 +46,11 @@ def balance_delta(balances1: cosmos.Balance, balances2: cosmos.Balance) -> cosmo def is_cosmos_native_denom(denom: str) -> bool: """Returns true if denom is a native cosmos token (Rowan, ibc) that was not imported using Peggy""" - return not str.startswith(denom, "sifBridge") + if on_peggy2_branch: + return not str.startswith(denom, "sifBridge") + else: + return (denom == ROWAN) or str.startswith(denom, "ibc/") + def ondemand_import_generated_protobuf_sources(): global cosmos_pb @@ -52,78 +58,465 @@ def ondemand_import_generated_protobuf_sources(): import cosmos.tx.v1beta1.service_pb2 as cosmos_pb import cosmos.tx.v1beta1.service_pb2_grpc as cosmos_pb_grpc +def mnemonic_to_address(cmd: command.Command, mnemonic: Iterable[str]): + tmpdir = cmd.mktempdir() + sifnode = Sifnoded(cmd, home=tmpdir) + try: + return sifnode.keys_add("tmp", mnemonic)["address"] + finally: + cmd.rmdir(tmpdir) + +def sifnoded_parse_output_lines(stdout: str) -> Mapping: + # TODO Some values are like '""' + pat = re.compile("^(.*?): (.*)$") + result = {} + for line in stdout.splitlines(): + m = pat.match(line) + result[m[1]] = m[2] + return result + +def format_pubkey(pubkey: JsonDict) -> str: + return "{{\"@type\":\"{}\",\"key\":\"{}\"}}".format(pubkey["@type"], pubkey["key"]) + +def format_peer_address(node_id: str, hostname: str, p2p_port: int) -> str: + return "{}@{}:{}".format(node_id, hostname, p2p_port) + +def format_node_url(hostname: str, p2p_port: int) -> str: + return "tcp://{}:{}".format(hostname, p2p_port) + +# Use this to check the output of sifnoded commands if transaction was successful. This can only be used with +# "--broadcast-mode block" when the stack trace is returned in standard output (json/yaml) field `raw_log`. +# @TODO Sometimes, raw_log is also json file, c.f. Sifnoded.send() +def check_raw_log(res: JsonDict): + if res["code"] == 0: + assert res["height"] != 0 + return + lines = res["raw_log"].splitlines() + last_line = lines[-1] + raise SifnodedException(last_line) + +def create_rewards_descriptor(rewards_period_id: str, start_block: int, end_block: int, + multipliers: Iterable[Tuple[str, int]], allocation: int, reward_period_default_multiplier: float, + reward_period_distribute: bool, reward_period_mod: int +) -> RewardsParams: + return { + "reward_period_id": rewards_period_id, + "reward_period_start_block": start_block, + "reward_period_end_block": end_block, + "reward_period_allocation": str(allocation), + "reward_period_pool_multipliers": [{ + "pool_multiplier_asset": denom, + "multiplier": str(multiplier) + } for denom, multiplier in multipliers], + "reward_period_default_multiplier": str(reward_period_default_multiplier), + "reward_period_distribute": reward_period_distribute, + "reward_period_mod": reward_period_mod + } + +def create_lppd_params(start_block: int, end_block: int, rate: float, mod: int) -> LPPDParams: + return { + "distribution_period_block_rate": str(rate), + "distribution_period_start_block": start_block, + "distribution_period_end_block": end_block, + "distribution_period_mod": mod + } + class Sifnoded: - def __init__(self, cmd, home: Optional[str] = None): + def __init__(self, cmd, /, home: Optional[str] = None, node: Optional[str] = None, chain_id: Optional[str] = None, + binary: Optional[str] = None + ): self.cmd = cmd - self.binary = "sifnoded" + self.binary = binary or "sifnoded" self.home = home + self.node = node + self.chain_id = chain_id self.keyring_backend = "test" + + self.fees = sif_tx_fee_in_rowan + self.gas = None + self.gas_adjustment = 1.5 + self.gas_prices = "0.5rowan" + + # Some transactions such as adding tokens to token registry or adding liquidity pools need a lot of gas and + # will exceed the default implicit value of 200000. According to Brandon the problem is in the code that loops + # over existing entries resulting in gas that is proportional to the number of existing entries. + self.high_gas = 200000 * 10000 + + # Firing transactions with "sifnoded tx bank send" in rapid succession does not work. This is currently a + # known limitation of Cosmos SDK, see https://github.com/cosmos/cosmos-sdk/issues/4186 + # Instead, we take advantage of batching multiple denoms to single account with single send command (amounts + # separated by by comma: "sifnoded tx bank send ... 100denoma,100denomb,100denomc") and wait for destination + # account to show changes for all denoms after each send. But also batches don't work reliably if they are too + # big, so we limit the maximum batch size here. + self.max_send_batch_size = 5 + + self.broadcast_mode = None # self.sifnoded_burn_gas_cost = 16 * 10**10 * 393000 # see x/ethbridge/types/msgs.go for gas # self.sifnoded_lock_gas_cost = 16 * 10**10 * 393000 + # TODO Rename, this is now shared among all callers of _paged_read() + self.get_balance_default_retries = 0 + + # Defaults + self.wait_for_balance_change_default_timeout = 90 + self.wait_for_balance_change_default_change_timeout = None + self.wait_for_balance_change_default_polling_time = 2 - def init(self, moniker, chain_id): - args = [self.binary, "init", moniker, "--chain-id", chain_id] + # Returns what looks like genesis file data + def init(self, moniker): + args = [self.binary, "init", moniker] + self._home_args() + self._chain_id_args() res = self.cmd.execst(args) - return json.loads(res[2]) # output is on stderr + return json.loads(stderr(res)) def keys_list(self): - args = ["keys", "list", "--output", "json"] - res = self.sifnoded_exec(args, keyring_backend=self.keyring_backend, sifnoded_home=self.home) + args = ["keys", "list", "--output", "json"] + self._home_args() + self._keyring_backend_args() + res = self.sifnoded_exec(args) return json.loads(stdout(res)) def keys_show(self, name, bech=None): args = ["keys", "show", name] + \ - (["--bech", bech] if bech else []) - res = self.sifnoded_exec(args, keyring_backend=self.keyring_backend, sifnoded_home=self.home) + (["--bech", bech] if bech else []) + \ + self._home_args() + \ + self._keyring_backend_args() + res = self.sifnoded_exec(args) return yaml_load(stdout(res)) - def get_val_address(self, moniker): - res = self.sifnoded_exec(["keys", "show", "-a", "--bech", "val", moniker], keyring_backend=self.keyring_backend, sifnoded_home=self.home) + def get_val_address(self, moniker) -> cosmos.BechAddress: + args = ["keys", "show", "-a", "--bech", "val", moniker] + self._home_args() + self._keyring_backend_args() + res = self.sifnoded_exec(args) expected = exactly_one(stdout_lines(res)) result = exactly_one(self.keys_show(moniker, bech="val"))["address"] assert result == expected return result - def keys_add(self, moniker: str, mnemonic: Optional[Iterable[str]] = None) -> Mapping[str, Any]: + def _keys_add(self, moniker: str, mnemonic: Optional[Iterable[str]] = None) -> Tuple[JsonDict, Iterable[str]]: if mnemonic is None: - res = self.sifnoded_exec(["keys", "add", moniker], keyring_backend=self.keyring_backend, - sifnoded_home=self.home, stdin=["y"]) - _unused_mnemonic = stderr(res).splitlines()[-1].split(" ") + args = ["keys", "add", moniker] + self._home_args() + self._keyring_backend_args() + res = self.sifnoded_exec(args, stdin=["y"]) + mnemonic = stderr(res).splitlines()[-1].split(" ") else: - res = self.sifnoded_exec(["keys", "add", moniker, "--recover"], keyring_backend=self.keyring_backend, - sifnoded_home=self.home, stdin=[" ".join(mnemonic)]) + args = ["keys", "add", moniker, "--recover"] + self._home_args() + self._keyring_backend_args() + res = self.sifnoded_exec(args, stdin=[" ".join(mnemonic)]) + account = exactly_one(yaml_load(stdout(res))) + return account, mnemonic + + def keys_add(self, moniker: Optional[str] = None, mnemonic: Optional[Iterable[str]] = None) -> JsonDict: + moniker = self.__fill_in_moniker(moniker) + account, _ = self._keys_add(moniker, mnemonic=mnemonic) + return account + + def generate_mnemonic(self) -> List[str]: + args = ["keys", "mnemonic"] + self._home_args() + self._keyring_backend_args() + res = self.sifnoded_exec(args) + return exactly_one(stderr(res).splitlines()).split(" ") + + def create_addr(self, moniker: Optional[str] = None, mnemonic: Optional[Iterable[str]] = None) -> cosmos.Address: + return self.keys_add(moniker=moniker, mnemonic=mnemonic)["address"] + + def keys_add_multisig(self, moniker: Optional[str], signers: Iterable[cosmos.KeyName], multisig_threshold: int): + moniker = self.__fill_in_moniker(moniker) + args = ["keys", "add", moniker, "--multisig", ",".join(signers), "--multisig-threshold", + str(multisig_threshold)] + self._home_args() + self._keyring_backend_args() + res = self.sifnoded_exec(args) account = exactly_one(yaml_load(stdout(res))) return account + def __fill_in_moniker(self, moniker): + return moniker if moniker else "temp-{}".format(random_string(10)) + def keys_delete(self, name: str): - self.cmd.execst(["sifnoded", "keys", "delete", name, "--keyring-backend", self.keyring_backend], stdin=["y"], check_exit=False) + self.cmd.execst(["sifnoded", "keys", "delete", name] + self._home_args() + self._keyring_backend_args(), + stdin=["y"], check_exit=False) + + def query_account(self, addr: cosmos.Address) -> JsonDict: + args = ["query", "auth", "account", addr, "--output", "json"] + self._node_args() + self._chain_id_args() + res = self.sifnoded_exec(args) + return json.loads(stdout(res)) + + def get_acct_seq(self, addr: cosmos.Address) -> Tuple[int, int]: + account = self.query_account(addr) + account_number = account["account_number"] + account_sequence = int(account["sequence"]) + return account_number, account_sequence def add_genesis_account(self, sifnodeadmin_addr: cosmos.Address, tokens: cosmos.Balance): tokens_str = cosmos.balance_format(tokens) - self.sifnoded_exec(["add-genesis-account", sifnodeadmin_addr, tokens_str], sifnoded_home=self.home) + self.sifnoded_exec(["add-genesis-account", sifnodeadmin_addr, tokens_str] + self._home_args() + self._keyring_backend_args()) - def add_genesis_validators(self, address: cosmos.Address): - args = ["sifnoded", "add-genesis-validators", address] - res = self.cmd.execst(args) + # TODO Obsolete + def add_genesis_account_directly_to_existing_genesis_json(self, + extra_balances: Mapping[cosmos.Address, cosmos.Balance] + ): + genesis = self.load_genesis_json() + self.add_accounts_to_existing_genesis(genesis, extra_balances) + self.save_genesis_json(genesis) + + def add_accounts_to_existing_genesis(self, genesis: JsonDict, extra_balances: Mapping[cosmos.Address, cosmos.Balance]): + bank = genesis["app_state"]["bank"] + # genesis.json uses a bit different structure for balances so we need to convert to and from our balances. + # Whatever is in extra_balances will be added to the existing amounts. + # We must also update supply which must be the sum of all balances. We assume that it initially already is. + # Cosmos SDK wants coins to be sorted or it will panic during chain initialization. + balances = {b["address"]: {c["denom"]: int(c["amount"]) for c in b["coins"]} for b in bank["balances"]} + supply = {b["denom"]: int(b["amount"]) for b in bank["supply"]} + accounts = genesis["app_state"]["auth"]["accounts"] + for addr, bal in extra_balances.items(): + b = cosmos.balance_add(balances.get(addr, {}), bal) + balances[addr] = b + supply = cosmos.balance_add(supply, bal) + accounts.extend([{ + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": addr, + "pub_key": None, + "account_number": "0", + "sequence": "0" + } for addr in set(balances).difference(set(x["address"] for x in accounts))]) + bank["balances"] = [{"address": a, "coins": [{"denom": d, "amount": str(c[d])} for d in sorted(c)]} for a, c in balances.items()] + bank["supply"] = [{"denom": d, "amount": str(supply[d])} for d in sorted(supply)] + + def load_genesis_json(self) -> JsonDict: + genesis_json_path = os.path.join(self.get_effective_home(), "config", "genesis.json") + return json.loads(self.cmd.read_text_file(genesis_json_path)) + + def save_genesis_json(self, genesis: JsonDict): + genesis_json_path = os.path.join(self.get_effective_home(), "config", "genesis.json") + self.cmd.write_text_file(genesis_json_path, json.dumps(genesis)) + + def load_app_toml(self) -> JsonDict: + app_toml_path = os.path.join(self.get_effective_home(), "config", "app.toml") + with open(app_toml_path, "r") as app_toml_file: + return toml.load(app_toml_file) + + def save_app_toml(self, data: JsonDict): + app_toml_path = os.path.join(self.get_effective_home(), "config", "app.toml") + with open(app_toml_path, "w") as app_toml_file: + app_toml_file.write(toml.dumps(data)) + + def load_config_toml(self) -> JsonDict: + config_toml_path = os.path.join(self.get_effective_home(), "config", "config.toml") + with open(config_toml_path, "r") as config_toml_file: + return toml.load(config_toml_file) + + def save_config_toml(self, data: JsonDict): + config_toml_path = os.path.join(self.get_effective_home(), "config", "config.toml") + with open(config_toml_path, "w") as config_toml_file: + config_toml_file.write(toml.dumps(data)) + + def enable_rpc_port(self): + app_toml = self.load_app_toml() + app_toml["api"]["enable"] = True + app_toml["api"]["address"] = format_node_url(ANY_ADDR, SIFNODED_DEFAULT_API_PORT) + self.save_app_toml(app_toml) + + def get_effective_home(self) -> str: + return self.home if self.home is not None else self.cmd.get_user_home(".sifnoded") + + def add_genesis_clp_admin(self, address: cosmos.Address): + args = ["add-genesis-clp-admin", address] + self._home_args() + self._keyring_backend_args() + self.sifnoded_exec(args) + + # Modifies genesis.json and adds the address to .oracle.address_whitelist array. + def add_genesis_validators(self, address: cosmos.BechAddress): + args = ["add-genesis-validators", address] + self._home_args() + self._keyring_backend_args() + res = self.sifnoded_exec(args) return res # At the moment only on future/peggy2 branch, called from PeggyEnvironment - def add_genesis_validators_peggy(self, evm_network_descriptor: int, valoper: str, validator_power: int): - self.sifnoded_exec(["add-genesis-validators", str(evm_network_descriptor), valoper, str(validator_power)], - sifnoded_home=self.home) + def add_genesis_validators_peggy(self, evm_network_descriptor: int, valoper: cosmos.BechAddress, validator_power: int): + assert on_peggy2_branch + args = ["add-genesis-validators", str(evm_network_descriptor), valoper, str(validator_power)] + \ + self._home_args() + self.sifnoded_exec(args) def set_genesis_oracle_admin(self, address): - self.sifnoded_exec(["set-genesis-oracle-admin", address], sifnoded_home=self.home) + self.sifnoded_exec(["set-genesis-oracle-admin", address] + self._home_args() + self._keyring_backend_args()) def set_genesis_token_registry_admin(self, address): - self.sifnoded_exec(["set-genesis-token-registry-admin", address], sifnoded_home=self.home) + self.sifnoded_exec(["set-genesis-token-registry-admin", address] + self._home_args()) def set_genesis_whitelister_admin(self, address): - self.sifnoded_exec(["set-genesis-whitelister-admin", address], sifnoded_home=self.home) + self.sifnoded_exec(["set-genesis-whitelister-admin", address] + self._home_args() + self._keyring_backend_args()) def set_gen_denom_whitelist(self, denom_whitelist_file): - self.sifnoded_exec(["set-gen-denom-whitelist", denom_whitelist_file], sifnoded_home=self.home) + self.sifnoded_exec(["set-gen-denom-whitelist", denom_whitelist_file] + self._home_args()) + + def tendermint_show_node_id(self) -> str: + args = ["tendermint", "show-node-id"] + self._home_args() + res = self.sifnoded_exec(args) + return exactly_one(stdout(res).splitlines()) + + def tendermint_show_validator(self): + args = ["tendermint", "show-validator"] + self._home_args() + res = self.sifnoded_exec(args) + return json.loads(stdout(res)) + + # self.node ("--node") should point to existing validator (i.e. node 0) which must be up. + # The balance of from_acct (from node 0's perspective) must be greater than the staking amount. + # amount must be a single denom, and must denominated as per config/app_state.toml::staking.params.bond_denom + # pubkey must be from "tendermint show validator", NOT from "keys add" + def staking_create_validator(self, amount: cosmos.Balance, pubkey: JsonDict, moniker: str, commission_rate: float, + commission_max_rate: float, commission_max_change_rate: float, min_self_delegation: int, + from_acct: cosmos.Address, broadcast_mode: Optional[str] = None + ) -> JsonDict: + assert len(amount) == 1 # Maybe not? We haven't seen staking with more than one denom yet... + assert cosmos.balance_exceeds(self.get_balance(from_acct), amount) + assert pubkey["@type"] == "/cosmos.crypto.ed25519.PubKey" + args = ["tx", "staking", "create-validator", "--amount", cosmos.balance_format(amount), "--pubkey", + format_pubkey(pubkey), "--moniker", moniker, "--commission-rate", str(commission_rate), + "--commission-max-rate", str(commission_max_rate), "--commission-max-change-rate", + str(commission_max_change_rate), "--min-self-delegation", str(min_self_delegation), "--from", from_acct] + \ + self._home_args() + self._chain_id_args() + self._node_args() + self._keyring_backend_args() + \ + self._fees_args() + self._broadcast_mode_args(broadcast_mode) + self._yes_args() + res = self.sifnoded_exec(args) + return yaml_load(stdout(res)) + + def staking_delegate(self, validator_addr, amount: cosmos.Balance, from_addr: cosmos.Balance, + broadcast_mode: Optional[str] = None + ) -> JsonDict: + args = ["tx", "staking", "delegate", validator_addr, cosmos.balance_format(amount), "--from", from_addr] + \ + self._home_args() + self._keyring_backend_args() + self._node_args() + self._chain_id_args() + \ + self._fees_args() + self._fees_args() + self._broadcast_mode_args(broadcast_mode=broadcast_mode) + \ + self._yes_args() + res = self.sifnoded_exec(args) + return yaml_load(stdout(res)) + + def staking_edit_validator(self, commission_rate: float, from_acct: cosmos.Address, + broadcast_mode: Optional[str] = None + ) -> JsonDict: + args = ["tx", "staking", "edit-validator", "--from", from_acct, "--commission-rate", str(commission_rate)] + \ + self._chain_id_args() + self._home_args() + self._node_args() + self._keyring_backend_args() + \ + self._fees_args() + self._broadcast_mode_args(broadcast_mode) + self._yes_args() + res = self.sifnoded_exec(args) + return yaml_load(stdout(res)) + + def query_staking_validators(self) -> JsonObj: + args = ["query", "staking", "validators"] + self._home_args() + self._node_args() + res = self._paged_read(args, "validators") + return res + + # See scripts/ibc/tokenregistration for more information and examples. + # JSON file can be generated with "sifnoded q tokenregistry generate" + def create_tokenregistry_entry(self, symbol: str, sifchain_symbol: str, decimals: int, + permissions: Iterable[str] = None + ) -> TokenRegistryParams: + permissions = permissions if permissions is not None else ["CLP", "IBCEXPORT", "IBCIMPORT"] + upper_symbol = symbol.upper() # Like "USDT" + return { + "decimals": str(decimals), + "denom": sifchain_symbol, + "base_denom": sifchain_symbol, + "path": "", + "ibc_channel_id": "", + "ibc_counterparty_channel_id": "", + "display_name": upper_symbol, + "display_symbol": "", + "network": "", + "address": "", + "external_symbol": upper_symbol, + "transfer_limit": "", + "permissions": list(permissions), + "unit_denom": "", + "ibc_counterparty_denom": "", + "ibc_counterparty_chain_id": "", + } + + # from_sif_addr has to be the address which was used at genesis time for "set-genesis-whitelister-admin". + # You need to have its private key in the test keyring. + # This is needed when creating pools for the token or when doing IBC transfers. + # If you are calling this for several tokens, you need to call it synchronously + # (i.e. wait_for_current_block_to_be_mined(), or broadcast_mode="block"). Otherwise this will silently fail. + # This is used in test_many_pools_and_liquidity_providers.py + def token_registry_register(self, entry: TokenRegistryParams, from_sif_addr: cosmos.Address, + account_seq: Optional[Tuple[int, int]] = None, broadcast_mode: Optional[str] = None + ) -> JsonDict: + # Check that we have the private key in test keyring. This will throw an exception if we don't. + assert self.keys_show(from_sif_addr) + # This command requires a single TokenRegistryEntry, even though the JSON file has "entries" as a list. + # If not: "Error: exactly one token entry must be specified in input file" + token_data = {"entries": [entry]} + with self._with_temp_json_file(token_data) as tmp_registry_json: + args = ["tx", "tokenregistry", "register", tmp_registry_json, "--from", from_sif_addr, "--output", + "json"] + self._home_args() + self._keyring_backend_args() + self._chain_id_args() + \ + self._account_number_and_sequence_args(account_seq) + \ + self._node_args() + self._high_gas_prices_args() + self._broadcast_mode_args(broadcast_mode=broadcast_mode) + \ + self._yes_args() + res = self.sifnoded_exec(args) + res = json.loads(stdout(res)) + # Example of successful output: {"height":"196804","txhash":"C8252E77BCD441A005666A4F3D76C99BD35F9CB49AA1BE44CBE2FFCC6AD6ADF4","codespace":"","code":0,"data":"0A270A252F7369666E6F64652E746F6B656E72656769737472792E76312E4D73675265676973746572","raw_log":"[{\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/sifnode.tokenregistry.v1.MsgRegister\"}]}]}]","logs":[{"msg_index":0,"log":"","events":[{"type":"message","attributes":[{"key":"action","value":"/sifnode.tokenregistry.v1.MsgRegister"}]}]}],"info":"","gas_wanted":"200000","gas_used":"115149","tx":null,"timestamp":""} + if res["raw_log"].startswith("signature verification failed"): + raise Exception(res["raw_log"]) + if res["raw_log"].startswith("failed to execute message"): + raise Exception(res["raw_log"]) + check_raw_log(res) + return res + + def token_registry_register_batch(self, from_sif_addr: cosmos.Address, entries: Iterable[TokenRegistryParams]): + account_number, account_sequence = self.get_acct_seq(from_sif_addr) + token_registry_entries_before = set(e["denom"] for e in self.query_tokenregistry_entries()) + for entry in entries: + res = self.token_registry_register(entry, from_sif_addr, account_seq=(account_number, account_sequence)) + check_raw_log(res) + account_sequence += 1 + self.wait_for_last_transaction_to_be_mined() + token_registry_entries_after = set(e["denom"] for e in self.query_tokenregistry_entries()) + token_registry_entries_added = token_registry_entries_after.difference(token_registry_entries_before) + assert token_registry_entries_added == set(e["denom"] for e in entries), \ + "Some tokenregistry registration have failed" + + def query_tokenregistry_entries(self): + args = ["query", "tokenregistry", "entries"] + self._node_args() + self._chain_id_args() + res = self.sifnoded_exec(args) + return json.loads(stdout(res))["entries"] + + # Creates file config/gentx/gentx-*.json + def gentx(self, name: str, stake: cosmos.Balance, keyring_dir: Optional[str] = None, + commission_rate: Optional[float] = None, commission_max_rate: Optional[float] = None, + commission_max_change_rate: Optional[float] = None\ + ): + # TODO Make chain_id an attribute + args = ["gentx", name, cosmos.balance_format(stake)] + \ + (["--keyring-dir", keyring_dir] if keyring_dir is not None else []) + \ + (["--commission-rate", str(commission_rate)] if commission_rate is not None else []) + \ + (["--commission-max-rate", str(commission_max_rate)] if commission_max_rate is not None else []) + \ + (["--commission-max-change-rate", str(commission_max_change_rate)] if commission_max_change_rate is not None else []) + \ + self._home_args() + self._keyring_backend_args() + self._chain_id_args() + res = self.sifnoded_exec(args) + return exactly_one(stderr(res).splitlines()) + + # Modifies genesis.json and adds .genutil.gen_txs (presumably from config/gentx/gentx-*.json) + def collect_gentx(self) -> JsonDict: + args = ["collect-gentxs"] + self._home_args() # Must not use --keyring-backend + res = self.sifnoded_exec(args) + return json.loads(stderr(res)) + + def validate_genesis(self): + args = ["validate-genesis"] + self._home_args() # Must not use --keyring-backend + res = self.sifnoded_exec(args) + res = exactly_one(stdout(res).splitlines()) + assert res.endswith(" is a valid genesis file") + + # Pause the ethbridge module's Lock/Burn on an evm_network_descriptor + def pause_peggy_bridge(self, admin_account_address) -> List[Mapping[str, Any]]: + return self._set_peggy_brige_pause_status(admin_account_address, True) + + # Unpause the ethbridge module's Lock/Burn on an evm_network_descriptor + def unpause_peggy_bridge(self, admin_account_address) -> List[Mapping[str, Any]]: + return self._set_peggy_brige_pause_status(admin_account_address, False) + + def _set_peggy_brige_pause_status(self, admin_account_address, pause_status: bool) -> List[Mapping[str, Any]]: + args = ["tx", "ethbridge", "set-pause", str(pause_status)] + \ + self._keyring_backend_args() + \ + self._chain_id_args() + self._node_args() + \ + self._fees_args() + \ + ["--from", admin_account_address] + \ + ["--chain-id", self.chain_id] + \ + ["--output", "json"] + \ + self._broadcast_mode_args("block") + \ + self._yes_args() + + res = self.sifnoded_exec(args) + return [json.loads(x) for x in stdout(res).splitlines()] + # At the moment only on future/peggy2 branch, called from PeggyEnvironment # This was split from init_common @@ -152,103 +545,667 @@ def peggy2_token_registry_set_registry(self, registry_path: str, gas_prices: Gas from_account: cosmos.Address, chain_id: str ) -> List[Mapping[str, Any]]: args = ["tx", "tokenregistry", "set-registry", registry_path, "--gas-prices", sif_format_amount(*gas_prices), - "--gas-adjustment", str(gas_adjustment), "--from", from_account, "--chain-id", chain_id, "--output", "json", - "--yes"] - res = self.sifnoded_exec(args, keyring_backend=self.keyring_backend, sifnoded_home=self.home) + "--gas-adjustment", str(gas_adjustment), "--from", from_account, "--chain-id", chain_id, "--output", "json"] + \ + self._home_args() + self._keyring_backend_args() + self._yes_args() + res = self.sifnoded_exec(args) return [json.loads(x) for x in stdout(res).splitlines()] def peggy2_set_cross_chain_fee(self, admin_account_address, network_id, ethereum_cross_chain_fee_token, - cross_chain_fee_base, cross_chain_lock_fee, cross_chain_burn_fee, admin_account_name, chain_id, gas_prices, + cross_chain_fee_base, cross_chain_lock_fee, cross_chain_burn_fee, admin_account_name, gas_prices, gas_adjustment ): args = ["tx", "ethbridge", "set-cross-chain-fee", str(network_id), ethereum_cross_chain_fee_token, str(cross_chain_fee_base), str(cross_chain_lock_fee), - str(cross_chain_burn_fee), "--from", admin_account_name, "--chain-id", chain_id, "--gas-prices", - sif_format_amount(*gas_prices), "--gas-adjustment", str(gas_adjustment), "-y"] - res = self.sifnoded_exec(args, keyring_backend=self.keyring_backend, sifnoded_home=self.home) - return res + str(cross_chain_burn_fee), "--from", admin_account_name, "--gas-prices", sif_format_amount(*gas_prices), + "--gas-adjustment", str(gas_adjustment)] + self._home_args() + self._keyring_backend_args() + \ + self._chain_id_args() + self._yes_args() + return self.sifnoded_exec(args) - def peggy2_update_consensus_needed(self, admin_account_address, hardhat_chain_id, chain_id, consensus_needed): + def peggy2_update_consensus_needed(self, admin_account_address, hardhat_chain_id, consensus_needed): args = ["tx", "ethbridge", "update-consensus-needed", str(hardhat_chain_id), - str(consensus_needed), "--from", admin_account_address, "--chain-id", chain_id, "--gas-prices", - "0.5rowan", "--gas-adjustment", "1.5", "-y"] - res = self.sifnoded_exec(args, keyring_backend=self.keyring_backend, sifnoded_home=self.home) - return res - - def sifnoded_start(self, tcp_url=None, minimum_gas_prices: Optional[GasFees] = None, - log_format_json: bool = False, log_file: Optional[IO] = None + str(consensus_needed), "--from", admin_account_address] + self._home_args() + \ + self._keyring_backend_args() + self._gas_prices_args() + self._chain_id_args() + self._yes_args() + return self.sifnoded_exec(args) + + # TODO Rename tcp_url to rpc_laddr + remove dependency on self.node + def sifnoded_start(self, tcp_url: Optional[str] = None, minimum_gas_prices: Optional[GasFees] = None, + log_format_json: bool = False, log_file: Optional[IO] = None, log_level: Optional[str] = None, + trace: bool = False, p2p_laddr: Optional[str] = None, grpc_address: Optional[str] = None, + grpc_web_address: Optional[str] = None, address: Optional[str] = None ): - sifnoded_exec_args = self.build_start_cmd(tcp_url=tcp_url, minimum_gas_prices=minimum_gas_prices, - log_format_json=log_format_json) + sifnoded_exec_args = self.build_start_cmd(tcp_url=tcp_url, p2p_laddr=p2p_laddr, grpc_address=grpc_address, + grpc_web_address=grpc_web_address, address=address, minimum_gas_prices=minimum_gas_prices, + log_format_json=log_format_json, log_level=log_level, trace=trace) return self.cmd.spawn_asynchronous_process(sifnoded_exec_args, log_file=log_file) - def build_start_cmd(self, tcp_url: Optional[str] = None, minimum_gas_prices: Optional[GasFees] = None, - log_format_json: bool = False, trace: bool = True + # TODO Rename tcp_url to rpc_laddr + remove dependency on self.node + def build_start_cmd(self, tcp_url: Optional[str] = None, p2p_laddr: Optional[str] = None, + grpc_address: Optional[str] = None, grpc_web_address: Optional[str] = None, address: Optional[str] = None, + minimum_gas_prices: Optional[GasFees] = None, log_format_json: bool = False, log_level: Optional[str] = None, + trace: bool = False ): args = [self.binary, "start"] + \ (["--trace"] if trace else []) + \ (["--minimum-gas-prices", sif_format_amount(*minimum_gas_prices)] if minimum_gas_prices is not None else []) + \ (["--rpc.laddr", tcp_url] if tcp_url else []) + \ - (["--log_level", "debug"] if log_format_json else []) + \ + (["--p2p.laddr", p2p_laddr] if p2p_laddr else []) + \ + (["--grpc.address", grpc_address] if grpc_address else []) + \ + (["--grpc-web.address", grpc_web_address] if grpc_web_address else []) + \ + (["--address", address] if address else []) + \ + (["--log_level", log_level] if log_level else []) + \ (["--log_format", "json"] if log_format_json else []) + \ - (["--home", self.home] if self.home else []) + self._home_args() return command.buildcmd(args) - def sifnoded_exec(self, args: List[str], sifnoded_home: Optional[str] = None, - keyring_backend: Optional[str] = None, stdin: Union[str, bytes, Sequence[str], None] = None, - cwd: Optional[str] = None, disable_log: bool = False + def send(self, from_sif_addr: cosmos.Address, to_sif_addr: cosmos.Address, amounts: cosmos.Balance, + account_seq: Optional[Tuple[int, int]] = None, broadcast_mode: Optional[str] = None + ) -> JsonDict: + amounts = cosmos.balance_normalize(amounts) + assert len(amounts) > 0 + # TODO Implement batching (factor it out of inflate_token and put here) + if self.max_send_batch_size > 0: + assert len(amounts) <= self.max_send_batch_size, \ + "Currently only up to {} balances can be send at the same time reliably.".format(self.max_send_batch_size) + amounts_string = cosmos.balance_format(amounts) + args = ["tx", "bank", "send", from_sif_addr, to_sif_addr, amounts_string, "--output", "json"] + \ + self._home_args() + self._keyring_backend_args() + self._chain_id_args() + self._node_args() + \ + self._fees_args() + self._account_number_and_sequence_args(account_seq) + \ + self._broadcast_mode_args(broadcast_mode) + self._yes_args() + res = self.sifnoded_exec(args) + retval = json.loads(stdout(res)) + # raw_log = retval["raw_log"] + # for bad_thing in ["insufficient funds", "signature verification failed"]: + # if bad_thing in raw_log: + # raise Exception(raw_log) + check_raw_log(retval) + return retval + + def send_and_check(self, from_addr: cosmos.Address, to_addr: cosmos.Address, amounts: cosmos.Balance + ) -> cosmos.Balance: + from_balance_before = self.get_balance(from_addr) + assert cosmos.balance_exceeds(from_balance_before, amounts), \ + "Source account has insufficient balance (excluding transaction fee)" + to_balance_before = self.get_balance(to_addr) + expected_balance = cosmos.balance_add(to_balance_before, amounts) + self.send(from_addr, to_addr, amounts) + self.wait_for_balance_change(to_addr, to_balance_before, expected_balance=expected_balance) + from_balance_after = self.get_balance(from_addr) + to_balance_after = self.get_balance(to_addr) + expected_tx_fee = {ROWAN: sif_tx_fee_in_rowan} + assert cosmos.balance_equal(cosmos.balance_sub(from_balance_before, from_balance_after, expected_tx_fee), amounts) + assert cosmos.balance_equal(to_balance_after, expected_balance) + return to_balance_after + + def get_balance(self, sif_addr: cosmos.Address, height: Optional[int] = None, + disable_log: bool = False, retries_on_error: Optional[int] = None, delay_on_error: int = 3 + ) -> cosmos.Balance: + base_args = ["query", "bank", "balances", sif_addr] + tmp_result = self._paged_read(base_args, "balances", height=height, limit=5000, disable_log=disable_log, + retries_on_error=retries_on_error, delay_on_error=delay_on_error) + return {b["denom"]: int(b["amount"]) for b in tmp_result} + + # Unless timed out, this function will exit: + # - if min_changes are given: when changes are greater. + # - if expected_balance is given: when balances are equal to that. + # - if neither min_changes nor expected_balance are given: when anything changes. + # You cannot use min_changes and expected_balance at the same time. + def wait_for_balance_change(self, sif_addr: cosmos.Address, old_balance: cosmos.Balance, + min_changes: cosmos.CompatBalance = None, expected_balance: cosmos.CompatBalance = None, + polling_time: Optional[int] = None, timeout: Optional[int] = None, change_timeout: Optional[int] = None, + disable_log: bool = True + ) -> cosmos.Balance: + polling_time = polling_time if polling_time is not None else self.wait_for_balance_change_default_polling_time + timeout = timeout if timeout is not None else self.wait_for_balance_change_default_timeout + change_timeout = change_timeout if change_timeout is not None else self.wait_for_balance_change_default_change_timeout + assert (min_changes is None) or (expected_balance is None), "Cannot use both min_changes and expected_balance" + log.debug("Waiting for balance to change for account {}...".format(sif_addr)) + min_changes = None if min_changes is None else cosmos.balance_normalize(min_changes) + expected_balance = None if expected_balance is None else cosmos.balance_normalize(expected_balance) + start_time = time.time() + last_change_time = None + last_changed_balance = None + while True: + new_balance = self.get_balance(sif_addr, disable_log=disable_log) + delta = cosmos.balance_sub(new_balance, old_balance) + if expected_balance is not None: + should_return = cosmos.balance_equal(expected_balance, new_balance) + elif min_changes is not None: + should_return = cosmos.balance_exceeds(delta, min_changes) + else: + should_return = not cosmos.balance_zero(delta) + if should_return: + return new_balance + now = time.time() + if (timeout is not None) and (timeout > 0) and (now - start_time > timeout): + raise SifnodedException("Timeout waiting for sif account {} balance to change ({}s)".format(sif_addr, timeout)) + if last_change_time is None: + last_changed_balance = new_balance + last_change_time = now + else: + delta = cosmos.balance_sub(new_balance, last_changed_balance) + if not cosmos.balance_zero(delta): + last_changed_balance = new_balance + last_change_time = now + log.debug("New state detected ({} denoms changed)".format(len(delta))) + if (change_timeout is not None) and (change_timeout > 0) and (now - last_change_time > change_timeout): + raise SifnodedException("Timeout waiting for sif account {} balance to change ({}s)".format(sif_addr, change_timeout)) + time.sleep(polling_time) + + # TODO Refactor - consolidate with test_inflate_tokens.py + def send_batch(self, from_addr: cosmos.Address, to_addr: cosmos.Address, amounts: cosmos.Balance): + to_go = list(amounts.keys()) + account_number, account_sequence = self.get_acct_seq(from_addr) + balance_before = self.get_balance(to_addr) + while to_go: + cnt = len(to_go) + if (self.max_send_batch_size > 0) and (cnt > self.max_send_batch_size): + cnt = self.max_send_batch_size + batch_denoms = to_go[:cnt] + to_go = to_go[cnt:] + batch_balance = {denom: amounts.get(denom, 0) for denom in batch_denoms} + self.send(from_addr, to_addr, batch_balance, account_seq=(account_number, account_sequence)) + account_sequence += 1 + expected_balance = cosmos.balance_add(balance_before, amounts) + self.wait_for_balance_change(to_addr, balance_before, expected_balance=expected_balance) + + def get_current_block(self): + return int(self.status()["SyncInfo"]["latest_block_height"]) + + # TODO Deduplicate + # TODO The /node_info URL does not exist any more, use /status? + def get_status(self, host, port): + return self._rpc_get(host, port, "node_info") + + # TODO Deduplicate + def status(self): + args = ["status"] + self._node_args() + res = self.sifnoded_exec(args) + return json.loads(stderr(res)) + + def query_block(self, block: Optional[int] = None) -> JsonDict: + args = ["query", "block"] + \ + ([str(block)] if block is not None else []) + self._node_args() + res = self.sifnoded_exec(args) + return json.loads(stdout(res)) + + def query_pools(self, height: Optional[int] = None) -> List[JsonDict]: + return self._paged_read(["query", "clp", "pools"], "pools", height=height) + + def query_pools_sorted(self, height: Optional[int] = None) -> Mapping[str, JsonDict]: + pools = self.query_pools(height=height) + result = {p["external_asset"]["symbol"]: p for p in pools} + assert len(result) == len(pools) + return result + + def query_clp_liquidity_providers(self, denom: str, height: Optional[int] = None) -> List[JsonDict]: + # Note: this paged result is slightly different than `query bank balances`. Here we always get "height" + return self._paged_read(["query", "clp", "lplist", denom], "liquidity_providers", height=height) + + def _paged_read(self, base_args: List[str], result_key: str, height: Optional[int] = None, + limit: Optional[int] = None, disable_log: bool = False, retries_on_error: Optional[int] = None, + delay_on_error: int = 3 + ) -> List[JsonObj]: + retries_on_error = retries_on_error if retries_on_error is not None else self.get_balance_default_retries + all_results = [] + page_key = None + while True: + args = base_args + ["--output", "json"] + \ + (["--height", str(height)] if height is not None else []) + \ + (["--limit", str(limit)] if limit is not None else []) + \ + (["--page-key", page_key] if page_key is not None else []) + \ + self._home_args() + self._chain_id_args() + self._node_args() + retries_left = retries_on_error + while True: + try: + res = self.sifnoded_exec(args, disable_log=disable_log) + break + except Exception as e: + if retries_left == 0: + raise e + else: + retries_left -= 1 + log.error("Error reading query result for '{}' ({}), retries left: {}".format(repr(base_args), repr(e), retries_left)) + time.sleep(delay_on_error) + res = json.loads(stdout(res)) + next_key = res["pagination"]["next_key"] + if next_key is not None: + if height is None: + # There are more results than fit on a page. To ensure we get all balances as a consistent + # snapshot, retry with "--height" fixed to the current block. + if "height" in res: + # In some cases such as "query clp pools" the response already contains a "height" and we can + # use it without incurring a separate request. + height = int(res["height"]) + log.debug("Large query result, continuing in paged mode using height of {}.".format(height)) + else: + # In some cases there is no "height" in the response and we must restart the query which wastes + # one request. We could optimize this by starting with explicit "--height" in the first place, + # but the assumption is that most of results will fit on one page and that this will be faster + # without "--height"). + height = self.get_current_block() + log.debug("Large query result, restarting in paged mode using height of {}.".format(height)) + continue + page_key = _b64_decode(next_key) + chunk = res[result_key] + all_results.extend(chunk) + log.debug("Read {} items, all={}, next_key={}".format(len(chunk), len(all_results), next_key)) + if next_key is None: + break + return all_results + + def tx_clp_create_pool(self, from_addr: cosmos.Address, symbol: str, native_amount: int, external_amount: int, + account_seq: Optional[Tuple[int, int]] = None, broadcast_mode: Optional[str] = None, + ) -> JsonDict: + # For more examples see ticket #2470, e.g. + args = ["tx", "clp", "create-pool", "--from", from_addr, "--symbol", symbol, "--nativeAmount", + str(native_amount), "--externalAmount", str(external_amount)] + self._chain_id_args() + \ + self._node_args() + self._high_gas_prices_args() + self._home_args() + self._keyring_backend_args() + \ + self._account_number_and_sequence_args(account_seq) + \ + self._broadcast_mode_args(broadcast_mode=broadcast_mode) + self._yes_args() + res = self.sifnoded_exec(args) + return yaml_load(stdout(res)) + + # Items: (denom, native_amount, external_amount) + # clp_admin has to have enough balances for the (native? / external?) amounts + def create_liquidity_pools_batch(self, clp_admin: cosmos.Address, entries: Iterable[Tuple[str, int, int]]): + account_number, account_sequence = self.get_acct_seq(clp_admin) + for denom, native_amount, external_amount in entries: + res = self.tx_clp_create_pool(clp_admin, denom, native_amount, external_amount, + account_seq=(account_number, account_sequence)) + check_raw_log(res) + account_sequence += 1 + self.wait_for_last_transaction_to_be_mined() + assert set(p["external_asset"]["symbol"] for p in self.query_pools()) == set(e[0] for e in entries), \ + "Failed to create one or more liquidity pools" + + def tx_clp_add_liquidity(self, from_addr: cosmos.Address, symbol: str, native_amount: int, external_amount: int, /, + account_seq: Optional[Tuple[int, int]] = None, broadcast_mode: Optional[str] = None + ) -> JsonDict: + args = ["tx", "clp", "add-liquidity", "--from", from_addr, "--symbol", symbol, "--nativeAmount", + str(native_amount), "--externalAmount", str(external_amount)] + self._home_args() + \ + self._keyring_backend_args() + self._chain_id_args() + self._node_args() + self._fees_args() + \ + self._account_number_and_sequence_args(account_seq) + \ + self._broadcast_mode_args(broadcast_mode=broadcast_mode) + self._yes_args() + res = self.sifnoded_exec(args) + return yaml_load(stdout(res)) + + # asymmetry: -10000 = 100% of native asset, 0 = 50% of native asset and 50% of external asset, 10000 = 100% of external asset + # w_basis 0 = 0%, 10000 = 100%, Remove 0-100% of liquidity symmetrically for both assets of the pair + # See https://github.com/Sifchain/sifnode/blob/master/docs/tutorials/clp%20tutorial.md + def tx_clp_remove_liquidity(self, from_addr: cosmos.Address, w_basis: int, asymmetry: int) -> JsonDict: + assert (w_basis >= 0) and (w_basis <= 10000) + assert (asymmetry >= -10000) and (asymmetry <= 10000) + args = ["tx", "clp", "remove-liquidity", "--from", from_addr, "--wBasis", int(w_basis), "--asymmetry", + str(asymmetry)] + self._node_args() + self._chain_id_args() + self._keyring_backend_args() + \ + self._fees_args() + self._yes_args() + res = self.sifnoded_exec(args) + res = yaml_load(res) + check_raw_log(res) + return res + + def tx_clp_unbond_liquidity(self, from_addr: cosmos.Address, symbol: str, units: int) -> JsonDict: + args = ["tx", "clp", "unbond-liquidity", "--from", from_addr, "--symbol", symbol, "--units", str(units)] + \ + self._home_args() + self._keyring_backend_args() + self._chain_id_args() + self._node_args() + \ + self._fees_args() + self._broadcast_mode_args() + self._yes_args() + res = self.sifnoded_exec(args) + res = yaml_load(stdout(res)) + check_raw_log(res) + return res + + def tx_clp_swap(self, from_addr: cosmos.Address, sent_symbol: str, sent_amount: int, received_symbol: str, + min_receiving_amount: int, broadcast_mode: Optional[str] = None + ) -> JsonDict: + args = ["tx", "clp", "swap", "--from", from_addr, "--sentSymbol", sent_symbol, "--sentAmount", str(sent_amount), + "--receivedSymbol", received_symbol, "--minReceivingAmount", str(min_receiving_amount)] + \ + self._node_args() + self._chain_id_args() + self._home_args() + self._keyring_backend_args() + \ + self._fees_args() + self._broadcast_mode_args(broadcast_mode) + self._yes_args() + res = self.sifnoded_exec(args) + res = yaml_load(stdout(res)) + check_raw_log(res) + return res + + def clp_reward_period(self, from_addr: cosmos.Address, rewards_params: RewardsParams): + with self._with_temp_json_file([rewards_params]) as tmp_rewards_json: + args = ["tx", "clp", "reward-period", "--from", from_addr, "--path", tmp_rewards_json] + \ + self._home_args() + self._keyring_backend_args() + self._node_args() + self._chain_id_args() + \ + self._fees_args() + self._broadcast_mode_args() + self._yes_args() + res = self.sifnoded_exec(args) + res = sifnoded_parse_output_lines(stdout(res)) + return res + + def query_reward_params(self): + args = ["query", "reward", "params"] + self._node_args() + res = self.sifnoded_exec(args) + return res + + def clp_set_lppd_params(self, from_addr: cosmos.Address, lppd_params: LPPDParams): + with self._with_temp_json_file([lppd_params]) as tmp_distribution_json: + args = ["tx", "clp", "set-lppd-params", "--path", tmp_distribution_json, "--from", from_addr] + \ + self._home_args() + self._keyring_backend_args() + self._node_args() + self._chain_id_args() + \ + self._fees_args() + self._broadcast_mode_args() + self._yes_args() + res = self.sifnoded_exec(args) + res = sifnoded_parse_output_lines(stdout(res)) + return res + + def tx_margin_update_pools(self, from_addr: cosmos.Address, open_pools: Iterable[str], + closed_pools: Iterable[str], broadcast_mode: Optional[str] = None + ) -> JsonDict: + with self.cmd.with_temp_file() as open_pools_file, self.cmd.with_temp_file() as closed_pools_file: + self.cmd.write_text_file(open_pools_file, json.dumps(list(open_pools))) + self.cmd.write_text_file(closed_pools_file, json.dumps(list(closed_pools))) + args = ["tx", "margin", "update-pools", open_pools_file, "--closed-pools", closed_pools_file, + "--from", from_addr] + self._home_args() + self._keyring_backend_args() + self._node_args() + \ + self._chain_id_args() + self._fees_args() + self._broadcast_mode_args(broadcast_mode) + self._yes_args() + res = self.sifnoded_exec(args) + res = yaml_load(stdout(res)) + check_raw_log(res) + return res + + def query_margin_params(self, height: Optional[int] = None) -> JsonDict: + args = ["query", "margin", "params"] + \ + (["--height", str(height)] if height is not None else []) + \ + self._node_args() + self._chain_id_args() + res = self.sifnoded_exec(args) + res = yaml_load(stdout(res)) + return res + + def tx_margin_whitelist(self, from_addr: cosmos.Address, address: cosmos.Address, + broadcast_mode: Optional[str] = None + ) -> JsonDict: + args = ["tx", "margin", "whitelist", address, "--from", from_addr] + self._home_args() + \ + self._keyring_backend_args() + self._node_args() + self._chain_id_args() + self._fees_args() + \ + self._broadcast_mode_args(broadcast_mode) + self._yes_args() + res = self.sifnoded_exec(args) + res = yaml_load(stdout(res)) + return res + + def tx_margin_open(self, from_addr: cosmos.Address, borrow_asset: str, collateral_asset: str, collateral_amount: int, + leverage: int, position: str, broadcast_mode: Optional[str] = None + ) -> JsonDict: + args = ["tx", "margin", "open", "--borrow_asset", borrow_asset, "--collateral_asset", collateral_asset, + "--collateral_amount", str(collateral_amount), "--leverage", str(leverage), "--position", position, \ + "--from", from_addr] + self._home_args() + self._keyring_backend_args() + self._node_args() + \ + self._chain_id_args() + self._fees_args() + self._broadcast_mode_args(broadcast_mode) + self._yes_args() + res = self.sifnoded_exec(args) + res = yaml_load(stdout(res)) + check_raw_log(res) + return res + + def tx_margin_close(self, from_addr: cosmos.Address, id: int, broadcast_mode: Optional[str] = None) -> JsonDict: + args = ["tx", "margin", "close", "--id", str(id), "--from", from_addr] + self._home_args() + \ + self._keyring_backend_args() + self._node_args() + self._chain_id_args() + self._fees_args() + \ + self._broadcast_mode_args(broadcast_mode) + self._yes_args() + res = self.sifnoded_exec(args) + res = yaml_load(stdout(res)) + check_raw_log(res) + return res + + def margin_open_simple(self, from_addr: cosmos.Address, borrow_asset: str, collateral_asset: str, collateral_amount: int, + leverage: int, position: str + ) -> JsonDict: + res = self.tx_margin_open(from_addr, borrow_asset, collateral_asset, collateral_amount, leverage, position, + broadcast_mode="block") + mtp_open_event = exactly_one([x for x in res["events"] if x["type"] == "margin/mtp_open"])["attributes"] + result = {_b64_decode(x["key"]): _b64_decode(x["value"]) for x in mtp_open_event} + return result + + def query_margin_positions_for_address(self, address: cosmos.Address, height: Optional[int] = None) -> JsonDict: + args = ["query", "margin", "positions-for-address", address] + self._node_args() + self._chain_id_args() + res = self._paged_read(args, "mtps", height=height) + return res + + def tx_margin_update_pools(self, from_addr: cosmos.Address, open_pools: Iterable[str], + closed_pools: Iterable[str], broadcast_mode: Optional[str] = None + ) -> JsonDict: + with self.cmd.with_temp_file() as open_pools_file, self.cmd.with_temp_file() as closed_pools_file: + self.cmd.write_text_file(open_pools_file, json.dumps(list(open_pools))) + self.cmd.write_text_file(closed_pools_file, json.dumps(list(closed_pools))) + args = ["tx", "margin", "update-pools", open_pools_file, "--closed-pools", closed_pools_file, + "--from", from_addr] + self._home_args() + self._keyring_backend_args() + self._node_args() + \ + self._chain_id_args() + self._fees_args() + self._broadcast_mode_args(broadcast_mode) + self._yes_args() + res = self.sifnoded_exec(args) + res = yaml_load(stdout(res)) + check_raw_log(res) + return res + + def query_margin_params(self, height: Optional[int] = None) -> JsonDict: + args = ["query", "margin", "params"] + \ + (["--height", str(height)] if height is not None else []) + \ + self._node_args() + self._chain_id_args() + res = self.sifnoded_exec(args) + res = yaml_load(stdout(res)) + return res + + def tx_margin_whitelist(self, from_addr: cosmos.Address, address: cosmos.Address, + broadcast_mode: Optional[str] = None + ) -> JsonDict: + args = ["tx", "margin", "whitelist", address, "--from", from_addr] + self._home_args() + \ + self._keyring_backend_args() + self._node_args() + self._chain_id_args() + self._fees_args() + \ + self._broadcast_mode_args(broadcast_mode) + self._yes_args() + res = self.sifnoded_exec(args) + res = yaml_load(stdout(res)) + return res + + def tx_margin_open(self, from_addr: cosmos.Address, borrow_asset: str, collateral_asset: str, collateral_amount: int, + leverage: int, position: str, broadcast_mode: Optional[str] = None + ) -> JsonDict: + args = ["tx", "margin", "open", "--borrow_asset", borrow_asset, "--collateral_asset", collateral_asset, + "--collateral_amount", str(collateral_amount), "--leverage", str(leverage), "--position", position, \ + "--from", from_addr] + self._home_args() + self._keyring_backend_args() + self._node_args() + \ + self._chain_id_args() + self._fees_args() + self._broadcast_mode_args(broadcast_mode) + self._yes_args() + res = self.sifnoded_exec(args) + res = yaml_load(stdout(res)) + check_raw_log(res) + return res + + def tx_margin_close(self, from_addr: cosmos.Address, id: int, broadcast_mode: Optional[str] = None) -> JsonDict: + args = ["tx", "margin", "close", "--id", str(id), "--from", from_addr] + self._home_args() + \ + self._keyring_backend_args() + self._node_args() + self._chain_id_args() + self._fees_args() + \ + self._broadcast_mode_args(broadcast_mode) + self._yes_args() + res = self.sifnoded_exec(args) + res = yaml_load(stdout(res)) + check_raw_log(res) + return res + + def margin_open_simple(self, from_addr: cosmos.Address, borrow_asset: str, collateral_asset: str, collateral_amount: int, + leverage: int, position: str + ) -> JsonDict: + res = self.tx_margin_open(from_addr, borrow_asset, collateral_asset, collateral_amount, leverage, position, + broadcast_mode="block") + mtp_open_event = exactly_one([x for x in res["events"] if x["type"] == "margin/mtp_open"])["attributes"] + result = {_b64_decode(x["key"]): _b64_decode(x["value"]) for x in mtp_open_event} + return result + + def query_margin_positions_for_address(self, address: cosmos.Address, height: Optional[int] = None) -> JsonDict: + args = ["query", "margin", "positions-for-address", address] + self._node_args() + self._chain_id_args() + res = self._paged_read(args, "mtps", height=height) + return res + + def sign_transaction(self, tx: JsonDict, from_sif_addr: cosmos.Address, sequence: int = None, + account_number: int = None + ) -> Mapping: + assert (sequence is not None) == (account_number is not None) # We need either both or none + with self._with_temp_json_file(tx) as tmp_tx_file: + args = ["tx", "sign", tmp_tx_file, "--from", from_sif_addr] + \ + (["--sequence", str(sequence), "--offline", "--account-number", str(account_number)] if sequence else []) + \ + self._home_args() + self._keyring_backend_args() + self._chain_id_args() + self._node_args() + res = self.sifnoded_exec(args) + signed_tx = json.loads(stderr(res)) + return signed_tx + + def encode_transaction(self, tx: JsonObj) -> bytes: + with self._with_temp_json_file(tx) as tmp_file: + res = self.sifnoded_exec(["tx", "encode", tmp_file]) + encoded_tx = base64.b64decode(stdout(res)) + return encoded_tx + + def version(self) -> str: + return exactly_one(stderr(self.sifnoded_exec(["version"])).splitlines()) + + def gov_submit_software_upgrade(self, version: str, from_acct: cosmos.Address, deposit: cosmos.Balance, + upgrade_height: int, upgrade_info: str, title: str, description: str, broadcast_mode: Optional[str] = None + ): + args = ["tx", "gov", "submit-proposal", "software-upgrade", version, "--from", from_acct, "--deposit", + cosmos.balance_format(deposit), "--upgrade-height", str(upgrade_height), "--upgrade-info", upgrade_info, + "--title", title, "--description", description] + self._home_args() + self._keyring_backend_args() + \ + self._node_args() + self._chain_id_args() + self._fees_args() + \ + self._broadcast_mode_args(broadcast_mode=broadcast_mode) + self._yes_args() + res = yaml_load(stdout(self.sifnoded_exec(args))) + return res + + def query_gov_proposals(self) -> JsonObj: + args = ["query", "gov", "proposals"] + self._node_args() + self._chain_id_args() + # Check if there are no active proposals, in this case we don't get an empty result but an error + res = self.sifnoded_exec(args, check_exit=False) + if res[0] == 1: + error_lines = stderr(res).splitlines() + if len(error_lines) > 0: + if error_lines[0] == "Error: no proposals found": + return [] + elif res[0] == 0: + assert len(stderr(res)) == 0 + # TODO Pagination without initial block + res = yaml_load(stdout(self.sifnoded_exec(args))) + assert res["pagination"]["next_key"] is None + return res["proposals"] + + def gov_vote(self, proposal_id: int, vote: bool, from_acct: cosmos.Address, broadcast_mode: Optional[str] = None): + args = ["tx", "gov", "vote", str(proposal_id), "yes" if vote else "no", "--from", from_acct] + \ + self._home_args() + self._keyring_backend_args() + self._node_args() + self._chain_id_args() + \ + self._fees_args() + self._broadcast_mode_args(broadcast_mode) + self._yes_args() + res = yaml_load(stdout(self.sifnoded_exec(args))) + return res + + @contextlib.contextmanager + def _with_temp_json_file(self, json_obj: JsonObj) -> str: + with self.cmd.with_temp_file() as tmpfile: + self.cmd.write_text_file(tmpfile, json.dumps(json_obj)) + yield tmpfile + + def sifnoded_exec(self, args: List[str], stdin: Union[str, bytes, Sequence[str], None] = None, + cwd: Optional[str] = None, disable_log: bool = False, check_exit: bool = True ) -> command.ExecResult: - args = [self.binary] + args + \ - (["--home", sifnoded_home] if sifnoded_home else []) + \ - (["--keyring-backend", keyring_backend] if keyring_backend else []) - res = self.cmd.execst(args, stdin=stdin, cwd=cwd, disable_log=disable_log) + args = [self.binary] + args + res = self.cmd.execst(args, stdin=stdin, cwd=cwd, disable_log=disable_log, check_exit=check_exit) return res + # Block has to be mined, does not work for block 0 + def get_block_results(self, height: Optional[int] = None): + path = "block_results{}".format("?height={}".format(height) if height is not None else "") + host, port = self._get_host_and_port() + return self._rpc_get(host, port, path)["result"] + + def _get_host_and_port(self) -> Tuple[str, int]: + # TODO HACK + # TODO Refactor ports + # TODO Better store self.host and self.port and make self.node a calculated property + if self.node is None: + return LOCALHOST, SIFNODED_DEFAULT_RPC_PORT + else: + m = re.compile("^tcp://(.+):(.+)$").match(self.node) + assert m, "Not implemented" + host, port = m[1], int(m[2]) + if host == ANY_ADDR: + host = LOCALHOST + return host, port + def _rpc_get(self, host, port, relative_url): url = "http://{}:{}/{}".format(host, port, relative_url) - return json.loads(http_get(url).decode("UTF-8")) - - def get_status(self, host, port): - return self._rpc_get(host, port, "node_info") + http_result_payload = http_get(url) + log.debug("Result for {}: {} bytes".format(url, len(http_result_payload))) + return json.loads(http_result_payload.decode("UTF-8")) def wait_for_last_transaction_to_be_mined(self, count: int = 1, disable_log: bool = True, timeout: int = 90): - # TODO return int(self._rpc_get(host, port, abci_info)["response"]["last_block_height"]) - def latest_block_height(): - args = ["status"] # TODO --node - return int(json.loads(stderr(self.sifnoded_exec(args, disable_log=disable_log)))["SyncInfo"]["latest_block_height"]) log.debug("Waiting for last sifnode transaction to be mined...") start_time = time.time() - initial_block = latest_block_height() - while latest_block_height() < initial_block + count: + initial_block = self.get_current_block() + while self.get_current_block() < initial_block + count: time.sleep(1) if time.time() - start_time > timeout: raise Exception("Timeout expired while waiting for last sifnode transaction to be mined") + def wait_for_block(self, height: int): + while self.get_current_block() < height: + time.sleep(1) + + # TODO Refactor wait_up() / _wait_up() def wait_up(self, host, port): + from urllib.error import URLError while True: - from urllib.error import URLError try: return self.get_status(host, port) except URLError: time.sleep(1) + # TODO Refactor wait_up() / _wait_up() + def _wait_up(self, timeout: int = 30): + host, port = self._get_host_and_port() + from urllib.error import URLError + start_time = time.time() + while True: + try: + response = self._rpc_get(host, port, "status") + result = response["result"] + if not result["sync_info"]["catching_up"]: + return result + except URLError: + pass + if time.time() - start_time > timeout: + raise SifnodedException("Timeout waiting for sifnoded to come up. Check if the process is running. " + "If it didn't start, ther should be some information in the log file. If the process is slow to " + "start or if the validator needs more time to catch up, increase the timeout.") + time.sleep(1) + + def _home_args(self) -> Optional[List[str]]: + return ["--home", self.home] if self.home else [] + + def _keyring_backend_args(self) -> Optional[List[str]]: + return ["--keyring-backend", self.keyring_backend] if self.keyring_backend else [] + + def _gas_prices_args(self) -> List[str]: + return ["--gas-prices", self.gas_prices, "--gas-adjustment", str(self.gas_adjustment)] + \ + (["--gas", str(self.gas)] if self.gas is not None else []) + + def _high_gas_prices_args(self) -> List[str]: + return ["--gas-prices", self.gas_prices, "--gas-adjustment", str(self.gas_adjustment), + "--gas", str(self.high_gas)] + + # Deprecated: sifnoded accepts --gas-prices=0.5rowan along with --gas-adjustment=1.5 instead of a fixed fee. + # However, this is needed for "sifnoded tx bank send" which does not work with "--gas" + def _fees_args(self) -> List[str]: + return ["--fees", sif_format_amount(self.fees, ROWAN)] + + def _chain_id_args(self) -> List[str]: + assert self.chain_id + return ["--chain-id", self.chain_id] + + def _node_args(self) -> Optional[List[str]]: + return ["--node", self.node] if self.node else [] + + def _account_number_and_sequence_args(self, account_seq: Optional[Tuple[int, int]] = None) -> Optional[List[str]]: + return ["--account-number", str(account_seq[0]), "--sequence", str(account_seq[1])] if account_seq is not None else [] + + # One of sync|async|block; block will actually get us raw_message + def _broadcast_mode_args(self, broadcast_mode: Optional[str] = None) -> Optional[List[str]]: + broadcast_mode = broadcast_mode if broadcast_mode is not None else self.broadcast_mode + return ["--broadcast-mode", broadcast_mode] if broadcast_mode is not None else [] + + def _yes_args(self) -> List[str]: + return ["--yes"] # Refactoring in progress - this class is supposed to become the successor of class Sifnode. # It wraps node, home, chain_id, fees and keyring backend +# TODO Remove 'ctx' (currently needed for cross-chain fees for Peggy2) class SifnodeClient: - def __init__(self, cmd: command.Command, ctx, node: Optional[str] = None, home: - Optional[str] = None, chain_id: Optional[str] = None, grpc_port: Optional[int] = None + def __init__(self, ctx, sifnode: Sifnoded, node: Optional[str] = None, chain_id: Optional[str] = None, + grpc_port: Optional[int] = None ): - self.cmd = cmd + self.sifnode = sifnode self.ctx = ctx # TODO Remove (currently needed for cross-chain fees for Peggy2) - self.binary = "sifnoded" - self.node = node - self.home = home - self.chain_id = chain_id self.grpc_port = grpc_port def query_account(self, sif_addr): - result = json.loads(stdout(self.sifnoded_exec(["query", "account", sif_addr, "--output", "json"]))) + result = json.loads(stdout(self.sifnode.sifnoded_exec(["query", "account", sif_addr, "--output", "json"]))) return result def query_tx(self, tx_hash: str) -> Optional[str]: @@ -322,29 +1279,53 @@ def send_from_sifchain_to_ethereum(self, from_sif_addr: cosmos.Address, to_eth_a generate_only: bool = False ) -> Mapping: """ Sends ETH from Sifchain to Ethereum (burn) """ - assert on_peggy2_branch, "Only for Peggy2.0" assert self.ctx.eth eth = self.ctx.eth - direction = "lock" if is_cosmos_native_denom(denom) else "burn" - cross_chain_ceth_fee = eth.cross_chain_fee_base * eth.cross_chain_burn_fee # TODO - args = ["tx", "ethbridge", direction, to_eth_addr, str(amount), denom, str(cross_chain_ceth_fee), - "--network-descriptor", str(eth.ethereum_network_descriptor), # Mandatory - "--from", from_sif_addr, # Mandatory, either name from keyring or address - "--output", "json", - "-y" - ] + \ - (["--generate-only"] if generate_only else []) + \ - self._gas_prices_args() + \ - self._home_args() + \ - self._chain_id_args() + \ - self._node_args() + \ - (self._keyring_backend_args() if not generate_only else []) - res = self.sifnoded_exec(args) - result = json.loads(stdout(res)) - if not generate_only: - assert "failed to execute message" not in result["raw_log"] - return result + if on_peggy2_branch: + cross_chain_ceth_fee = eth.cross_chain_fee_base * eth.cross_chain_burn_fee # TODO + args = ["tx", "ethbridge", direction, to_eth_addr, str(amount), denom, str(cross_chain_ceth_fee), + "--network-descriptor", str(eth.ethereum_network_descriptor), # Mandatory + "--from", from_sif_addr, # Mandatory, either name from keyring or address + "--output", "json", + ] + \ + (["--generate-only"] if generate_only else []) + \ + self.sifnode._fees_args() + \ + self.sifnode._home_args() + \ + (self.sifnode._keyring_backend_args() if not generate_only else []) + \ + self.sifnode._chain_id_args() + \ + self.sifnode._node_args() + \ + self.sifnode._yes_args() + res = self.sifnode.sifnoded_exec(args) + result = json.loads(stdout(res)) + if not generate_only: + assert "failed to execute message" not in result["raw_log"] + return result + else: + gas_cost = 160000000000 * 393000 # Taken from peggy1 + cross_chain_ceth_fee = str(gas_cost) # TODO Not sure if this is the right variable + # Ethereum chain id is hardcoded according to peggy1 + ethereum_chain_id = str(5777) + args = ["tx", "ethbridge", direction] + \ + self.sifnode._node_args() + \ + [from_sif_addr, to_eth_addr, str(amount), denom, cross_chain_ceth_fee] + \ + (self.sifnode._keyring_backend_args() if not generate_only else []) + \ + self.sifnode._fees_args() + \ + ["--ethereum-chain-id", ethereum_chain_id] + \ + self.sifnode._chain_id_args() + \ + self.sifnode._home_args() + \ + ["--from", from_sif_addr] + \ + ["--output","json"] + \ + self.sifnode._yes_args() + + res = self.sifnode.sifnoded_exec(args) + result = json.loads(stdout(res)) + if not generate_only: + assert "failed to execute message" not in result["raw_log"] + return result + + # sifnoded tx ethbridge + def tx_clp_create_pool(self, from_addr: cosmos.Address, symbol: str, native_amount: int, external_amount: int ) -> Mapping[str, Any]: @@ -431,8 +1412,8 @@ def send_from_sifchain_to_ethereum_grpc(self, from_sif_addr: cosmos.Address, to_ denom: str ): tx = self.send_from_sifchain_to_ethereum(from_sif_addr, to_eth_addr, amount, denom, generate_only=True) - signed_tx = self.sign_transaction(tx, from_sif_addr) - encoded_tx = self.encode_transaction(signed_tx) + signed_tx = self.sifnode.sign_transaction(tx, from_sif_addr) + encoded_tx = self.sifnode.encode_transaction(signed_tx) result = self.broadcast_tx(encoded_tx) return result @@ -470,7 +1451,8 @@ def open_grpc_channel(self) -> grpc.Channel: # See https://docs.cosmos.network/v0.44/core/grpc_rest.html # See https://app.swaggerhub.com/apis/Ivan-Verchenko/sifnode-swagger-api/1.1.1 # See https://raw.githubusercontent.com/Sifchain/sifchain-ui/develop/ui/core/swagger.yaml - return grpc.insecure_channel("127.0.0.1:9090") + return grpc.insecure_channel("{}:{}".format(LOCALHOST, + self.grpc_port if self.grpc_port is not None else SIFNODED_DEFAULT_GRPC_PORT)) def broadcast_tx(self, encoded_tx: bytes): ondemand_import_generated_protobuf_sources() @@ -588,6 +1570,39 @@ def init(self, tendermind_node, web3_provider, bridge_registry_contract_address, return self.cmd.popen(args, env=env, cwd=cwd, log_file=log_file) +class SifnodedException(Exception): + def __init__(self, message = None): + super().__init__(message) + self.message = message + + +def is_min_commission_too_low_exception(e: Exception): + patt = re.compile("^validator commission [\d.]+ cannot be lower than minimum of [\d.]+: invalid request$") + return (type(e) == SifnodedException) and patt.match(e.message) + + +def is_max_voting_power_limit_exceeded_exception(e: Exception): + patt = re.compile("^This validator has a voting power of [\d.]+%. Delegations not allowed to a validator whose " + "post-delegation voting power is more than [\d.]+%. Please delegate to a validator with less bonded tokens: " + "invalid request$") + return (type(e) == SifnodedException) and patt.match(e.message) + + +class RateLimiter: + def __init__(self, sifnoded, max_tpb): + self.sifnoded = sifnoded + self.max_tpb = max_tpb + self.counter = 0 + + def limit(self): + if self.max_tpb == 0: + pass + self.counter += 1 + if self.counter == self.max_tpb: + self.sifnoded.wait_for_last_transaction_to_be_mined() + self.counter = 0 + + # This is probably useful for any program that uses web3 library in the same way # ETHEREUM_ADDRESS has to start with "0x" and ETHEREUM_PRIVATE_KEY has to be without "0x". def _env_for_ethereum_address_and_key(ethereum_address, ethereum_private_key): @@ -599,3 +1614,6 @@ def _env_for_ethereum_address_and_key(ethereum_address, ethereum_private_key): assert ethereum_address.startswith("0x") env["ETHEREUM_ADDRESS"] = ethereum_address return env or None # Avoid passing empty environment + +def _b64_decode(s: str): + return base64.b64decode(s).decode("UTF-8") diff --git a/test/integration/framework/src/siftool/test_utils.py b/test/integration/framework/src/siftool/test_utils.py index 2643eb4141..f440efed69 100644 --- a/test/integration/framework/src/siftool/test_utils.py +++ b/test/integration/framework/src/siftool/test_utils.py @@ -10,7 +10,7 @@ from hexbytes import HexBytes from web3.types import TxReceipt -from siftool import eth, truffle, hardhat, run_env, sifchain, cosmos +from siftool import eth, truffle, hardhat, run_env, sifchain, cosmos, command from siftool.common import * # These are utilities to interact with running environment (running agains local ganache-cli/hardhat/sifnoded). @@ -64,49 +64,106 @@ def get_env_ctx(cmd=None, env_file=None, env_vars=None): def get_env_ctx_peggy2(): cmd = run_env.Integrator() - dot_env_vars = json.loads(cmd.read_text_file(cmd.project.project_dir("smart-contracts/env.json"))) - environment_vars = json.loads(cmd.read_text_file(cmd.project.project_dir("smart-contracts/environment.json"))) - - deployed_contract_address_overrides = get_overrides_for_smart_contract_addresses(dot_env_vars) - tmp = environment_vars["contractResults"]["contractAddresses"] - deployed_contract_addresses = dict_merge({ - "BridgeBank": tmp["bridgeBank"], - "CosmosBridge": tmp["cosmosBridge"], - "BridgeRegistry": tmp["bridgeRegistry"], - "Rowan": tmp["rowanContract"], - "Blocklist": tmp["blocklist"], - }, deployed_contract_address_overrides) - abi_provider = hardhat.HardhatAbiProvider(cmd, deployed_contract_addresses) - - # TODO We're mixing "OPERATOR" vs. "OWNER" - # TODO Addressses from dot_env_vars are not in correct EIP55 "checksum" format - # operator_address = web3.Web3.toChecksumAddress(dot_env_vars["ETH_ACCOUNT_OPERATOR_ADDRESS"]) - # operator_private_key = dot_env_vars["ETH_ACCOUNT_OPERATOR_PRIVATEKEY"][2:] - owner_address = web3.Web3.toChecksumAddress(dot_env_vars["ETH_ACCOUNT_OWNER_ADDRESS"]) - owner_private_key = dot_env_vars.get("ETH_ACCOUNT_OWNER_PRIVATEKEY") - if (owner_private_key is not None) and (owner_private_key.startswith("0x")): - owner_private_key = owner_private_key[2:] # TODO Remove - owner_address, owner_private_key = eth.validate_address_and_private_key(owner_address, owner_private_key) - - rowan_source = dot_env_vars["ROWAN_SOURCE"] - - w3_url = eth.web3_host_port_url(dot_env_vars["ETH_HOST"], int(dot_env_vars["ETH_PORT"])) - w3_conn = eth.web3_connect(w3_url) - sifnode_url = dot_env_vars["TCP_URL"] - sifnode_chain_id = "localnet" # TODO Mandatory, but not present either in environment_vars or dot_env_vars - assert dot_env_vars["CHAINDIR"] == dot_env_vars["HOME"] - sifnoded_home = os.path.join(dot_env_vars["CHAINDIR"], ".sifnoded") - ethereum_network_descriptor = int(dot_env_vars["ETH_CHAIN_ID"]) + if "SIFTOOL_ENV_FILE" in os.environ: + # New-style format (hopefully unified) + # This is for connecting to peggy2-tempnet etc. + env_file = os.environ["SIFTOOL_ENV_FILE"] + env_vars = json.loads(cmd.read_text_file(env_file)) + global_mnemonic = env_vars.get("mnemonic", None) + + sifchain_config = env_vars["sifchain"] + sifnode_url = sifchain_config["rpc_url"] + sifnode_chain_id = sifchain_config["chain_id"] + sifnoded_home = sifchain_config.get("home") + + # Supported scenarios regarding rowan_source: + # (1) no rowan_source, no mnemonic => set rowan_source to None and assume it will not be used + # (1) rowan_source without mnemonic => assume private key is already in keystore + # (2) mnemonic without rowan source => create it if it doesn't exist yet + rowan_source = sifchain_config.get("rowan_source") + if not rowan_source: + if "rowan_source_mnemonic" in sifchain_config: + rowan_source_mnemonic = sifchain_config["rowan_source_mnemonic"] + elif global_mnemonic: + rowan_source_mnemonic = global_mnemonic + else: + rowan_source_mnemonic = None + if rowan_source_mnemonic: + rowan_source_mnemonic = rowan_source_mnemonic.split(" ") + rowan_source = sifchain.mnemonic_to_address(cmd, rowan_source_mnemonic) + sifnoded = sifchain.Sifnoded(cmd, home=sifnoded_home) + if not [x for x in sifnoded.keys_list() if x["address"] == rowan_source]: + sifnoded.keys_add(None, rowan_source_mnemonic) + + eth_config = env_vars["ethereum"] + smart_contract_addresses = {k: eth.validate_address_and_private_key(v, None)[0] for k, v in eth_config["smart_contract_addresses"].items()} + w3_url = eth_config["url"] + ethereum_network_descriptor = eth_config["chain_id"] + eth_node_is_local = eth_config.get("is_local", False) + + if "owner" in eth_config: + owner_address, owner_private_key = eth.validate_address_and_private_key(eth_config["owner"], eth_config["owner_private_key"]) + elif global_mnemonic: + owner_address, owner_private_key = eth.validate_address_and_private_key(None, eth._mnemonic_to_private_key(global_mnemonic)) + else: + raise ValueError("Missing ethereum.owner (and/or corresponding private key/mnemonic)") + eth_faucet = eth.validate_address_and_private_key(eth_config.get("faucet", None), None)[0] or owner_address + else: + # For either `siftool run-env` or `devenv` + # TODO Transition to unified format (above) and remove this block + dot_env_vars = json.loads(cmd.read_text_file(cmd.project.project_dir("smart-contracts/env.json"))) + environment_vars = json.loads(cmd.read_text_file(cmd.project.project_dir("smart-contracts/environment.json"))) + + # Note the inconsistency in obtaining deployed smart contract addresses: first we read one set of variables from + # env.json, then we override them with another set of variables from environment.json. Those two files do not + # use the same names, they contain typos and they don't match 1:1. These inconsistencies were copied over from + # devenv intentionally to preserve compatibility with devenv users. The source of variables is however just one: + # the output from deploy_contracts_dev.ts script (that being bridgeBank, bridgeRegistry, cosmosBridge, + # rowanContract and blocklist). TODO Refactor to unified format (above) + smart_contract_address_overrides = _get_overrides_for_smart_contract_addresses(dot_env_vars) + tmp = environment_vars["contractResults"]["contractAddresses"] + smart_contract_addresses = dict_merge({ + "BridgeBank": tmp["bridgeBank"], + "CosmosBridge": tmp["cosmosBridge"], + "BridgeRegistry": tmp["bridgeRegistry"], + "Rowan": tmp["rowanContract"], + "Blocklist": tmp["blocklist"], + }, smart_contract_address_overrides) + + # TODO We're mixing "OPERATOR" vs. "OWNER" + # TODO Addressses from dot_env_vars are not in correct EIP55 "checksum" format + # operator_address = web3.Web3.toChecksumAddress(dot_env_vars["ETH_ACCOUNT_OPERATOR_ADDRESS"]) + # operator_private_key = dot_env_vars["ETH_ACCOUNT_OPERATOR_PRIVATEKEY"][2:] + owner_address = web3.Web3.toChecksumAddress(dot_env_vars["ETH_ACCOUNT_OWNER_ADDRESS"]) + owner_private_key = dot_env_vars.get("ETH_ACCOUNT_OWNER_PRIVATEKEY") + if (owner_private_key is not None) and (owner_private_key.startswith("0x")): + owner_private_key = owner_private_key[2:] # TODO Remove + owner_address, owner_private_key = eth.validate_address_and_private_key(owner_address, owner_private_key) + eth_faucet = owner_address + + rowan_source = dot_env_vars["ROWAN_SOURCE"] + + w3_url = eth.web3_host_port_url(dot_env_vars["ETH_HOST"], int(dot_env_vars["ETH_PORT"])) + + sifnode_url = dot_env_vars["TCP_URL"] + sifnode_chain_id = "localnet" # TODO Mandatory, but not present either in environment_vars or dot_env_vars + assert dot_env_vars["CHAINDIR"] == dot_env_vars["HOME"] + sifnoded_home = os.path.join(dot_env_vars["CHAINDIR"], ".sifnoded") + ethereum_network_descriptor = int(dot_env_vars["ETH_CHAIN_ID"]) + + eth_node_is_local = True + + w3_conn = eth.web3_connect(w3_url) - eth_node_is_local = True generic_erc20_contract = "BridgeToken" ceth_symbol = sifchain.sifchain_denom_hash(ethereum_network_descriptor, eth.NULL_ADDRESS) - assert ceth_symbol == "sifBridge99990x0000000000000000000000000000000000000000" + abi_files_root = cmd.project.project_dir("smart-contracts/artifacts/contracts") + abi_provider = hardhat.HardhatAbiProvider(cmd, abi_files_root, smart_contract_addresses) ctx_eth = eth.EthereumTxWrapper(w3_conn, eth_node_is_local) ctx = EnvCtx(cmd, w3_conn, ctx_eth, abi_provider, owner_address, sifnoded_home, sifnode_url, sifnode_chain_id, - rowan_source, ceth_symbol, generic_erc20_contract) + rowan_source, ceth_symbol, generic_erc20_contract, eth_faucet) if owner_private_key: ctx.eth.set_private_key(owner_address, owner_private_key) @@ -133,8 +190,8 @@ def get_env_ctx_peggy2(): def get_env_ctx_peggy1(cmd=None, env_file=None, env_vars=None): cmd = cmd or run_env.Integrator() - if "ENV_FILE" in os.environ: - env_file = os.environ["ENV_FILE"] + if "SIFTOOL_ENV_FILE" in os.environ: + env_file = os.environ["SIFTOOL_ENV_FILE"] env_vars = json.loads(cmd.read_text_file(env_file)) else: env_file = cmd.project.project_dir("test/integration/vagraneenv.json") @@ -271,28 +328,29 @@ def sif_addr_to_evm_arg(sif_address): class EnvCtx: - def __init__(self, cmd, w3_conn: web3.Web3, ctx_eth, abi_provider, operator, sifnoded_home, sifnode_url, sifnode_chain_id, - rowan_source, ceth_symbol, generic_erc20_contract + def __init__(self, cmd: command.Command, w3_conn: web3.Web3, ctx_eth: eth.EthereumTxWrapper, abi_provider, + operator: eth.Address, sifnoded_home: str, sifnode_url: Optional[str], sifnode_chain_id: str, + rowan_source: cosmos.Address, ceth_symbol: str, generic_erc20_contract: str, eth_faucet: eth.Address ): self.cmd = cmd self.w3_conn = w3_conn self.eth: eth.EthereumTxWrapper = ctx_eth self.abi_provider: hardhat.HardhatAbiProvider = abi_provider self.operator = operator - self.sifnode = sifchain.Sifnoded(self.cmd, home=sifnoded_home) - self.sifnode_url = sifnode_url - self.sifnode_chain_id = sifnode_chain_id + self.sifnode = sifchain.Sifnoded(self.cmd, home=sifnoded_home, node=sifnode_url, chain_id=sifnode_chain_id) # Refactoring in progress: moving stuff into separate client that encapsulates things like url, home and chain_id - self.sifnode_client = sifchain.SifnodeClient(self.cmd, self, node=sifnode_url, home=sifnoded_home, chain_id=sifnode_chain_id, grpc_port=9090) + self.sifnode_client = sifchain.SifnodeClient(self, self.sifnode, grpc_port=9090) self.rowan_source = rowan_source self.ceth_symbol = ceth_symbol self.generic_erc20_contract = generic_erc20_contract self.available_test_eth_accounts = None + self.eth_faucet = eth_faucet + self.sifchain_ethbridge_admin_account = self.rowan_source # Defaults - self.wait_for_sif_balance_change_default_timeout = 90 - self.wait_for_sif_balance_change_default_change_timeout = None - self.wait_for_sif_balance_change_default_polling_time = 2 + # self.wait_for_sif_balance_change_default_timeout = 90 + # self.wait_for_sif_balance_change_default_change_timeout = None + # self.wait_for_sif_balance_change_default_polling_time = 2 def get_current_block_number(self) -> int: return self.eth.w3_conn.eth.block_number @@ -306,12 +364,18 @@ def get_blocklist_sc(self): result = self.w3_conn.eth.contract(address=address, abi=abi) return result - def get_bridge_bank_sc(self): + def get_bridge_bank_sc(self) -> Contract: abi, _, address = self.abi_provider.get_descriptor("BridgeBank") assert address, "No address for BridgeBank" result = self.w3_conn.eth.contract(address=address, abi=abi) return result + def get_bridge_token_sc(self) -> Contract: + abi, _, address = self.abi_provider.get_descriptor("BridgeToken") + assert address, "No address for BridgeToken" + result = self.w3_conn.eth.contract(address=address, abi=abi) + return result + def get_cosmos_bridge_sc(self) -> Contract: abi, _, address = self.abi_provider.get_descriptor("CosmosBridge") assert address, "No address for CosmosBridge" @@ -564,7 +628,10 @@ def send_erc20_from_ethereum_to_sifchain(self, from_eth_addr, dest_sichain_addr, self.approve_erc20_token(token_sc, from_eth_addr, amount) self.bridge_bank_lock_eth(from_eth_addr, dest_sichain_addr, amount) - def create_sifchain_addr(self, moniker: str = None, fund_amounts: Union[cosmos.Balance, cosmos.LegacyBalance] = None): + # TODO Decouple; we want to use this with just "sifnoded" running, move to Sifnoded class? + def create_sifchain_addr(self, moniker: str = None, + fund_amounts: Union[cosmos.Balance, cosmos.LegacyBalance, None] = None + ) -> cosmos.Address: """ Generates a new sifchain address in test keyring. If moniker is given, uses it, otherwise generates a random one 'test-xxx'. If fund_amounts is given, the sifchain funds are transferred @@ -582,136 +649,32 @@ def create_sifchain_addr(self, moniker: str = None, fund_amounts: Union[cosmos.B self.rowan_source, sif_format_amount(required_amount, denom), sif_format_amount(available_amount, denom)) old_balances = self.get_sifchain_balance(sif_address) self.send_from_sifchain_to_sifchain(self.rowan_source, sif_address, fund_amounts) - self.wait_for_sif_balance_change(sif_address, old_balances, min_changes=fund_amounts) + self.sifnode.wait_for_balance_change(sif_address, old_balances, min_changes=fund_amounts) new_balances = self.get_sifchain_balance(sif_address) assert cosmos.balance_zero(cosmos.balance_sub(new_balances, fund_amounts)) return sif_address + # TODO Clean up def send_from_sifchain_to_sifchain(self, from_sif_addr: cosmos.Address, to_sif_addr: cosmos.Address, amounts: cosmos.Balance - ): - amounts = cosmos.balance_normalize(amounts) - amounts_string = cosmos.balance_format(amounts) - args = ["tx", "bank", "send", from_sif_addr, to_sif_addr, amounts_string] + \ - self.sifnode_client._chain_id_args() + \ - self.sifnode_client._node_args() + \ - self.sifnode_client._fees_args() + \ - ["--yes", "--output", "json"] - res = self.sifnode.sifnoded_exec(args, sifnoded_home=self.sifnode.home, keyring_backend=self.sifnode.keyring_backend) - retval = json.loads(stdout(res)) - raw_log = retval["raw_log"] - for bad_thing in ["insufficient funds", "signature verification failed"]: - if bad_thing in raw_log: - raise Exception(raw_log) - return retval + ) -> Mapping: + return self.sifnode.send(from_sif_addr, to_sif_addr, amounts) + # TODO Clean up def get_sifchain_balance(self, sif_addr: cosmos.Address, height: Optional[int] = None, - disable_log: bool = False, retries_on_error: int = 3, delay_on_error: int = 3 + disable_log: bool = False, retries_on_error: Optional[int] = None, delay_on_error: int = 3 ) -> cosmos.Balance: - all_balances = {} - # The actual limit might be capped to a lower value (100), in this case everything will still work but we'll get - # fewer results - desired_page_size = 5000 - page_key = None - while True: - args = ["query", "bank", "balances", sif_addr, "--output", "json"] + \ - (["--height", str(height)] if height is not None else []) + \ - (["--limit", str(desired_page_size)] if desired_page_size is not None else []) + \ - (["--page-key", page_key] if page_key is not None else []) + \ - self.sifnode_client._chain_id_args() + \ - self.sifnode_client._node_args() - retries_left = retries_on_error - while True: - try: - res = self.sifnode.sifnoded_exec(args, sifnoded_home=self.sifnode.home, disable_log=disable_log) - break - except Exception as e: - retries_left -= 1 - log.error("Error reading balances, retries left: {}".format(retries_left)) - if retries_left > 0: - time.sleep(delay_on_error) - else: - raise e - res = json.loads(stdout(res)) - balances = res["balances"] - next_key = res["pagination"]["next_key"] - if next_key is not None: - if height is None: - # There are more results than fit on a page. To ensure we get all balances as a consistent - # snapshot, retry with "--height" fised to the current block. This wastes one request. - # We could optimize this by starting with explicit "--height" in the first place, but the current - # assumption is that most of results will fit on one page and that this will be faster without - # "--height". - height = self.get_current_block() - log.debug("Large balance result, switching to paged mode using height of {}.".format(height)) - continue - page_key = base64.b64decode(next_key).decode("UTF-8") - for bal in balances: - denom, amount = bal["denom"], int(bal["amount"]) - assert denom not in all_balances - all_balances[denom] = amount - log.debug("Read {} balances, all={}, first='{}', next_key={}".format(len(balances), len(all_balances), - balances[0]["denom"] if len(balances) > 0 else None, next_key)) - if next_key is None: - break - return all_balances + return self.sifnode.get_balance(sif_addr, height=height, disable_log=disable_log, + retries_on_error=retries_on_error, delay_on_error=delay_on_error) def get_current_block(self): - return int(self.status()["SyncInfo"]["latest_block_height"]) + return self.sifnode.get_current_block() def status(self): - args = ["status"] + self.sifnode_client._node_args() - res = self.sifnode.sifnoded_exec(args) - return json.loads(stderr(res)) - - # Unless timed out, this function will exit: - # - if min_changes are given: when changes are greater. - # - if expected_balance is given: when balances are equal to that. - # - if neither min_changes nor expected_balance are given: when anything changes. - # You cannot use min_changes and expected_balance at the same time. - def wait_for_sif_balance_change(self, sif_addr: cosmos.Address, old_balance: cosmos.Balance, - min_changes: cosmos.CompatBalance = None, expected_balance: cosmos.CompatBalance = None, - polling_time: Optional[int] = None, timeout: Optional[int] = None, change_timeout: Optional[int] = None, - disable_log: bool = True - ) -> cosmos.Balance: - polling_time = polling_time if polling_time is not None else self.wait_for_sif_balance_change_default_polling_time - timeout = timeout if timeout is not None else self.wait_for_sif_balance_change_default_timeout - change_timeout = change_timeout if change_timeout is not None else self.wait_for_sif_balance_change_default_timeout - assert (min_changes is None) or (expected_balance is None), "Cannot use both min_changes and expected_balance" - log.debug("Waiting for balance to change for account {}...".format(sif_addr)) - min_changes = None if min_changes is None else cosmos.balance_normalize(min_changes) - expected_balance = None if expected_balance is None else cosmos.balance_normalize(expected_balance) - start_time = time.time() - last_change_time = None - last_changed_balance = None - while True: - new_balance = self.get_sifchain_balance(sif_addr, disable_log=disable_log) - delta = cosmos.balance_sub(new_balance, old_balance) - if expected_balance is not None: - should_return = cosmos.balance_equal(expected_balance, new_balance) - elif min_changes is not None: - should_return = cosmos.balance_exceeds(delta, min_changes) - else: - should_return = not cosmos.balance_zero(delta) - if should_return: - return new_balance - now = time.time() - if (timeout is not None) and (timeout > 0) and (now - start_time > timeout): - raise Exception("Timeout waiting for sif balance to change") - if last_change_time is None: - last_changed_balance = new_balance - last_change_time = now - else: - delta = cosmos.balance_sub(new_balance, last_changed_balance) - if not cosmos.balance_zero(delta): - last_changed_balance = new_balance - last_change_time = now - log.debug("New state detected ({} denoms changed)".format(len(delta))) - if (change_timeout is not None) and (change_timeout > 0) and (now - last_change_time > change_timeout): - raise Exception("Timeout waiting for sif balance to change") - time.sleep(polling_time) + return self.sifnode.status() def eth_symbol_to_sif_symbol(self, eth_token_symbol): + assert not on_peggy2_branch # TODO sifchain.use sifchain_denom_hash() if on_peggy2_branch # E.g. "usdt" -> "cusdt" if eth_token_symbol == "erowan": @@ -825,7 +788,8 @@ def create_and_fund_eth_account(self, fund_from=None, fund_amount=None): self.eth.set_private_key(address, key) assert self.eth.get_eth_balance(address) == 0 if fund_amount is not None: - fund_from = fund_from or self.operator + fund_from = fund_from or self.eth_faucet + assert fund_from funder_balance_before = self.eth.get_eth_balance(fund_from) assert funder_balance_before >= fund_amount, "Cannot fund created account with ETH: {} needs {}, but has {}" \ .format(fund_from, fund_amount, funder_balance_before) @@ -902,13 +866,13 @@ def sanity_check(self): if on_peggy2_branch: pass else: - assert (self.sifnode_chain_id != "sifchain-testnet-1") or (bridge_bank_sc.address == "0x6CfD69783E3fFb44CBaaFF7F509a4fcF0d8e2835") - assert (self.sifnode_chain_id != "sifchain-devnet-1") or (bridge_bank_sc.address == "0x96DC6f02C66Bbf2dfbA934b8DafE7B2c08715A73") - assert (self.sifnode_chain_id != "localnet") or (bridge_bank_sc.address == "0x30753E4A8aad7F8597332E813735Def5dD395028") + assert (self.sifnode.chain_id != "sifchain-testnet-1") or (bridge_bank_sc.address == "0x6CfD69783E3fFb44CBaaFF7F509a4fcF0d8e2835") + assert (self.sifnode.chain_id != "sifchain-devnet-1") or (bridge_bank_sc.address == "0x96DC6f02C66Bbf2dfbA934b8DafE7B2c08715A73") + assert (self.sifnode.chain_id != "localnet") or (bridge_bank_sc.address == "0x30753E4A8aad7F8597332E813735Def5dD395028") assert bridge_bank_sc.functions.owner().call() == self.operator, \ "BridgeBank owner is {}, but OPERATOR is {}".format(bridge_bank_sc.functions.owner().call(), self.operator) operator_balance = self.eth.get_eth_balance(self.operator) / eth.ETH - assert operator_balance >= 1, "Insufficient operator balance, should be at least 1 ETH" + assert operator_balance >= 1, "Insufficient operator balance {} ETH, should be at least 1 ETH".format(operator_balance) available_accounts = self.sifnode.keys_list() rowan_source_account = [x for x in available_accounts if x["address"] == self.rowan_source] diff --git a/test/integration/src/features/margin.py b/test/integration/src/features/margin.py index 6cfc19d45e..49069ba5f8 100644 --- a/test/integration/src/features/margin.py +++ b/test/integration/src/features/margin.py @@ -49,6 +49,15 @@ def with_test_env(self, pool_setup): pool_definitions = {denom: (decimals, native_amount, external_amount) for denom, decimals, native_amount, external_amount, _ in pool_setup} env.setup_liquidity_pools_simple(pool_definitions) + + # Enable margin on all pools + mtp_enabled_pools = set(denom for denom, _, _, _, _ in pool_setup) + margin_params_before = env.sifnoded.query_margin_params() + env.sifnoded.tx_margin_update_pools(env.clp_admin, mtp_enabled_pools, [], broadcast_mode="block") + margin_params_after = env.sifnoded.query_margin_params() + assert len(margin_params_before["params"]["pools"]) == 0 + assert set(margin_params_after["params"]["pools"]) == mtp_enabled_pools + yield env, env.sifnoded, pool_definitions env.close() @@ -119,7 +128,7 @@ def test_swap_external_to_external(self): balances.append(sifnoded.get_balance(account)) pools.append(sifnoded.query_pools_sorted()) - env.fund(account, {ROWAN: 10**25, src_denom: swap_amount}) + env.fund(account, {ROWAN: 10**20, src_denom: swap_amount}) balance_before = sifnoded.get_balance(account) pools_before = sifnoded.query_pools_sorted() @@ -160,5 +169,56 @@ def test_swap_external_to_external(self): assert True # no-op line just for setting a breakpoint def test_swap(self): - self.test_swap_rowan_to_external() + # self.test_swap_rowan_to_external() # self.test_swap_external_to_external() + self.test_margin() + + def test_margin(self): + borrow_asset = "rowan" + collateral_asset = "cusdc" + collateral_amount = 10**20 + leverage = 2 + + with self.with_test_env(self.default_pool_setup) as (env, sifnoded, pools_definitions): + account = sifnoded.create_addr() + env.fund(account, { + ROWAN: 10**25, + collateral_asset: 10**25, + }) + margin_params = sifnoded.query_margin_params() + + # sifnoded.tx_margin_whitelist(env.clp_admin, account, broadcast_mode="block") + + pool_before_open = sifnoded.query_pools_sorted()[collateral_asset] + balance_before_open = sifnoded.get_balance(account) + mtp_positions_before_open = sifnoded.query_margin_positions_for_address(account) + res = sifnoded.margin_open_simple(account, borrow_asset, collateral_asset=collateral_asset, + collateral_amount=collateral_amount, leverage=leverage, position="long") + mtp_id = int(res["id"]) + pool_after_open = sifnoded.query_pools_sorted()[collateral_asset] + balance_after_open = sifnoded.get_balance(account) + mtp_positions_after_open = sifnoded.query_margin_positions_for_address(account) + + assert len(mtp_positions_before_open) == 0 + assert len(mtp_positions_after_open) == 1 + + open_borrow_delta = balance_after_open.get(borrow_asset, 0) - balance_before_open.get(borrow_asset, 0) + open_collateral_delta = balance_after_open.get(collateral_asset, 0) - balance_before_open.get(collateral_asset, 0) + + # TODO Why does the open position disappear after 4 blocks? + # Whitelisting does not help + for i in range(10): + cnt = len(sifnoded.query_margin_positions_for_address(account)) + if cnt == 0: + break + sifnoded.wait_for_last_transaction_to_be_mined() + + # TODO + + pool_before_close = sifnoded.query_pools_sorted()[collateral_asset] + balance_before_close = sifnoded.get_balance(account) + res2 = sifnoded.tx_margin_close(account, mtp_id, broadcast_mode="block") + pool_after_close = sifnoded.query_pools_sorted()[collateral_asset] + balance_after_close = sifnoded.get_balance(account) + + assert True diff --git a/test/integration/src/py/test_sifnode_peggy_pause_unpause.py b/test/integration/src/py/test_sifnode_peggy_pause_unpause.py new file mode 100644 index 0000000000..9fd1a71e03 --- /dev/null +++ b/test/integration/src/py/test_sifnode_peggy_pause_unpause.py @@ -0,0 +1,115 @@ +from typing import Tuple +import pytest + +import siftool_path +from siftool import eth, test_utils, sifchain, cosmos +from siftool.inflate_tokens import InflateTokens +from siftool.common import * +from siftool.test_utils import EnvCtx + +def test_pause_unpause_no_error(ctx: EnvCtx): + res = ctx.sifnode.pause_peggy_bridge(ctx.sifchain_ethbridge_admin_account) + assert res[0]['code'] == 0 + res = ctx.sifnode.unpause_peggy_bridge(ctx.sifchain_ethbridge_admin_account) + assert res[0]['code'] == 0 + +# We assert a tx is successful before pausing because we test the pause +# functionality by 1. An error response and 2. Balance unchanged within timeout. +# We want to make sure #2 is not a false positive due to lock function not +# working in the first place +def test_pause_lock_valid(ctx: EnvCtx): + # Test a working flow: + fund_amount_sif = 10 * test_utils.sifnode_funds_for_transfer_peggy1 + fund_amount_eth = 1 * eth.ETH + + test_sif_account = ctx.create_sifchain_addr(fund_amounts=[[fund_amount_sif, "rowan"]]) + ctx.tx_bridge_bank_lock_eth(ctx.eth_faucet, test_sif_account, fund_amount_eth) + ctx.eth.advance_blocks() + # Setup is complete, test account has rowan AND eth + + test_eth_destination_account = ctx.create_and_fund_eth_account() + + send_amount = 1 + balance_diff, erc_diff = send_test_account(ctx, test_sif_account, test_eth_destination_account, send_amount, erc20_token_addr=ctx.get_bridge_token_sc().address) + # TODO: sif_tx_fee vs get from envctx vs more lenient assertion + assert balance_diff.get(sifchain.ROWAN, 0) == (-1 * (send_amount + sifchain.sif_tx_fee_in_rowan )), "Gas fee and sent amount should be deducted from sender sif acct" + assert erc_diff == send_amount, "Eth destination should receive rowan token" + + res = ctx.sifnode.pause_peggy_bridge(ctx.sifchain_ethbridge_admin_account) + assert res[0]['code'] == 0 + + balance_diff, erc_diff = send_test_account(ctx, test_sif_account, test_eth_destination_account, send_amount, erc20_token_addr=ctx.get_bridge_token_sc().address) + assert balance_diff.get(sifchain.ROWAN, 0) == (-1 * sifchain.sif_tx_fee_in_rowan), "Only gas fee should be deducted for attempted tx" + assert erc_diff == 0, "Eth destination should not receive rowan token" + + res = ctx.sifnode.unpause_peggy_bridge(ctx.sifchain_ethbridge_admin_account) + assert res[0]['code'] == 0 + # # Submit lock + # # Assert tx go through, balance updated correctly. + send_amount = 15 + balance_diff, erc_diff = send_test_account(ctx, test_sif_account, test_eth_destination_account, send_amount, erc20_token_addr=ctx.get_bridge_token_sc().address) + # TODO: sif_tx_fee vs get from envctx vs more lenient assertion + assert balance_diff.get(sifchain.ROWAN, 0) == (-1 * (send_amount + sifchain.sif_tx_fee_in_rowan )), "Gas fee and sent amount should be deducted from sender sif acct" + assert erc_diff == send_amount, "Eth destination should receive rowan token" + +# Burn CETH +def test_pause_burn_valid(ctx: EnvCtx): + fund_amount_sif = 10 * test_utils.sifnode_funds_for_transfer_peggy1 + fund_amount_eth = 1 * eth.ETH + + test_sif_account = ctx.create_sifchain_addr(fund_amounts=[[fund_amount_sif, "rowan"]]) + sif_account_balance_before = ctx.get_sifchain_balance(test_sif_account) + ctx.tx_bridge_bank_lock_eth(ctx.eth_faucet, test_sif_account, fund_amount_eth) + ctx.eth.advance_blocks(100) + # Setup is complete, test account has rowan AND eth + ctx.sifnode.wait_for_balance_change(test_sif_account, sif_account_balance_before) + test_eth_destination_account = ctx.create_and_fund_eth_account() + + send_amount = 1 + balance_diff, erc_diff = send_test_account(ctx, test_sif_account, test_eth_destination_account, send_amount, denom=sifchain.CETH, erc20_token_addr=None) + # TODO: sif_tx_fee vs get from envctx vs more lenient assertion + gas_cost = 160000000000 * 393000 # Taken from peggy1 + assert balance_diff.get(sifchain.ROWAN, 0) == (-1 * sifchain.sif_tx_fee_in_rowan), "Gas fee should be deducted from sender sif acct" + assert balance_diff.get(sifchain.CETH, 0) == (-1 * (send_amount + gas_cost)), "Sent amount should be deducted from sender sif acct ceth balance" + assert erc_diff == send_amount, "Eth destination should receive rowan token" + + res = ctx.sifnode.pause_peggy_bridge(ctx.sifchain_ethbridge_admin_account) + assert res[0]['code'] == 0 + + send_amount = 1 + balance_diff, erc_diff = send_test_account(ctx, test_sif_account, test_eth_destination_account, send_amount, denom=sifchain.CETH, erc20_token_addr=None) + assert balance_diff.get(sifchain.ROWAN, 0) == (-1 * sifchain.sif_tx_fee_in_rowan), "Only gas fee should be deducted for attempted tx" + assert balance_diff.get(sifchain.CETH, 0) == 0, "Eth amount should'nt be deducted, no tx to evm" + assert erc_diff == 0, "Eth destination should not receive rowan token" + + + res = ctx.sifnode.unpause_peggy_bridge(ctx.sifchain_ethbridge_admin_account) + assert res[0]['code'] == 0 + + send_amount = 15 + balance_diff, erc_diff = send_test_account(ctx, test_sif_account, test_eth_destination_account, send_amount, denom=sifchain.CETH, erc20_token_addr=None) + # TODO: sif_tx_fee vs get from envctx vs more lenient assertion + gas_cost = 160000000000 * 393000 # Taken from peggy1 + assert balance_diff.get(sifchain.ROWAN, 0) == (-1 * sifchain.sif_tx_fee_in_rowan), "Gas fee should be deducted from sender sif acct" + assert balance_diff.get(sifchain.CETH, 0) == (-1 * (send_amount + gas_cost)), "Sent amount should be deducted from sender sif acct ceth balance" + assert erc_diff == send_amount, "Eth destination should receive rowan token" + +# This is a temporary helper method. It will eventually be incorporated into siftool +def send_test_account(ctx: EnvCtx, test_sif_account, test_eth_destination_account, send_amount, denom=sifchain.ROWAN, erc20_token_addr: str=None) -> Tuple[cosmos.Balance, int]: + sif_balance_before = ctx.get_sifchain_balance(test_sif_account) + if erc20_token_addr is not None: + eth_balance_before = ctx.get_erc20_token_balance(erc20_token_addr, test_eth_destination_account) + else: + eth_balance_before = ctx.eth.get_eth_balance(test_eth_destination_account) + + ctx.sifnode_client.send_from_sifchain_to_ethereum(test_sif_account, test_eth_destination_account, send_amount, denom) + + sif_balance_after = ctx.sifnode.wait_for_balance_change(test_sif_account, sif_balance_before) + try: + eth_balance_after = ctx.wait_for_eth_balance_change(test_eth_destination_account, eth_balance_before, token_addr=erc20_token_addr, timeout=30) + except Exception as e: + # wait_for_eth_balance_change raises exception only if timedout, implying old_balance == new_balance + eth_balance_after = eth_balance_before + + balance_diff = sifchain.balance_delta(sif_balance_before, sif_balance_after) + return balance_diff, (eth_balance_after - eth_balance_before) \ No newline at end of file diff --git a/test/load/test_many_pools_and_liquidity_providers.py b/test/load/test_many_pools_and_liquidity_providers.py index 0d48f0859e..1610ff14dc 100644 --- a/test/load/test_many_pools_and_liquidity_providers.py +++ b/test/load/test_many_pools_and_liquidity_providers.py @@ -1,6 +1,6 @@ # This is for load testing of LPD/rewardss (and in future, margin) # -# Scenarion description: https://www.notion.so/nodechain/Rewards-2-0-Load-Testing-972fbe73b04440cd87232aa60a3146c5 +# Scenario description: https://www.notion.so/sifchain/Rewards-2-0-Load-Testing-972fbe73b04440cd87232aa60a3146c5 # Ticket: https://app.zenhub.com/workspaces/current-sprint---engineering-615a2e9fe2abd5001befc7f9/issues/sifchain/sifnode/3020 # How to run a validator in multi-node setup: # - https://docs.sifchain.finance/network-security/validators/running-sifnode-and-becoming-a-validator diff --git a/version b/version index 08d20492e0..0a0bfd8dc9 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.0-beta.13 +1.1.0-beta.rc1 diff --git a/x/admin/client/cli/query.go b/x/admin/client/cli/query.go index 8371563d8f..8a1b24c112 100644 --- a/x/admin/client/cli/query.go +++ b/x/admin/client/cli/query.go @@ -18,7 +18,7 @@ func GetQueryCmd() *cobra.Command { SuggestionsMinimumDistance: 2, RunE: client.ValidateCmd, } - cmd.AddCommand(GetCmdAccounts()) + cmd.AddCommand(GetCmdAccounts(), GetCmdParams()) return cmd } @@ -43,3 +43,25 @@ func GetCmdAccounts() *cobra.Command { flags.AddQueryFlagsToCmd(cmd) return cmd } + +func GetCmdParams() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "query params", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + res, err := queryClient.GetParams(context.Background(), &types.GetParamsRequest{}) + if err != nil { + return err + } + return clientCtx.PrintBytes(clientCtx.Codec.MustMarshalJSON(res)) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} diff --git a/x/admin/client/cli/tx.go b/x/admin/client/cli/tx.go index 1dfdb2eb9e..59b5b1d623 100644 --- a/x/admin/client/cli/tx.go +++ b/x/admin/client/cli/tx.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/spf13/cobra" + "github.com/spf13/viper" ) // GetTxCmd returns the transaction commands for this module @@ -20,6 +21,7 @@ func GetTxCmd() *cobra.Command { cmd.AddCommand( GetCmdAdd(), GetCmdRemove(), + GetCmdSetParams(), ) return cmd } @@ -119,3 +121,33 @@ func GetCmdRemove() *cobra.Command { flags.AddTxFlagsToCmd(cmd) return cmd } + +func GetCmdSetParams() *cobra.Command { + cmd := &cobra.Command{ + Use: "set-params", + Short: "Set parameters", + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + fee := sdk.NewUintFromString(viper.GetString("submit-proposal-fee")) + + msg := types.MsgSetParams{ + Signer: clientCtx.GetFromAddress().String(), + Params: &types.Params{ + SubmitProposalFee: fee, + }, + } + if err := msg.ValidateBasic(); err != nil { + return err + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + cmd.Flags().String("submit-proposal-fee", "5000000000000000000000", "fee to submit proposals") + _ = cmd.MarkFlagRequired("submit-proposal-fee") + flags.AddTxFlagsToCmd(cmd) + return cmd +} diff --git a/x/admin/keeper/grpc_query.go b/x/admin/keeper/grpc_query.go index f3a2849f43..38e3214e84 100644 --- a/x/admin/keeper/grpc_query.go +++ b/x/admin/keeper/grpc_query.go @@ -18,6 +18,10 @@ func (q Querier) ListAccounts(ctx context.Context, _ *types.ListAccountsRequest) }, nil } +func (q Querier) GetParams(ctx context.Context, _ *types.GetParamsRequest) (*types.GetParamsResponse, error) { + return &types.GetParamsResponse{Params: q.Keeper.GetParams(sdk.UnwrapSDKContext(ctx))}, nil +} + func NewQueryServer(k Keeper) types.QueryServer { return Querier{k} } diff --git a/x/admin/keeper/keeper.go b/x/admin/keeper/keeper.go index ea6d7ec52a..4b1302e0b6 100644 --- a/x/admin/keeper/keeper.go +++ b/x/admin/keeper/keeper.go @@ -100,3 +100,21 @@ func (k Keeper) GetAdminAccounts(ctx sdk.Context) []*types.AdminAccount { } return accounts } + +func (k Keeper) SetParams(ctx sdk.Context, params *types.Params) { + store := ctx.KVStore(k.storeKey) + store.Set(types.ParamsStorePrefix, k.cdc.MustMarshal(params)) +} + +func (k Keeper) GetParams(ctx sdk.Context) *types.Params { + defaultSubmitProposalFee := sdk.NewUintFromString("5000000000000000000000") // 5000 + + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.ParamsStorePrefix) + if bz == nil { + return &types.Params{SubmitProposalFee: defaultSubmitProposalFee} + } + var params types.Params + k.cdc.MustUnmarshal(bz, ¶ms) + return ¶ms +} diff --git a/x/admin/keeper/msg_server.go b/x/admin/keeper/msg_server.go index f459ee161d..20cfb7f599 100644 --- a/x/admin/keeper/msg_server.go +++ b/x/admin/keeper/msg_server.go @@ -12,6 +12,20 @@ type msgServer struct { keeper Keeper } +func (m msgServer) SetParams(ctx context.Context, msg *types.MsgSetParams) (*types.MsgSetParamsResponse, error) { + addr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return nil, err + } + + if !m.keeper.IsAdminAccount(sdk.UnwrapSDKContext(ctx), types.AdminType_ADMIN, addr) { + return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "unauthorised signer") + } + + m.keeper.SetParams(sdk.UnwrapSDKContext(ctx), msg.Params) + return &types.MsgSetParamsResponse{}, nil +} + func (m msgServer) AddAccount(ctx context.Context, msg *types.MsgAddAccount) (*types.MsgAddAccountResponse, error) { addr, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { diff --git a/x/admin/types/keys.go b/x/admin/types/keys.go index e4e0a86cb6..175afcb74a 100644 --- a/x/admin/types/keys.go +++ b/x/admin/types/keys.go @@ -3,6 +3,7 @@ package types import "fmt" var AdminAccountStorePrefix = []byte{0x01} +var ParamsStorePrefix = []byte{0x02} func GetAdminAccountKey(adminAccount AdminAccount) []byte { key := []byte(fmt.Sprintf("%s_%s", adminAccount.AdminType.String(), adminAccount.AdminAddress)) diff --git a/x/admin/types/msgs.go b/x/admin/types/msgs.go index 1cc2a6d3b0..dff7f9bd74 100644 --- a/x/admin/types/msgs.go +++ b/x/admin/types/msgs.go @@ -7,8 +7,10 @@ import ( var _ sdk.Msg = &MsgAddAccount{} var _ sdk.Msg = &MsgRemoveAccount{} +var _ sdk.Msg = &MsgSetParams{} var _ legacytx.LegacyMsg = &MsgAddAccount{} var _ legacytx.LegacyMsg = &MsgRemoveAccount{} +var _ legacytx.LegacyMsg = &MsgSetParams{} func (m *MsgAddAccount) Route() string { return RouterKey @@ -57,3 +59,27 @@ func (m *MsgRemoveAccount) GetSigners() []sdk.AccAddress { } return []sdk.AccAddress{addr} } + +func (m *MsgSetParams) Route() string { + return RouterKey +} + +func (m *MsgSetParams) Type() string { + return "set_params" +} + +func (m *MsgSetParams) ValidateBasic() error { + return nil +} + +func (m *MsgSetParams) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(m)) +} + +func (m *MsgSetParams) GetSigners() []sdk.AccAddress { + addr, err := sdk.AccAddressFromBech32(m.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{addr} +} diff --git a/x/admin/types/query.pb.go b/x/admin/types/query.pb.go index bd56146743..7daf6de8dc 100644 --- a/x/admin/types/query.pb.go +++ b/x/admin/types/query.pb.go @@ -109,32 +109,118 @@ func (m *ListAccountsResponse) GetKeys() []*AdminAccount { return nil } +type GetParamsRequest struct { +} + +func (m *GetParamsRequest) Reset() { *m = GetParamsRequest{} } +func (m *GetParamsRequest) String() string { return proto.CompactTextString(m) } +func (*GetParamsRequest) ProtoMessage() {} +func (*GetParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_3e062bad86f8e9de, []int{2} +} +func (m *GetParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetParamsRequest.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 *GetParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetParamsRequest.Merge(m, src) +} +func (m *GetParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *GetParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetParamsRequest proto.InternalMessageInfo + +type GetParamsResponse struct { + Params *Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` +} + +func (m *GetParamsResponse) Reset() { *m = GetParamsResponse{} } +func (m *GetParamsResponse) String() string { return proto.CompactTextString(m) } +func (*GetParamsResponse) ProtoMessage() {} +func (*GetParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_3e062bad86f8e9de, []int{3} +} +func (m *GetParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetParamsResponse.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 *GetParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetParamsResponse.Merge(m, src) +} +func (m *GetParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *GetParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetParamsResponse proto.InternalMessageInfo + +func (m *GetParamsResponse) GetParams() *Params { + if m != nil { + return m.Params + } + return nil +} + func init() { proto.RegisterType((*ListAccountsRequest)(nil), "sifnode.admin.v1.ListAccountsRequest") proto.RegisterType((*ListAccountsResponse)(nil), "sifnode.admin.v1.ListAccountsResponse") + proto.RegisterType((*GetParamsRequest)(nil), "sifnode.admin.v1.GetParamsRequest") + proto.RegisterType((*GetParamsResponse)(nil), "sifnode.admin.v1.GetParamsResponse") } func init() { proto.RegisterFile("sifnode/admin/v1/query.proto", fileDescriptor_3e062bad86f8e9de) } var fileDescriptor_3e062bad86f8e9de = []byte{ - // 265 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x29, 0xce, 0x4c, 0xcb, - 0xcb, 0x4f, 0x49, 0xd5, 0x4f, 0x4c, 0xc9, 0xcd, 0xcc, 0xd3, 0x2f, 0x33, 0xd4, 0x2f, 0x2c, 0x4d, - 0x2d, 0xaa, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x80, 0xca, 0xea, 0x81, 0x65, 0xf5, - 0xca, 0x0c, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x92, 0xfa, 0x20, 0x16, 0x44, 0x9d, 0x94, - 0x4c, 0x7a, 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x7e, 0x62, 0x41, 0xa6, 0x7e, 0x62, 0x5e, 0x5e, 0x7e, - 0x49, 0x62, 0x49, 0x66, 0x7e, 0x5e, 0x31, 0x4c, 0x16, 0xc3, 0x8e, 0x92, 0xca, 0x82, 0x54, 0xa8, - 0xac, 0x92, 0x28, 0x97, 0xb0, 0x4f, 0x66, 0x71, 0x89, 0x63, 0x72, 0x72, 0x7e, 0x69, 0x5e, 0x49, - 0x71, 0x50, 0x6a, 0x61, 0x69, 0x6a, 0x71, 0x89, 0x92, 0x17, 0x97, 0x08, 0xaa, 0x70, 0x71, 0x41, - 0x7e, 0x5e, 0x71, 0xaa, 0x90, 0x11, 0x17, 0x4b, 0x76, 0x6a, 0x65, 0xb1, 0x04, 0x93, 0x02, 0xb3, - 0x06, 0xb7, 0x91, 0x9c, 0x1e, 0xba, 0x0b, 0xf5, 0x1c, 0x41, 0x0c, 0xa8, 0xb6, 0x20, 0xb0, 0x5a, - 0xa3, 0x0c, 0x2e, 0xd6, 0x40, 0x90, 0xaf, 0x84, 0xe2, 0xb9, 0x78, 0x90, 0x0d, 0x15, 0x52, 0xc5, - 0xd4, 0x8e, 0xc5, 0x2d, 0x52, 0x6a, 0x84, 0x94, 0x41, 0xdc, 0xa6, 0xc4, 0xe0, 0xe4, 0x7c, 0xe2, - 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, - 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x9a, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, - 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0xc1, 0x99, 0x69, 0xc9, 0x19, 0x89, 0x99, 0x79, 0xfa, 0xb0, 0x80, - 0xa9, 0x80, 0x06, 0x0d, 0x38, 0x5c, 0x92, 0xd8, 0xc0, 0x01, 0x63, 0x0c, 0x08, 0x00, 0x00, 0xff, - 0xff, 0xc4, 0x67, 0x5c, 0x86, 0x9c, 0x01, 0x00, 0x00, + // 325 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x51, 0xcb, 0x4e, 0x02, 0x31, + 0x14, 0x9d, 0xf1, 0x41, 0x62, 0x71, 0x81, 0x15, 0x13, 0x32, 0x21, 0x0d, 0x19, 0xa3, 0xc1, 0xcd, + 0x54, 0xc6, 0x2f, 0x40, 0x63, 0x4c, 0x8c, 0x0b, 0xc5, 0xc4, 0x85, 0x1b, 0x53, 0x86, 0x32, 0x34, + 0x4a, 0xef, 0x40, 0x3b, 0x44, 0xfe, 0xc2, 0x4f, 0x72, 0xe9, 0x92, 0xa5, 0x4b, 0x03, 0x3f, 0x62, + 0x28, 0x1d, 0x82, 0x8c, 0xd1, 0xdd, 0xcd, 0x3d, 0x8f, 0x9e, 0xdb, 0x83, 0xaa, 0x4a, 0x74, 0x25, + 0x74, 0x38, 0x65, 0x9d, 0xbe, 0x90, 0x74, 0xd4, 0xa0, 0x83, 0x94, 0x0f, 0xc7, 0x41, 0x32, 0x04, + 0x0d, 0xb8, 0x64, 0xd1, 0xc0, 0xa0, 0xc1, 0xa8, 0xe1, 0x95, 0x63, 0x88, 0xc1, 0x80, 0x74, 0x3e, + 0x2d, 0x78, 0x5e, 0x35, 0x06, 0x88, 0x5f, 0x38, 0x65, 0x89, 0xa0, 0x4c, 0x4a, 0xd0, 0x4c, 0x0b, + 0x90, 0x2a, 0x43, 0x73, 0x6f, 0xe8, 0x71, 0xc2, 0x2d, 0xea, 0x1f, 0xa0, 0xfd, 0x1b, 0xa1, 0x74, + 0x33, 0x8a, 0x20, 0x95, 0x5a, 0xb5, 0xf8, 0x20, 0xe5, 0x4a, 0xfb, 0xd7, 0xa8, 0xfc, 0x73, 0xad, + 0x12, 0x90, 0x8a, 0xe3, 0x10, 0x6d, 0x3d, 0xf3, 0xb1, 0xaa, 0x6c, 0xd4, 0x36, 0xeb, 0xc5, 0x90, + 0x04, 0xeb, 0x09, 0x83, 0xe6, 0x7c, 0xb0, 0xb2, 0x96, 0xe1, 0xfa, 0x18, 0x95, 0xae, 0xb8, 0xbe, + 0x65, 0x43, 0xd6, 0x5f, 0xfa, 0x5f, 0xa2, 0xbd, 0x95, 0x9d, 0x35, 0x3f, 0x45, 0x85, 0xc4, 0x6c, + 0x2a, 0x6e, 0xcd, 0xad, 0x17, 0xc3, 0x4a, 0xde, 0xde, 0x2a, 0x2c, 0x2f, 0x7c, 0x77, 0xd1, 0xf6, + 0xdd, 0xfc, 0xc7, 0xf0, 0x13, 0xda, 0x5d, 0x0d, 0x8c, 0x8f, 0xf2, 0xda, 0x5f, 0xee, 0xf4, 0x8e, + 0xff, 0xa3, 0x2d, 0xa2, 0xf9, 0x0e, 0x7e, 0x40, 0x3b, 0xcb, 0xc4, 0xd8, 0xcf, 0xcb, 0xd6, 0x4f, + 0xf4, 0x0e, 0xff, 0xe4, 0x64, 0xbe, 0xe7, 0x17, 0x1f, 0x53, 0xe2, 0x4e, 0xa6, 0xc4, 0xfd, 0x9a, + 0x12, 0xf7, 0x6d, 0x46, 0x9c, 0xc9, 0x8c, 0x38, 0x9f, 0x33, 0xe2, 0x3c, 0x9e, 0xc4, 0x42, 0xf7, + 0xd2, 0x76, 0x10, 0x41, 0x9f, 0xde, 0x8b, 0x6e, 0xd4, 0x63, 0x42, 0xd2, 0xac, 0xcc, 0x57, 0x5b, + 0xa7, 0xe9, 0xb2, 0x5d, 0x30, 0x65, 0x9e, 0x7d, 0x07, 0x00, 0x00, 0xff, 0xff, 0x53, 0x9d, 0x55, + 0x9e, 0x50, 0x02, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -150,6 +236,7 @@ const _ = grpc.SupportPackageIsVersion4 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type QueryClient interface { ListAccounts(ctx context.Context, in *ListAccountsRequest, opts ...grpc.CallOption) (*ListAccountsResponse, error) + GetParams(ctx context.Context, in *GetParamsRequest, opts ...grpc.CallOption) (*GetParamsResponse, error) } type queryClient struct { @@ -169,9 +256,19 @@ func (c *queryClient) ListAccounts(ctx context.Context, in *ListAccountsRequest, return out, nil } +func (c *queryClient) GetParams(ctx context.Context, in *GetParamsRequest, opts ...grpc.CallOption) (*GetParamsResponse, error) { + out := new(GetParamsResponse) + err := c.cc.Invoke(ctx, "/sifnode.admin.v1.Query/GetParams", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { ListAccounts(context.Context, *ListAccountsRequest) (*ListAccountsResponse, error) + GetParams(context.Context, *GetParamsRequest) (*GetParamsResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -181,6 +278,9 @@ type UnimplementedQueryServer struct { func (*UnimplementedQueryServer) ListAccounts(ctx context.Context, req *ListAccountsRequest) (*ListAccountsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListAccounts not implemented") } +func (*UnimplementedQueryServer) GetParams(ctx context.Context, req *GetParamsRequest) (*GetParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetParams not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -204,6 +304,24 @@ func _Query_ListAccounts_Handler(srv interface{}, ctx context.Context, dec func( return interceptor(ctx, in, info, handler) } +func _Query_GetParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).GetParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/sifnode.admin.v1.Query/GetParams", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).GetParams(ctx, req.(*GetParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "sifnode.admin.v1.Query", HandlerType: (*QueryServer)(nil), @@ -212,6 +330,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "ListAccounts", Handler: _Query_ListAccounts_Handler, }, + { + MethodName: "GetParams", + Handler: _Query_GetParams_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "sifnode/admin/v1/query.proto", @@ -277,6 +399,64 @@ func (m *ListAccountsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *GetParamsRequest) 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 *GetParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *GetParamsResponse) 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 *GetParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Params != nil { + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -312,6 +492,28 @@ func (m *ListAccountsResponse) Size() (n int) { return n } +func (m *GetParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *GetParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Params != nil { + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -452,6 +654,142 @@ func (m *ListAccountsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *GetParamsRequest) 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 ErrIntOverflowQuery + } + 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: GetParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetParamsResponse) 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 ErrIntOverflowQuery + } + 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: GetParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetParamsResponse: 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 ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = &Params{} + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/admin/types/tx.pb.go b/x/admin/types/tx.pb.go index 7b1a134363..868eca7391 100644 --- a/x/admin/types/tx.pb.go +++ b/x/admin/types/tx.pb.go @@ -204,36 +204,129 @@ func (m *MsgRemoveAccountResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgRemoveAccountResponse proto.InternalMessageInfo +type MsgSetParams struct { + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + Params *Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params,omitempty"` +} + +func (m *MsgSetParams) Reset() { *m = MsgSetParams{} } +func (m *MsgSetParams) String() string { return proto.CompactTextString(m) } +func (*MsgSetParams) ProtoMessage() {} +func (*MsgSetParams) Descriptor() ([]byte, []int) { + return fileDescriptor_600acd904f18192e, []int{4} +} +func (m *MsgSetParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSetParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSetParams.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 *MsgSetParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetParams.Merge(m, src) +} +func (m *MsgSetParams) XXX_Size() int { + return m.Size() +} +func (m *MsgSetParams) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetParams.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSetParams proto.InternalMessageInfo + +func (m *MsgSetParams) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +func (m *MsgSetParams) GetParams() *Params { + if m != nil { + return m.Params + } + return nil +} + +type MsgSetParamsResponse struct { +} + +func (m *MsgSetParamsResponse) Reset() { *m = MsgSetParamsResponse{} } +func (m *MsgSetParamsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSetParamsResponse) ProtoMessage() {} +func (*MsgSetParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_600acd904f18192e, []int{5} +} +func (m *MsgSetParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSetParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSetParamsResponse.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 *MsgSetParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetParamsResponse.Merge(m, src) +} +func (m *MsgSetParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSetParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSetParamsResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*MsgAddAccount)(nil), "sifnode.admin.v1.MsgAddAccount") proto.RegisterType((*MsgAddAccountResponse)(nil), "sifnode.admin.v1.MsgAddAccountResponse") proto.RegisterType((*MsgRemoveAccount)(nil), "sifnode.admin.v1.MsgRemoveAccount") proto.RegisterType((*MsgRemoveAccountResponse)(nil), "sifnode.admin.v1.MsgRemoveAccountResponse") + proto.RegisterType((*MsgSetParams)(nil), "sifnode.admin.v1.MsgSetParams") + proto.RegisterType((*MsgSetParamsResponse)(nil), "sifnode.admin.v1.MsgSetParamsResponse") } func init() { proto.RegisterFile("sifnode/admin/v1/tx.proto", fileDescriptor_600acd904f18192e) } var fileDescriptor_600acd904f18192e = []byte{ - // 293 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2c, 0xce, 0x4c, 0xcb, - 0xcb, 0x4f, 0x49, 0xd5, 0x4f, 0x4c, 0xc9, 0xcd, 0xcc, 0xd3, 0x2f, 0x33, 0xd4, 0x2f, 0xa9, 0xd0, - 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x80, 0x4a, 0xe9, 0x81, 0xa5, 0xf4, 0xca, 0x0c, 0xa5, - 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x92, 0xfa, 0x20, 0x16, 0x44, 0x9d, 0x94, 0x0c, 0xa6, 0x11, - 0x95, 0x05, 0xa9, 0xc5, 0x10, 0x59, 0xa5, 0x44, 0x2e, 0x5e, 0xdf, 0xe2, 0x74, 0xc7, 0x94, 0x14, - 0xc7, 0xe4, 0xe4, 0xfc, 0xd2, 0xbc, 0x12, 0x21, 0x31, 0x2e, 0xb6, 0xe2, 0xcc, 0xf4, 0xbc, 0xd4, - 0x22, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x28, 0x4f, 0xc8, 0x82, 0x8b, 0x3d, 0x11, 0xa2, - 0x44, 0x82, 0x49, 0x81, 0x51, 0x83, 0xdb, 0x48, 0x4e, 0x0f, 0xdd, 0x01, 0x7a, 0x8e, 0x20, 0x06, - 0xd4, 0xa0, 0x20, 0x98, 0x72, 0x25, 0x71, 0x2e, 0x51, 0x14, 0x2b, 0x82, 0x52, 0x8b, 0x0b, 0xf2, - 0xf3, 0x8a, 0x53, 0x95, 0x52, 0xb8, 0x04, 0x7c, 0x8b, 0xd3, 0x83, 0x52, 0x73, 0xf3, 0xcb, 0x52, - 0x69, 0x67, 0xbd, 0x14, 0x97, 0x04, 0xba, 0x2d, 0x30, 0x17, 0x18, 0x1d, 0x62, 0xe4, 0x62, 0xf6, - 0x2d, 0x4e, 0x17, 0x8a, 0xe0, 0xe2, 0x42, 0x0a, 0x02, 0x79, 0x4c, 0xa3, 0x51, 0x3c, 0x20, 0xa5, - 0x4e, 0x40, 0x01, 0xdc, 0x87, 0x0c, 0x42, 0x89, 0x5c, 0xbc, 0xa8, 0x1e, 0x54, 0xc2, 0xaa, 0x17, - 0x45, 0x8d, 0x94, 0x16, 0x61, 0x35, 0x08, 0x2b, 0x9c, 0x9c, 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, 0x4a, 0x33, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, - 0x3f, 0x38, 0x33, 0x2d, 0x39, 0x23, 0x31, 0x33, 0x4f, 0x1f, 0x96, 0x1c, 0x2a, 0xa0, 0x09, 0x02, - 0x9c, 0x1a, 0x92, 0xd8, 0xc0, 0xc9, 0xc1, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xe2, 0xdf, 0x39, - 0xfd, 0x71, 0x02, 0x00, 0x00, + // 351 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x92, 0x3f, 0x4f, 0xfa, 0x40, + 0x18, 0xc7, 0x5b, 0x7e, 0x09, 0xbf, 0xf0, 0x28, 0x09, 0x69, 0x10, 0x6b, 0x63, 0x4e, 0xd2, 0x41, + 0xd1, 0xa1, 0x15, 0x5c, 0x5c, 0xd1, 0xb9, 0x89, 0x29, 0x31, 0x21, 0x6e, 0x47, 0x7b, 0x1c, 0x37, + 0xf4, 0xae, 0xe1, 0x0a, 0xc1, 0x77, 0xe1, 0xea, 0x3b, 0x72, 0x64, 0x74, 0x34, 0xf0, 0x46, 0x0c, + 0xed, 0xf1, 0xa7, 0x80, 0x32, 0xb9, 0x5d, 0xf3, 0xfd, 0xdc, 0xf7, 0x73, 0x7d, 0xf2, 0xc0, 0x99, + 0x64, 0x7d, 0x2e, 0x42, 0xe2, 0xe2, 0x30, 0x62, 0xdc, 0x1d, 0x37, 0xdd, 0x64, 0xe2, 0xc4, 0x43, + 0x91, 0x08, 0xa3, 0xa2, 0x22, 0x27, 0x8d, 0x9c, 0x71, 0xd3, 0xaa, 0x52, 0x41, 0x45, 0x1a, 0xba, + 0x8b, 0x53, 0xc6, 0x59, 0xe7, 0xbb, 0x15, 0xaf, 0x31, 0x91, 0x59, 0x6a, 0x63, 0x28, 0x7b, 0x92, + 0xb6, 0xc3, 0xb0, 0x1d, 0x04, 0x62, 0xc4, 0x13, 0xa3, 0x06, 0x45, 0xc9, 0x28, 0x27, 0x43, 0x53, + 0xaf, 0xeb, 0x8d, 0x92, 0xaf, 0xbe, 0x8c, 0x7b, 0xf8, 0x8f, 0x33, 0xc4, 0x2c, 0xd4, 0xf5, 0xc6, + 0x51, 0x0b, 0x39, 0xdb, 0x0f, 0x70, 0xda, 0x8b, 0x83, 0x2a, 0xf2, 0x97, 0xb8, 0x7d, 0x0a, 0x27, + 0x39, 0x85, 0x4f, 0x64, 0x2c, 0xb8, 0x24, 0x76, 0x08, 0x15, 0x4f, 0x52, 0x9f, 0x44, 0x62, 0x4c, + 0xfe, 0x4e, 0x6f, 0x81, 0xb9, 0x6d, 0x59, 0xbd, 0xa0, 0x0b, 0xc7, 0x9e, 0xa4, 0x1d, 0x92, 0x3c, + 0xe1, 0x21, 0x8e, 0xe4, 0x8f, 0xf6, 0x5b, 0x28, 0xc6, 0x29, 0xa1, 0xe4, 0xe6, 0xae, 0x3c, 0x6b, + 0xf0, 0x15, 0x67, 0xd7, 0xa0, 0xba, 0xd9, 0xbc, 0x34, 0xb6, 0xde, 0x0b, 0xf0, 0xcf, 0x93, 0xd4, + 0xe8, 0x02, 0x6c, 0x0c, 0xfd, 0x62, 0xb7, 0x2f, 0x37, 0x32, 0xeb, 0xea, 0x00, 0xb0, 0xfa, 0x23, + 0xcd, 0xc0, 0x50, 0xce, 0x8f, 0xd4, 0xde, 0x7b, 0x37, 0xc7, 0x58, 0x37, 0x87, 0x99, 0x0d, 0xc5, + 0x33, 0x94, 0xd6, 0x33, 0x43, 0x7b, 0xaf, 0xae, 0x72, 0xeb, 0xf2, 0xf7, 0x7c, 0x5d, 0xfb, 0xf0, + 0xf8, 0x31, 0x43, 0xfa, 0x74, 0x86, 0xf4, 0xaf, 0x19, 0xd2, 0xdf, 0xe6, 0x48, 0x9b, 0xce, 0x91, + 0xf6, 0x39, 0x47, 0xda, 0xcb, 0x35, 0x65, 0xc9, 0x60, 0xd4, 0x73, 0x02, 0x11, 0xb9, 0x1d, 0xd6, + 0x0f, 0x06, 0x98, 0x71, 0x77, 0xb9, 0xd7, 0x13, 0xb5, 0xd9, 0xe9, 0x5a, 0xf7, 0x8a, 0xe9, 0x5e, + 0xdf, 0x7d, 0x07, 0x00, 0x00, 0xff, 0xff, 0xd4, 0xe1, 0x12, 0x18, 0x3a, 0x03, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -250,6 +343,7 @@ const _ = grpc.SupportPackageIsVersion4 type MsgClient interface { AddAccount(ctx context.Context, in *MsgAddAccount, opts ...grpc.CallOption) (*MsgAddAccountResponse, error) RemoveAccount(ctx context.Context, in *MsgRemoveAccount, opts ...grpc.CallOption) (*MsgRemoveAccountResponse, error) + SetParams(ctx context.Context, in *MsgSetParams, opts ...grpc.CallOption) (*MsgSetParamsResponse, error) } type msgClient struct { @@ -278,10 +372,20 @@ func (c *msgClient) RemoveAccount(ctx context.Context, in *MsgRemoveAccount, opt return out, nil } +func (c *msgClient) SetParams(ctx context.Context, in *MsgSetParams, opts ...grpc.CallOption) (*MsgSetParamsResponse, error) { + out := new(MsgSetParamsResponse) + err := c.cc.Invoke(ctx, "/sifnode.admin.v1.Msg/SetParams", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { AddAccount(context.Context, *MsgAddAccount) (*MsgAddAccountResponse, error) RemoveAccount(context.Context, *MsgRemoveAccount) (*MsgRemoveAccountResponse, error) + SetParams(context.Context, *MsgSetParams) (*MsgSetParamsResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -294,6 +398,9 @@ func (*UnimplementedMsgServer) AddAccount(ctx context.Context, req *MsgAddAccoun func (*UnimplementedMsgServer) RemoveAccount(ctx context.Context, req *MsgRemoveAccount) (*MsgRemoveAccountResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RemoveAccount not implemented") } +func (*UnimplementedMsgServer) SetParams(ctx context.Context, req *MsgSetParams) (*MsgSetParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetParams not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -335,6 +442,24 @@ func _Msg_RemoveAccount_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } +func _Msg_SetParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSetParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SetParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/sifnode.admin.v1.Msg/SetParams", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SetParams(ctx, req.(*MsgSetParams)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "sifnode.admin.v1.Msg", HandlerType: (*MsgServer)(nil), @@ -347,6 +472,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "RemoveAccount", Handler: _Msg_RemoveAccount_Handler, }, + { + MethodName: "SetParams", + Handler: _Msg_SetParams_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "sifnode/admin/v1/tx.proto", @@ -482,6 +611,71 @@ func (m *MsgRemoveAccountResponse) MarshalToSizedBuffer(dAtA []byte) (int, error return len(dAtA) - i, nil } +func (m *MsgSetParams) 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 *MsgSetParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSetParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Params != nil { + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSetParamsResponse) 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 *MsgSetParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSetParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -545,6 +739,32 @@ func (m *MsgRemoveAccountResponse) Size() (n int) { return n } +func (m *MsgSetParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Params != nil { + l = m.Params.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSetParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -887,6 +1107,174 @@ func (m *MsgRemoveAccountResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgSetParams) 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: MsgSetParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSetParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + 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 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 m.Params == nil { + m.Params = &Params{} + } + if err := m.Params.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 *MsgSetParamsResponse) 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: MsgSetParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSetParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + 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/admin/types/types.pb.go b/x/admin/types/types.pb.go index 8761a2b15a..961e30d06e 100644 --- a/x/admin/types/types.pb.go +++ b/x/admin/types/types.pb.go @@ -5,6 +5,7 @@ package types import ( fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" @@ -156,38 +157,80 @@ func (m *AdminAccount) GetAdminAddress() string { return "" } +type Params struct { + SubmitProposalFee github_com_cosmos_cosmos_sdk_types.Uint `protobuf:"bytes,1,opt,name=submit_proposal_fee,json=submitProposalFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Uint" json:"submit_proposal_fee"` +} + +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_97192799444a7295, []int{2} +} +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 init() { proto.RegisterEnum("sifnode.admin.v1.AdminType", AdminType_name, AdminType_value) proto.RegisterType((*GenesisState)(nil), "sifnode.admin.v1.GenesisState") proto.RegisterType((*AdminAccount)(nil), "sifnode.admin.v1.AdminAccount") + proto.RegisterType((*Params)(nil), "sifnode.admin.v1.Params") } func init() { proto.RegisterFile("sifnode/admin/v1/types.proto", fileDescriptor_97192799444a7295) } var fileDescriptor_97192799444a7295 = []byte{ - // 342 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x29, 0xce, 0x4c, 0xcb, - 0xcb, 0x4f, 0x49, 0xd5, 0x4f, 0x4c, 0xc9, 0xcd, 0xcc, 0xd3, 0x2f, 0x33, 0xd4, 0x2f, 0xa9, 0x2c, - 0x48, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x80, 0xca, 0xea, 0x81, 0x65, 0xf5, - 0xca, 0x0c, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x92, 0xfa, 0x20, 0x16, 0x44, 0x9d, 0x52, - 0x28, 0x17, 0x8f, 0x7b, 0x6a, 0x5e, 0x6a, 0x71, 0x66, 0x71, 0x70, 0x49, 0x62, 0x49, 0xaa, 0x90, - 0x2b, 0x17, 0x1f, 0x58, 0x47, 0x7c, 0x62, 0x72, 0x72, 0x7e, 0x69, 0x5e, 0x49, 0xb1, 0x04, 0xa3, - 0x02, 0xb3, 0x06, 0xb7, 0x91, 0x9c, 0x1e, 0xba, 0x81, 0x7a, 0x8e, 0x20, 0x86, 0x23, 0x44, 0x59, - 0x10, 0x6f, 0x22, 0x12, 0xaf, 0x58, 0x29, 0x9f, 0x8b, 0x07, 0x59, 0x5a, 0xc8, 0x8a, 0x8b, 0x0b, - 0x62, 0x2c, 0xc8, 0x8d, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x7c, 0x46, 0xd2, 0x38, 0x8c, 0x0c, 0xa9, - 0x2c, 0x48, 0x0d, 0xe2, 0x4c, 0x84, 0x31, 0x85, 0x94, 0xb9, 0x78, 0xa1, 0x4e, 0x4a, 0x49, 0x29, - 0x4a, 0x2d, 0x2e, 0x96, 0x60, 0x52, 0x60, 0xd4, 0xe0, 0x0c, 0xe2, 0x81, 0xd8, 0x08, 0x11, 0xd3, - 0x4a, 0xe4, 0xe2, 0x84, 0x6b, 0x16, 0xe2, 0xe2, 0x62, 0x73, 0xf6, 0x09, 0x70, 0x71, 0x8d, 0x10, - 0x60, 0x10, 0xe2, 0xe7, 0xe2, 0x0e, 0xf0, 0x0d, 0x09, 0x08, 0x72, 0x0d, 0x77, 0x0c, 0x72, 0x09, - 0x16, 0x60, 0x14, 0x12, 0xe4, 0xe2, 0x0d, 0xf1, 0xf7, 0x76, 0xf5, 0x0b, 0x72, 0x75, 0xf7, 0x0c, - 0x0e, 0x09, 0x8a, 0x14, 0x60, 0x12, 0xe2, 0xe5, 0xe2, 0x74, 0x0d, 0xf1, 0x70, 0x0a, 0xf2, 0x74, - 0x71, 0x77, 0x15, 0x60, 0x16, 0xe2, 0xe4, 0x62, 0x75, 0x74, 0xf1, 0xf5, 0xf4, 0x13, 0x60, 0x01, - 0x99, 0xe4, 0xeb, 0x18, 0xe4, 0xee, 0xe9, 0x27, 0xc0, 0xea, 0xe4, 0x7c, 0xe2, 0x91, 0x1c, 0xe3, - 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, - 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x9a, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, - 0xb9, 0xfa, 0xc1, 0x99, 0x69, 0xc9, 0x19, 0x89, 0x99, 0x79, 0xfa, 0xb0, 0xe8, 0xa9, 0x80, 0x46, - 0x10, 0x38, 0x76, 0x92, 0xd8, 0xc0, 0xc1, 0x6e, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xa1, 0x21, - 0x7f, 0xd8, 0xbe, 0x01, 0x00, 0x00, + // 410 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x4f, 0x6f, 0xd3, 0x30, + 0x18, 0xc6, 0xe3, 0x8d, 0x55, 0xca, 0xbb, 0x76, 0x64, 0x86, 0x43, 0x05, 0x28, 0xab, 0xca, 0x81, + 0x82, 0x44, 0xa2, 0x8d, 0x1b, 0xb7, 0x74, 0x0d, 0x21, 0x82, 0x96, 0xc8, 0xc9, 0xc4, 0x9f, 0x4b, + 0xe4, 0x26, 0x6e, 0x67, 0x41, 0xe2, 0x28, 0x76, 0x27, 0xf6, 0x2d, 0xf8, 0x58, 0x3b, 0xee, 0x88, + 0x38, 0x4c, 0xa8, 0xfd, 0x22, 0x28, 0x7f, 0x8a, 0x2a, 0x24, 0x4e, 0x7e, 0xec, 0xe7, 0xf5, 0xef, + 0x7d, 0x64, 0xbf, 0xf0, 0x44, 0xf2, 0x45, 0x2e, 0x52, 0x66, 0xd3, 0x34, 0xe3, 0xb9, 0x7d, 0x75, + 0x6a, 0xab, 0xeb, 0x82, 0x49, 0xab, 0x28, 0x85, 0x12, 0xd8, 0x68, 0x5d, 0xab, 0x76, 0xad, 0xab, + 0xd3, 0x47, 0x0f, 0x97, 0x62, 0x29, 0x6a, 0xd3, 0xae, 0x54, 0x53, 0x37, 0xbc, 0x80, 0xae, 0xc7, + 0x72, 0x26, 0xb9, 0x0c, 0x15, 0x55, 0x0c, 0xbb, 0x70, 0x54, 0xdf, 0x88, 0x69, 0x92, 0x88, 0x55, + 0xae, 0x64, 0x1f, 0x0d, 0xf6, 0x47, 0x87, 0x67, 0xa6, 0xf5, 0x2f, 0xd0, 0x72, 0x2a, 0xe1, 0x34, + 0x65, 0xa4, 0x47, 0x77, 0x76, 0x72, 0x28, 0xa0, 0xbb, 0x6b, 0xe3, 0xd7, 0x00, 0x0d, 0xb6, 0xca, + 0xd8, 0x47, 0x03, 0x34, 0x3a, 0x3a, 0x7b, 0xfc, 0x1f, 0x64, 0x74, 0x5d, 0x30, 0xa2, 0xd3, 0xad, + 0xc4, 0x4f, 0xa1, 0xd7, 0x46, 0x4a, 0xd3, 0x92, 0x49, 0xd9, 0xdf, 0x1b, 0xa0, 0x91, 0x4e, 0xba, + 0x4d, 0xc7, 0xe6, 0x6c, 0xc8, 0xa1, 0x13, 0xd0, 0x92, 0x66, 0x12, 0xc7, 0xf0, 0x40, 0xae, 0xe6, + 0x19, 0x57, 0x71, 0x51, 0x8a, 0x42, 0x48, 0xfa, 0x2d, 0x5e, 0xb0, 0xa6, 0xa7, 0x3e, 0xb6, 0x6f, + 0xee, 0x4e, 0xb4, 0x5f, 0x77, 0x27, 0xcf, 0x96, 0x5c, 0x5d, 0xae, 0xe6, 0x56, 0x22, 0x32, 0x3b, + 0x11, 0x32, 0x13, 0xb2, 0x5d, 0x5e, 0xca, 0xf4, 0x6b, 0xfb, 0x90, 0x17, 0x3c, 0x57, 0xe4, 0xb8, + 0x61, 0x05, 0x2d, 0xea, 0x0d, 0x63, 0x2f, 0x28, 0xe8, 0x7f, 0x73, 0x62, 0x80, 0xce, 0xf9, 0xfb, + 0x60, 0xe2, 0x7e, 0x32, 0x34, 0x7c, 0x1f, 0x0e, 0x83, 0x69, 0x14, 0x10, 0xf7, 0xa3, 0x43, 0x26, + 0xa1, 0x81, 0xf0, 0x31, 0xf4, 0xa2, 0x0f, 0xef, 0xdc, 0x19, 0x71, 0x3d, 0x3f, 0x8c, 0xc8, 0x67, + 0x63, 0x0f, 0xf7, 0x40, 0x77, 0xa3, 0xb7, 0x63, 0xe2, 0x4f, 0x3c, 0xd7, 0xd8, 0xc7, 0x3a, 0x1c, + 0x38, 0x93, 0xa9, 0x3f, 0x33, 0xee, 0x55, 0xa4, 0xa9, 0x43, 0x3c, 0x7f, 0x66, 0x1c, 0x8c, 0xcf, + 0x6f, 0xd6, 0x26, 0xba, 0x5d, 0x9b, 0xe8, 0xf7, 0xda, 0x44, 0x3f, 0x36, 0xa6, 0x76, 0xbb, 0x31, + 0xb5, 0x9f, 0x1b, 0x53, 0xfb, 0xf2, 0x7c, 0x27, 0x78, 0xc8, 0x17, 0xc9, 0x25, 0xe5, 0xb9, 0xbd, + 0x9d, 0x84, 0xef, 0xed, 0x2c, 0xd4, 0xf9, 0xe7, 0x9d, 0xfa, 0x87, 0x5f, 0xfd, 0x09, 0x00, 0x00, + 0xff, 0xff, 0x9a, 0x5e, 0xd3, 0xde, 0x29, 0x02, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -262,6 +305,39 @@ func (m *AdminAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +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 + { + size := m.SubmitProposalFee.Size() + i -= size + if _, err := m.SubmitProposalFee.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { offset -= sovTypes(v) base := offset @@ -304,6 +380,17 @@ func (m *AdminAccount) Size() (n int) { return n } +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.SubmitProposalFee.Size() + n += 1 + l + sovTypes(uint64(l)) + return n +} + func sovTypes(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -495,6 +582,90 @@ func (m *AdminAccount) Unmarshal(dAtA []byte) error { } return nil } +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 ErrIntOverflowTypes + } + 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 SubmitProposalFee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + 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 ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.SubmitProposalFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTypes(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/clp/client/cli/tx.go b/x/clp/client/cli/tx.go index 6cabd2b80c..70cde0c6c5 100644 --- a/x/clp/client/cli/tx.go +++ b/x/clp/client/cli/tx.go @@ -188,9 +188,9 @@ func GetCmdSetSwapFeeParams() *cobra.Command { return err } msg := types.MsgUpdateSwapFeeParamsRequest{ - Signer: signer.String(), - SwapFeeRate: swapFeeParams.SwapFeeRate, - TokenParams: swapFeeParams.TokenParams, + Signer: signer.String(), + DefaultSwapFeeRate: swapFeeParams.DefaultSwapFeeRate, + TokenParams: swapFeeParams.TokenParams, } if err := msg.ValidateBasic(); err != nil { return err diff --git a/x/clp/handler_test.go b/x/clp/handler_test.go index d3b61631a1..416260ca68 100644 --- a/x/clp/handler_test.go +++ b/x/clp/handler_test.go @@ -109,8 +109,9 @@ func TestAddLiquidity(t *testing.T) { require.NotNil(t, res) msg = clptypes.NewMsgAddLiquidity(signer, asset, sdk.ZeroUint(), addLiquidityAmount) res, err = handler(ctx, &msg) - require.EqualError(t, err, "Cannot add liquidity asymmetrically") - require.Nil(t, res) + require.NoError(t, err) + require.NotNil(t, res) + // Subtracted twice , during create and add externalCoin = sdk.NewCoin(asset.Symbol, sdk.Int(initialBalance.Sub(addLiquidityAmount).Sub(addLiquidityAmount))) nativeCoin = sdk.NewCoin(clptypes.NativeSymbol, sdk.Int(initialBalance.Sub(addLiquidityAmount).Sub(sdk.ZeroUint()))) @@ -155,8 +156,8 @@ func TestAddLiquidity_LargeValue(t *testing.T) { require.NotNil(t, res) msg := clptypes.NewMsgAddLiquidity(signer, asset, addLiquidityAmountRowan, addLiquidityAmountCaCoin) res, err = handler(ctx, &msg) - require.EqualError(t, err, "Cannot add liquidity asymmetrically") - require.Nil(t, res) + require.NoError(t, err) + require.NotNil(t, res) } func TestRemoveLiquidity(t *testing.T) { @@ -395,16 +396,16 @@ func CalculateWithdraw(t *testing.T, keeper clpkeeper.Keeper, ctx sdk.Context, a nativeAssetCoin := sdk.Coin{} ctx, app := test.CreateTestAppClp(false) _, err = app.TokenRegistryKeeper.GetRegistryEntry(ctx, pool.ExternalAsset.Symbol) - swapFeeParams := clptypes.SwapFeeParams{SwapFeeRate: sdk.NewDecWithPrec(3, 3)} + swapFeeRate := sdk.NewDecWithPrec(3, 3) assert.NoError(t, err) if asymmetry.IsPositive() { - swapResult, _, _, _, err := clpkeeper.SwapOne(clptypes.GetSettlementAsset(), swapAmount, asset, pool, sdk.OneDec(), swapFeeParams) + swapResult, _, _, _, err := clpkeeper.SwapOne(clptypes.GetSettlementAsset(), swapAmount, asset, pool, sdk.OneDec(), swapFeeRate) assert.NoError(t, err) externalAssetCoin = sdk.NewCoin(asset.Symbol, sdk.Int(withdrawExternalAssetAmount.Add(swapResult))) nativeAssetCoin = sdk.NewCoin(clptypes.GetSettlementAsset().Symbol, sdk.Int(withdrawNativeAssetAmount)) } if asymmetry.IsNegative() { - swapResult, _, _, _, err := clpkeeper.SwapOne(asset, swapAmount, clptypes.GetSettlementAsset(), pool, sdk.OneDec(), swapFeeParams) + swapResult, _, _, _, err := clpkeeper.SwapOne(asset, swapAmount, clptypes.GetSettlementAsset(), pool, sdk.OneDec(), swapFeeRate) assert.NoError(t, err) externalAssetCoin = sdk.NewCoin(asset.Symbol, sdk.Int(withdrawExternalAssetAmount)) nativeAssetCoin = sdk.NewCoin(clptypes.GetSettlementAsset().Symbol, sdk.Int(withdrawNativeAssetAmount.Add(swapResult))) @@ -417,18 +418,18 @@ func CalculateWithdraw(t *testing.T, keeper clpkeeper.Keeper, ctx sdk.Context, a } func CalculateSwapReceived(t *testing.T, keeper clpkeeper.Keeper, tokenRegistryKeeper tokenregistrytypes.Keeper, ctx sdk.Context, assetSent clptypes.Asset, assetReceived clptypes.Asset, swapAmount sdk.Uint) sdk.Uint { - swapFeeParams := clptypes.SwapFeeParams{SwapFeeRate: sdk.NewDecWithPrec(3, 3)} + swapFeeRate := sdk.NewDecWithPrec(3, 3) inPool, err := keeper.GetPool(ctx, assetSent.Symbol) assert.NoError(t, err) outPool, err := keeper.GetPool(ctx, assetReceived.Symbol) assert.NoError(t, err) _, err = tokenRegistryKeeper.GetRegistryEntry(ctx, inPool.ExternalAsset.Symbol) assert.NoError(t, err) - emitAmount, _, _, _, err := clpkeeper.SwapOne(assetSent, swapAmount, clptypes.GetSettlementAsset(), inPool, sdk.OneDec(), swapFeeParams) + emitAmount, _, _, _, err := clpkeeper.SwapOne(assetSent, swapAmount, clptypes.GetSettlementAsset(), inPool, sdk.OneDec(), swapFeeRate) assert.NoError(t, err) _, err = tokenRegistryKeeper.GetRegistryEntry(ctx, outPool.ExternalAsset.Symbol) assert.NoError(t, err) - emitAmount2, _, _, _, err := clpkeeper.SwapOne(clptypes.GetSettlementAsset(), emitAmount, assetReceived, outPool, sdk.OneDec(), swapFeeParams) + emitAmount2, _, _, _, err := clpkeeper.SwapOne(clptypes.GetSettlementAsset(), emitAmount, assetReceived, outPool, sdk.OneDec(), swapFeeRate) assert.NoError(t, err) return emitAmount2 } diff --git a/x/clp/keeper/calculations.go b/x/clp/keeper/calculations.go index c6d0eb1d34..a0959b541e 100644 --- a/x/clp/keeper/calculations.go +++ b/x/clp/keeper/calculations.go @@ -5,7 +5,6 @@ import ( "math/big" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/errors" "github.com/Sifchain/sifnode/x/clp/types" ) @@ -69,10 +68,10 @@ func CalculateWithdrawal(poolUnits sdk.Uint, nativeAssetDepth string, //if asymmetry is 0 we don't need to swap lpUnitsLeft := lpUnitsF.Sub(unitsToClaim) - return sdk.NewUintFromBigInt(withdrawNativeAssetAmount.RoundInt().BigInt()), - sdk.NewUintFromBigInt(withdrawExternalAssetAmount.RoundInt().BigInt()), - sdk.NewUintFromBigInt(lpUnitsLeft.RoundInt().BigInt()), - sdk.NewUintFromBigInt(swapAmount.RoundInt().BigInt()) + return sdk.NewUintFromBigInt(withdrawNativeAssetAmount.TruncateInt().BigInt()), + sdk.NewUintFromBigInt(withdrawExternalAssetAmount.TruncateInt().BigInt()), + sdk.NewUintFromBigInt(lpUnitsLeft.TruncateInt().BigInt()), + sdk.NewUintFromBigInt(swapAmount.TruncateInt().BigInt()) } // More details on the formula @@ -109,139 +108,120 @@ func CalculateWithdrawalFromUnits(poolUnits sdk.Uint, nativeAssetDepth string, sdk.NewUintFromBigInt(lpUnitsLeft.RoundInt().BigInt()) } -// More details on the formula -// https://github.com/Sifchain/sifnode/blob/develop/docs/1.Liquidity%20Pools%20Architecture.md - -//native asset balance : currently in pool before adding -//external asset balance : currently in pool before adding -//native asset to added : the amount the user sends -//external asset amount to be added : the amount the user sends - -// R = native Balance (before) -// A = external Balance (before) -// r = native asset added; -// a = external asset added -// P = existing Pool Units -// slipAdjustment = (1 - ABS((R a - r A)/((r + R) (a + A)))) -// units = ((P (a R + A r))/(2 A R))*slidAdjustment - -func CalculatePoolUnits(oldPoolUnits, nativeAssetDepth, externalAssetDepth, nativeAssetAmount, - externalAssetAmount sdk.Uint, externalDecimals uint8, symmetryThreshold, ratioThreshold sdk.Dec) (sdk.Uint, sdk.Uint, error) { - - if nativeAssetAmount.IsZero() && externalAssetAmount.IsZero() { - return sdk.ZeroUint(), sdk.ZeroUint(), types.ErrAmountTooLow - } - - if nativeAssetDepth.Add(nativeAssetAmount).IsZero() { - return sdk.ZeroUint(), sdk.ZeroUint(), errors.Wrap(errors.ErrInsufficientFunds, nativeAssetAmount.String()) - } - if externalAssetDepth.Add(externalAssetAmount).IsZero() { - return sdk.ZeroUint(), sdk.ZeroUint(), errors.Wrap(errors.ErrInsufficientFunds, externalAssetAmount.String()) - } - if nativeAssetDepth.IsZero() || externalAssetDepth.IsZero() { - return nativeAssetAmount, nativeAssetAmount, nil - } - - slipAdjustmentValues := calculateSlipAdjustment(nativeAssetDepth.BigInt(), externalAssetDepth.BigInt(), - nativeAssetAmount.BigInt(), externalAssetAmount.BigInt()) - - one := big.NewRat(1, 1) - symmetryThresholdRat := DecToRat(&symmetryThreshold) +const ( + SellNative = iota + BuyNative + NoSwap +) - var diff big.Rat - diff.Sub(one, slipAdjustmentValues.slipAdjustment) - if diff.Cmp(&symmetryThresholdRat) == 1 { // this is: if diff > symmetryThresholdRat - return sdk.ZeroUint(), sdk.ZeroUint(), types.ErrAsymmetricAdd +// Calculate pool units taking into account the current pmtpCurrentRunningRate +// R - native asset depth +// A - external asset depth +// r - native asset amount +// a - external asset amount +// P - current number of pool units +func CalculatePoolUnits(P, R, A, r, a sdk.Uint, sellNativeSwapFeeRate, buyNativeSwapFeeRate, pmtpCurrentRunningRate sdk.Dec) (sdk.Uint, sdk.Uint, int, sdk.Uint, error) { + pmtpCurrentRunningRateR := DecToRat(&pmtpCurrentRunningRate) + sellNativeSwapFeeRateR := DecToRat(&sellNativeSwapFeeRate) + buyNativeSwapFeeRateR := DecToRat(&buyNativeSwapFeeRate) + + symmetryState := GetLiquidityAddSymmetryState(A, a, R, r) + switch symmetryState { + case ErrorEmptyPool: + // At least one side of the pool is empty. + // + // If both sides of the pool are empty then we start counting pool units from scratch. We can assign + // an arbitrary number, which we'll choose to be the amount of native asset added. However this + // should only be done if adding to both sides of the pool, otherwise one side will still be empty. + // + // If only one side of the pool is empty then it's not clear what should be done - in which case + // we'll default to doing the same thing. + + if a.IsZero() || r.IsZero() { + return sdk.Uint{}, sdk.Uint{}, 0, sdk.Uint{}, types.ErrInValidAmount + } + + return r, r, NoSwap, sdk.Uint{}, nil + case ErrorNothingAdded: + // Keep the pool units as they were and don't give any units to the liquidity provider + return P, sdk.ZeroUint(), NoSwap, sdk.Uint{}, nil + case NeedMoreY: + // Need more native token to make R/A == r/a + swapAmount := CalculateExternalSwapAmountAsymmetric(R, A, r, a, &buyNativeSwapFeeRateR, &pmtpCurrentRunningRateR) + aCorrected := a.Sub(swapAmount) + AProjected := A.Add(swapAmount) + + // external or native asset can be used to calculate pool units since now r/R = a/A. for convenience + // use external asset + poolUnits, lpUnits := CalculatePoolUnitsSymmetric(AProjected, aCorrected, P) + return poolUnits, lpUnits, BuyNative, swapAmount, nil + case Symmetric: + // R/A == r/a + poolUnits, lpUnits := CalculatePoolUnitsSymmetric(R, r, P) + return poolUnits, lpUnits, NoSwap, sdk.Uint{}, nil + case NeedMoreX: + // Need more external token to make R/A == r/a + swapAmount := CalculateNativeSwapAmountAsymmetric(R, A, r, a, &sellNativeSwapFeeRateR, &pmtpCurrentRunningRateR) + rCorrected := r.Sub(swapAmount) + RProjected := R.Add(swapAmount) + poolUnits, lpUnits := CalculatePoolUnitsSymmetric(RProjected, rCorrected, P) + return poolUnits, lpUnits, SellNative, swapAmount, nil + default: + panic("expect not to reach here!") } - - ratioThresholdRat := DecToRat(&ratioThreshold) - ratioPercentDiff, err := CalculateRatioPercentDiff(externalAssetDepth.BigInt(), nativeAssetDepth.BigInt(), externalAssetAmount.BigInt(), nativeAssetAmount.BigInt()) - if err != nil { - return sdk.ZeroUint(), sdk.ZeroUint(), err - } - diff.Sub(one, &ratioPercentDiff) - diff.Abs(&diff) - if diff.Cmp(&ratioThresholdRat) == 1 { //if ratioDiff > ratioThreshold - return sdk.ZeroUint(), sdk.ZeroUint(), types.ErrAsymmetricRatioAdd - } - - stakeUnits := calculateStakeUnits(oldPoolUnits.BigInt(), nativeAssetDepth.BigInt(), - externalAssetDepth.BigInt(), nativeAssetAmount.BigInt(), slipAdjustmentValues) - - var newPoolUnit big.Int - newPoolUnit.Add(oldPoolUnits.BigInt(), stakeUnits) - - return sdk.NewUintFromBigInt(&newPoolUnit), sdk.NewUintFromBigInt(stakeUnits), nil } -// (A/R) / (a/r) -func CalculateRatioPercentDiff(A, R, a, r *big.Int) (big.Rat, error) { - if R.Cmp(big.NewInt(0)) == 0 || r.Cmp(big.NewInt(0)) == 0 { // check for zeros - return *big.NewRat(0, 1), types.ErrAsymmetricRatioAdd - } - var AdivR, adivr, percentDiff big.Rat - - AdivR.SetFrac(A, R) - adivr.SetFrac(a, r) +func CalculatePoolUnitsSymmetric(X, x, P sdk.Uint) (sdk.Uint, sdk.Uint) { + var providerUnitsB big.Int - percentDiff.Quo(&AdivR, &adivr) + providerUnitsB.Mul(x.BigInt(), P.BigInt()).Quo(&providerUnitsB, X.BigInt()) // providerUnits = P * x / X + providerUnits := sdk.NewUintFromBigInt(&providerUnitsB) - return percentDiff, nil + return P.Add(providerUnits), providerUnits } -// units = ((P (a R + A r))/(2 A R))*slidAdjustment -func calculateStakeUnits(P, R, A, r *big.Int, slipAdjustmentValues *slipAdjustmentValues) *big.Int { - var add, numerator big.Int - add.Add(slipAdjustmentValues.RTimesa, slipAdjustmentValues.rTimesA) - numerator.Mul(P, &add) - - var denominator big.Int - denominator.Mul(big.NewInt(2), A) - denominator.Mul(&denominator, R) - - var n, d, stakeUnits big.Rat - n.SetInt(&numerator) - d.SetInt(&denominator) - stakeUnits.Quo(&n, &d) - stakeUnits.Mul(&stakeUnits, slipAdjustmentValues.slipAdjustment) - - return RatIntQuo(&stakeUnits) -} - -// slipAdjustment = (1 - ABS((R a - r A)/((r + R) (a + A)))) -type slipAdjustmentValues struct { - slipAdjustment *big.Rat - RTimesa *big.Int - rTimesA *big.Int -} - -func calculateSlipAdjustment(R, A, r, a *big.Int) *slipAdjustmentValues { - var denominator, rPlusR, aPlusA big.Int - rPlusR.Add(r, R) - aPlusA.Add(a, A) - denominator.Mul(&rPlusR, &aPlusA) +const ( + ErrorEmptyPool = iota + ErrorNothingAdded + NeedMoreY // Need more y token to make Y/X == y/x + Symmetric // Y/X == y/x + NeedMoreX // Need more x token to make Y/X == y/x +) - var RTimesa, rTimesA, nominator big.Int - RTimesa.Mul(R, a) - rTimesA.Mul(r, A) - nominator.Sub(&RTimesa, &rTimesA) +// Determines how the amount of assets added to a pool, x, y, compare to the current +// pool ratio, Y/X +func GetLiquidityAddSymmetryState(X, x, Y, y sdk.Uint) int { + if X.IsZero() || Y.IsZero() { + return ErrorEmptyPool + } - var one, nom, denom, slipAdjustment big.Rat - one.SetInt64(1) + if x.IsZero() && y.IsZero() { + return ErrorNothingAdded + } - nom.SetInt(&nominator) - denom.SetInt(&denominator) + if x.IsZero() { + return NeedMoreX + } + var YoverX, yOverx big.Rat - slipAdjustment.Quo(&nom, &denom) - slipAdjustment.Abs(&slipAdjustment) - slipAdjustment.Sub(&one, &slipAdjustment) + YoverX.SetFrac(Y.BigInt(), X.BigInt()) + yOverx.SetFrac(y.BigInt(), x.BigInt()) - return &slipAdjustmentValues{slipAdjustment: &slipAdjustment, RTimesa: &RTimesa, rTimesA: &rTimesA} + switch YoverX.Cmp(&yOverx) { + case -1: + return NeedMoreX + case 0: + return Symmetric + case 1: + return NeedMoreY + default: + panic("expect not to reach here!") + } } func CalcSwapResult(toRowan bool, X, x, Y sdk.Uint, - pmtpCurrentRunningRate, swapFeeRate sdk.Dec, minSwapFee sdk.Uint) (sdk.Uint, sdk.Uint) { + pmtpCurrentRunningRate, swapFeeRate sdk.Dec) (sdk.Uint, sdk.Uint) { // if either side of the pool is empty or swap amount iz zero then return zero if IsAnyZero([]sdk.Uint{X, x, Y}) { @@ -265,10 +245,9 @@ func CalcSwapResult(toRowan bool, percentFee := sdk.NewUintFromBigInt(RatIntQuo(&percentFeeR)) adjusted := sdk.NewUintFromBigInt(RatIntQuo(&adjustedR)) - fee := sdk.MinUint(sdk.MaxUint(percentFee, minSwapFee), adjusted) - y := adjusted.Sub(fee) + y := adjusted.Sub(percentFee) - return y, fee + return y, percentFee } func calcRawXYK(x, X, Y *big.Int) big.Rat { @@ -329,13 +308,10 @@ func CalcSpotPriceX(X, Y sdk.Uint, decimalsX, decimalsY uint8, pmtpCurrentRunnin return RatToDec(&pmtpPrice) } -func CalcRowanValue(pool *types.Pool, pmtpCurrentRunningRate sdk.Dec, rowanAmount sdk.Uint) (sdk.Uint, error) { - spotPrice, err := CalcRowanSpotPrice(pool, pmtpCurrentRunningRate) - if err != nil { - return sdk.ZeroUint(), err - } - value := spotPrice.Mul(sdk.NewDecFromBigInt(rowanAmount.BigInt())) - return sdk.NewUintFromBigInt(value.RoundInt().BigInt()), nil + +func CalcRowanValue(rowanAmount sdk.Uint, price sdk.Dec) sdk.Uint { + value := price.Mul(sdk.NewDecFromBigInt(rowanAmount.BigInt())) + return sdk.NewUintFromBigInt(value.RoundInt().BigInt()) } // Calculates spot price of Rowan accounting for PMTP @@ -391,6 +367,163 @@ func CalculateAllAssetsForLP(pool types.Pool, lp types.LiquidityProvider) (sdk.U ) } +// Calculates how much external asset to swap for an asymmetric add to become +// symmetric. +// R - native asset depth +// A - external asset depth +// r - native asset amount +// a - external asset amount +// f - swap fee rate +// p - pmtp (ratio shifting) current running rate +// +// Calculates the amount of external asset to swap, s, such that the ratio of the added assets after the swap +// equals the ratio of assets in the pool after the swap i.e. calculates s, such that (a+A)/(r+R) = (a−s) / (r + s*R/(s+A)*(1−f)/(1+p)). +// +// Solving for s gives, s = math.Abs((math.Sqrt(R*(-1*(a+A))*(-1*f*f*a*R-f*f*A*R-2*f*p*a*R+4*f*p*A*r+2*f*p*A*R+4*f*A*r+4*f*A*R-p*p*a*R-p*p*A*R-4*p*A*r-4*p*A*R-4*A*r-4*A*R)) + f*a*R + f*A*R + p*a*R - 2*p*A*r - p*A*R - 2*A*r - 2*A*R) / (2 * (p + 1) * (r + R))). +// +// This function should only be used when when more native asset is required in order for an add to be symmetric i.e. when R,A,a > 0 and R/A > r/a. +// If more external asset is required, then due to ratio shifting the swap formula changes, in which case +// use CalculateNativeSwapAmountAsymmetric. +func CalculateExternalSwapAmountAsymmetric(R, A, r, a sdk.Uint, f, p *big.Rat) sdk.Uint { + var RRat, ARat, rRat, aRat big.Rat + RRat.SetInt(R.BigInt()) + ARat.SetInt(A.BigInt()) + rRat.SetInt(r.BigInt()) + aRat.SetInt(a.BigInt()) + + s := CalculateExternalSwapAmountAsymmetricRat(&RRat, &ARat, &rRat, &aRat, f, p) + return sdk.NewUintFromBigInt(RatIntQuo(&s)) +} + +// NOTE: this method is only exported to make testing easier +// +// NOTE: this method panics if a negative value is passed to the sqrt +// It's not clear whether this condition could ever happen given the external +// constraints on the inputs (e.g. X,Y,x > 0 and Y/X > y/x). It is possible to guard against +// a panic by ensuring the sqrt argument is positive. +func CalculateExternalSwapAmountAsymmetricRat(Y, X, y, x, f, r *big.Rat) big.Rat { + var a_, b_, c_, d_, e_, f_, g_, h_, i_, j_, k_, l_, m_, n_, o_, p_, q_, r_, s_, t_, u_, v_, w_, x_, y_, z_, aa_, ab_, ac_, ad_, minusOne, one, two, four, r1 big.Rat //nolint:revive + minusOne.SetInt64(-1) + one.SetInt64(1) + two.SetInt64(2) + four.SetInt64(4) + r1.Add(r, &one) + + a_.Add(x, X) // a_ = x + X + b_.Mul(&a_, &minusOne) // b_ = -1 * (x + X) + c_.Mul(Y, &b_) // c_ = Y * -1 * (x + X) + + d_.Mul(f, f).Mul(&d_, x).Mul(&d_, Y) // d_ = f * f * x * Y + e_.Mul(f, f).Mul(&e_, X).Mul(&e_, Y) // e_ := f * f * X * Y + f_.Mul(&two, f).Mul(&f_, r).Mul(&f_, x).Mul(&f_, Y) // f_ := 2 * f * r * x * Y + g_.Mul(&four, f).Mul(&g_, r).Mul(&g_, X).Mul(&g_, y) // g_ := 4 * f * r * X * y + h_.Mul(&two, f).Mul(&h_, r).Mul(&h_, X).Mul(&h_, Y) // h_ := 2 * f * r * X * Y + i_.Mul(&four, f).Mul(&i_, X).Mul(&i_, y) // i_ := 4 * f * X * y + j_.Mul(&four, f).Mul(&j_, X).Mul(&j_, Y) // j_ := 4 * f * X * Y + k_.Mul(r, r).Mul(&k_, x).Mul(&k_, Y) // k_ := r * r * x * Y + l_.Mul(r, r).Mul(&l_, X).Mul(&l_, Y) // l_ := r * r * X * Y + m_.Mul(&four, r).Mul(&m_, X).Mul(&m_, y) // m_ := 4 * r * X * y + n_.Mul(&four, r).Mul(&n_, X).Mul(&n_, Y) // n_ := 4 * r * X * Y + o_.Mul(&four, X).Mul(&o_, y) // o_ := 4 * X * y + p_.Mul(&four, X).Mul(&p_, Y) // p_ := 4 * X * Y + q_.Mul(f, x).Mul(&q_, Y) // q_ := f * x * Y + r_.Mul(f, X).Mul(&r_, Y) // r_ := f * X * Y + s_.Mul(r, x).Mul(&s_, Y) // s_ := r * x * Y + t_.Mul(&two, r).Mul(&t_, X).Mul(&t_, y) // t_ := 2 * r * X * y + u_.Mul(r, X).Mul(&u_, Y) // u_ := r * X * Y + v_.Mul(&two, X).Mul(&v_, y) // v_ := 2 * X * y + w_.Mul(&two, X).Mul(&w_, Y) // w_ := 2 * X * Y + + x_.Add(y, Y) // x_ := (y + Y) + + y_.Add(&g_, &h_).Add(&y_, &i_).Add(&y_, &j_).Sub(&y_, &d_).Sub(&y_, &e_).Sub(&y_, &f_).Sub(&y_, &k_).Sub(&y_, &l_).Sub(&y_, &m_).Sub(&y_, &n_).Sub(&y_, &o_).Sub(&y_, &p_) // y_ := g_ + h_ + i_ + j_ - d_ - e_ - f_ - k_ - l_ - m_ - n_ - o_ - p_ // y_ := -d_ - e_ - f_ + g_ + h_ + i_ + j_ - k_ - l_ - m_ - n_ - o_ - p_ + + z_.Mul(&c_, &y_) // z_ := c_ * y_ + aa_.SetInt(ApproxRatSquareRoot(&z_)) // aa_ := math.Sqrt(z_) + + ab_.Add(&aa_, &q_).Add(&ab_, &r_).Add(&ab_, &s_).Sub(&ab_, &t_).Sub(&ab_, &u_).Sub(&ab_, &v_).Sub(&ab_, &w_) // ab_ := (aa_ + q_ + r_ + s_ - t_ - u_ - v_ - w_) + + ac_.Mul(&two, &r1).Mul(&ac_, &x_) // ac_ := (2 * r1 * x_) + ad_.Quo(&ab_, &ac_) // ad_ := ab_ / ac_ + return *ad_.Abs(&ad_) +} + +// Calculates how much native asset to swap for an asymmetric add to become +// symmetric. +// R - native asset depth +// A - external asset depth +// r - native asset amount +// a - external asset amount +// f - swap fee rate +// p - pmtp (ratio shifting) current running rate +// +// Calculates the amount of native asset to swap, s, such that the ratio of the added assets after the swap +// equals the ratio of assets in the pool after the swap i.e. calculates s, such that (r+R)/(a+A) = (r-s) / (a + (s*A)/(s+R)*(1+p)*(1-f)). +// +// Solving for s gives, s = math.Abs((math.Sqrt(math.Pow((-1*f*p*A*r-f*p*A*R-f*A*r-f*A*R+p*A*r+p*A*R+2*a*R+2*A*R), 2)-4*(a+A)*(a*R*R-A*r*R)) + f*p*A*r + f*p*A*R + f*A*r + f*A*R - p*A*r - p*A*R - 2*a*R - 2*A*R) / (2 * (a + A))). + +// This function should only be used when when more external asset is required in order for an add to be symmetric i.e. when R,A,r > 0 and (a==0 or R/A < r/a) +// If more native asset is required, then due to ratio shifting the swap formula changes, in which case +// use CalculateExternalSwapAmountAsymmetric. +func CalculateNativeSwapAmountAsymmetric(R, A, r, a sdk.Uint, f, p *big.Rat) sdk.Uint { + var RRat, ARat, rRat, aRat big.Rat + RRat.SetInt(R.BigInt()) + ARat.SetInt(A.BigInt()) + rRat.SetInt(r.BigInt()) + aRat.SetInt(a.BigInt()) + + s := CalculateNativeSwapAmountAsymmetricRat(&RRat, &ARat, &rRat, &aRat, f, p) + return sdk.NewUintFromBigInt(RatIntQuo(&s)) +} + +// NOTE: this method is only exported to make testing easier +// +// NOTE: this method panics if a negative value is passed to the sqrt +// It's not clear whether this condition could ever happen given the +// constraints on the inputs (i.e. Y,X,y > 0 and (x==0 or Y/X < y/x). It is possible to guard against +// a panic by ensuring the sqrt argument is positive. +func CalculateNativeSwapAmountAsymmetricRat(Y, X, y, x, f, r *big.Rat) big.Rat { + var a_, b_, c_, d_, e_, f_, g_, h_, i_, j_, k_, l_, m_, n_, o_, p_, q_, r_, s_, t_, u_, v_, w_, x_, y_, z_, aa_, ab_, two, four big.Rat // nolint:revive + two.SetInt64(2) + four.SetInt64(4) + + a_.Mul(f, r).Mul(&a_, X).Mul(&a_, y) // a_ := f * r * X * y + b_.Mul(f, r).Mul(&b_, X).Mul(&b_, Y) // b_ := f * r * X * Y + c_.Mul(f, X).Mul(&c_, y) // c_ := f * X * y + d_.Mul(f, X).Mul(&d_, Y) // d_ := f * X * Y + e_.Mul(r, X).Mul(&e_, y) // e_ := r * X * y + f_.Mul(r, X).Mul(&f_, Y) // f_ := r * X * Y + g_.Mul(&two, x).Mul(&g_, Y) // g_ := 2 * x * Y + h_.Mul(&two, X).Mul(&h_, Y) // h_ := 2 * X * Y + i_.Add(x, X) // i_ := x + X + j_.Mul(x, Y).Mul(&j_, Y) // j_ := x * Y * Y + k_.Mul(X, y).Mul(&k_, Y) // k_ := X * y * Y + l_.Sub(&j_, &k_) // l_ := j_ - k_ + m_.Mul(&four, &i_).Mul(&m_, &l_) // m_ := 4 * i_ * l_ + n_.Mul(f, r).Mul(&n_, X).Mul(&n_, y) // n_ := f * r * X * y + o_.Mul(f, r).Mul(&o_, X).Mul(&o_, Y) // o_ := f * r * X * Y + p_.Mul(f, X).Mul(&p_, y) // p_ := f * X * y + q_.Mul(f, X).Mul(&q_, Y) // q_ := f * X * Y + r_.Mul(r, X).Mul(&r_, y) // r_ := r * X * y + s_.Mul(r, X).Mul(&s_, Y) // s_ := r * X * Y + t_.Mul(&two, x).Mul(&t_, Y) // t_ := 2 * x * Y + u_.Mul(&two, X).Mul(&u_, Y) // u_ := 2 * X * Y + v_.Add(x, X).Mul(&v_, &two) // v_ := 2 * (x + X) + + w_.Add(&e_, &f_).Add(&w_, &g_).Add(&w_, &h_).Sub(&w_, &a_).Sub(&w_, &b_).Sub(&w_, &c_).Sub(&w_, &d_) // w_ := e_ + f_ + g_ + h_ -a_ - b_ - c_ - d_ // w_ := -a_ - b_ - c_ - d_ + e_ + f_ + g_ + h_ + + x_.Mul(&w_, &w_) // x_ := math.Pow(w_, 2) + y_.Sub(&x_, &m_) // y_ := x_ - m_ + + z_.SetInt(ApproxRatSquareRoot(&y_)) // z_ := math.Sqrt(y_) + + aa_.Add(&z_, &n_).Add(&aa_, &o_).Add(&aa_, &p_).Add(&aa_, &q_).Sub(&aa_, &r_).Sub(&aa_, &s_).Sub(&aa_, &t_).Sub(&aa_, &u_) // aa_ := z_ + n_ + o_ + p_ + q_ - r_ - s_ - t_ - u_ + + ab_.Quo(&aa_, &v_) // ab_ := aa_ / v_ + + return *ab_.Abs(&ab_) +} + func ConvUnitsToWBasisPoints(total, units sdk.Uint) sdk.Int { totalDec, err := sdk.NewDecFromStr(total.String()) if err != nil { @@ -413,15 +546,13 @@ func CalculateWithdrawalRowanValue( sentAmount sdk.Uint, to types.Asset, pool types.Pool, - pmtpCurrentRunningRate sdk.Dec, swapFeeParams types.SwapFeeParams) sdk.Uint { + pmtpCurrentRunningRate, swapFeeRate sdk.Dec) sdk.Uint { - minSwapFee := GetMinSwapFee(to, swapFeeParams.TokenParams) - - X, Y, toRowan := pool.ExtractValues(to) + X, Y, toRowan, _ := pool.ExtractValues(to) X, Y = pool.ExtractDebt(X, Y, toRowan) - value, _ := CalcSwapResult(toRowan, X, sentAmount, Y, pmtpCurrentRunningRate, swapFeeParams.SwapFeeRate, minSwapFee) + value, _ := CalcSwapResult(toRowan, X, sentAmount, Y, pmtpCurrentRunningRate, swapFeeRate) return value } @@ -430,18 +561,16 @@ func SwapOne(from types.Asset, sentAmount sdk.Uint, to types.Asset, pool types.Pool, - pmtpCurrentRunningRate sdk.Dec, swapFeeParams types.SwapFeeParams) (sdk.Uint, sdk.Uint, sdk.Uint, types.Pool, error) { - - minSwapFee := GetMinSwapFee(to, swapFeeParams.TokenParams) + pmtpCurrentRunningRate sdk.Dec, swapFeeRate sdk.Dec) (sdk.Uint, sdk.Uint, sdk.Uint, types.Pool, error) { - X, Y, toRowan := pool.ExtractValues(to) + X, Y, toRowan, _ := pool.ExtractValues(to) var Xincl, Yincl sdk.Uint Xincl, Yincl = pool.ExtractDebt(X, Y, toRowan) priceImpact := calcPriceImpact(Xincl, sentAmount) - swapResult, liquidityFee := CalcSwapResult(toRowan, Xincl, sentAmount, Yincl, pmtpCurrentRunningRate, swapFeeParams.SwapFeeRate, minSwapFee) + swapResult, liquidityFee := CalcSwapResult(toRowan, Xincl, sentAmount, Yincl, pmtpCurrentRunningRate, swapFeeRate) // NOTE: impossible... pre-pmtp at least if swapResult.GTE(Y) { @@ -456,17 +585,22 @@ func SwapOne(from types.Asset, func GetSwapFee(sentAmount sdk.Uint, to types.Asset, pool types.Pool, - pmtpCurrentRunningRate sdk.Dec, swapFeeParams types.SwapFeeParams) sdk.Uint { - minSwapFee := GetMinSwapFee(to, swapFeeParams.TokenParams) + pmtpCurrentRunningRate, swapFeeRate sdk.Dec) sdk.Uint { - X, Y, toRowan := pool.ExtractValues(to) + X, Y, toRowan, _ := pool.ExtractValues(to) X, Y = pool.ExtractDebt(X, Y, toRowan) - swapResult, _ := CalcSwapResult(toRowan, X, sentAmount, Y, pmtpCurrentRunningRate, swapFeeParams.SwapFeeRate, minSwapFee) + swapResult, _ := CalcSwapResult(toRowan, X, sentAmount, Y, pmtpCurrentRunningRate, swapFeeRate) if swapResult.GTE(Y) { return sdk.ZeroUint() } return swapResult } + +func CalculateDiscountedSentAmount(sentAmount sdk.Uint, swapFeeRate sdk.Dec) sdk.Uint { + discountedSentAmount := sentAmount.Sub(sdk.Uint(sdk.NewDecFromBigInt(sentAmount.BigInt()).Mul(swapFeeRate).RoundInt())) + + return discountedSentAmount +} diff --git a/x/clp/keeper/calculations_test.go b/x/clp/keeper/calculations_test.go index 5d56ce7db4..d5a65e176b 100644 --- a/x/clp/keeper/calculations_test.go +++ b/x/clp/keeper/calculations_test.go @@ -73,7 +73,7 @@ func TestKeeper_SwapOne(t *testing.T) { fromAsset types.Asset toAsset types.Asset pmtpCurrentRunningRate sdk.Dec - swapFeeParams types.SwapFeeParams + swapFeeRate sdk.Dec errString error expectedSwapResult sdk.Uint expectedLiquidityFee sdk.Uint @@ -93,7 +93,7 @@ func TestKeeper_SwapOne(t *testing.T) { fromAsset: types.GetSettlementAsset(), toAsset: types.NewAsset("eth"), pmtpCurrentRunningRate: sdk.NewDec(0), - swapFeeParams: types.SwapFeeParams{SwapFeeRate: sdk.NewDecWithPrec(3, 3)}, + swapFeeRate: sdk.NewDecWithPrec(3, 3), expectedSwapResult: sdk.NewUint(43501), expectedLiquidityFee: sdk.NewUint(130), expectedPriceImpact: sdk.ZeroUint(), @@ -112,7 +112,7 @@ func TestKeeper_SwapOne(t *testing.T) { toAsset: types.GetSettlementAsset(), fromAsset: types.NewAsset("cusdt"), pmtpCurrentRunningRate: sdk.NewDec(0), - swapFeeParams: types.SwapFeeParams{SwapFeeRate: sdk.NewDecWithPrec(3, 3)}, + swapFeeRate: sdk.NewDecWithPrec(3, 3), expectedSwapResult: sdk.NewUintFromString("11704434254784015637542"), expectedLiquidityFee: sdk.NewUintFromString("35218959643281892590"), expectedPriceImpact: sdk.ZeroUint(), @@ -131,32 +131,13 @@ func TestKeeper_SwapOne(t *testing.T) { fromAsset: types.GetSettlementAsset(), toAsset: types.NewAsset("eth"), pmtpCurrentRunningRate: sdk.NewDec(0), - swapFeeParams: types.SwapFeeParams{SwapFeeRate: sdk.NewDecWithPrec(3, 3)}, + swapFeeRate: sdk.NewDecWithPrec(3, 3), expectedSwapResult: sdk.NewUint(43550), expectedLiquidityFee: sdk.NewUint(131), expectedPriceImpact: sdk.ZeroUint(), expectedExternalAssetBalance: sdk.NewUint(8726450), expectedNativeAssetBalance: sdk.NewUint(10050000), }, - { - name: "real world numbers - fee < minSwapFee", - nativeAssetBalance: sdk.NewUint(10000000), - externalAssetBalance: sdk.NewUint(8770000), - nativeCustody: sdk.ZeroUint(), - externalCustody: sdk.ZeroUint(), - nativeLiabilities: sdk.ZeroUint(), - externalLiabilities: sdk.ZeroUint(), - sentAmount: sdk.NewUint(50000), - fromAsset: types.GetSettlementAsset(), - toAsset: types.NewAsset("eth"), - pmtpCurrentRunningRate: sdk.NewDec(0), - swapFeeParams: types.SwapFeeParams{SwapFeeRate: sdk.NewDecWithPrec(3, 3), TokenParams: []*types.SwapFeeTokenParams{{Asset: "eth", MinSwapFee: sdk.NewUint(300)}, {Asset: types.GetSettlementAsset().Symbol, MinSwapFee: sdk.NewUint(100)}}}, - expectedSwapResult: sdk.NewUint(43331), - expectedLiquidityFee: sdk.NewUint(300), - expectedPriceImpact: sdk.ZeroUint(), - expectedExternalAssetBalance: sdk.NewUint(8726669), - expectedNativeAssetBalance: sdk.NewUint(10050000), - }, } for _, tc := range testcases { @@ -170,7 +151,7 @@ func TestKeeper_SwapOne(t *testing.T) { pool.NativeLiabilities = tc.nativeLiabilities pool.ExternalLiabilities = tc.externalLiabilities - swapResult, liquidityFee, priceImpact, pool, err := clpkeeper.SwapOne(tc.fromAsset, tc.sentAmount, tc.toAsset, pool, tc.pmtpCurrentRunningRate, tc.swapFeeParams) + swapResult, liquidityFee, priceImpact, pool, err := clpkeeper.SwapOne(tc.fromAsset, tc.sentAmount, tc.toAsset, pool, tc.pmtpCurrentRunningRate, tc.swapFeeRate) if tc.errString != nil { require.EqualError(t, err, tc.errString.Error()) @@ -202,11 +183,12 @@ func TestKeeper_ExtractValuesFromPool(t *testing.T) { msgCreatePool := types.NewMsgCreatePool(signer, asset, nativeAssetAmount, externalAssetAmount) // Create Pool pool, _ := app.ClpKeeper.CreatePool(ctx, sdk.NewUint(1), &msgCreatePool) - X, Y, toRowan := pool.ExtractValues(asset) + X, Y, toRowan, from := pool.ExtractValues(asset) assert.Equal(t, X, sdk.NewUint(998)) assert.Equal(t, Y, sdk.NewUint(998)) assert.Equal(t, toRowan, false) + assert.Equal(t, types.GetSettlementAsset(), from) } func TestKeeper_GetSwapFee(t *testing.T) { @@ -223,8 +205,8 @@ func TestKeeper_GetSwapFee(t *testing.T) { msgCreatePool := types.NewMsgCreatePool(signer, asset, nativeAssetAmount, externalAssetAmount) // Create Pool pool, _ := app.ClpKeeper.CreatePool(ctx, sdk.NewUint(1), &msgCreatePool) - swapFeeParams := types.SwapFeeParams{SwapFeeRate: sdk.NewDecWithPrec(3, 3)} - swapResult := clpkeeper.GetSwapFee(sdk.NewUint(1), asset, *pool, sdk.OneDec(), swapFeeParams) + swapFeeRate := sdk.NewDecWithPrec(3, 3) + swapResult := clpkeeper.GetSwapFee(sdk.NewUint(1), asset, *pool, sdk.OneDec(), swapFeeRate) assert.Equal(t, "1", swapResult.String()) } @@ -239,9 +221,9 @@ func TestKeeper_GetSwapFee_PmtpParams(t *testing.T) { } asset := types.Asset{} - swapFeeParams := types.SwapFeeParams{SwapFeeRate: sdk.NewDecWithPrec(3, 3)} + swapFeeRate := sdk.NewDecWithPrec(3, 3) - swapResult := clpkeeper.GetSwapFee(sdk.NewUint(1), asset, pool, sdk.NewDec(100), swapFeeParams) + swapResult := clpkeeper.GetSwapFee(sdk.NewUint(1), asset, pool, sdk.NewDec(100), swapFeeRate) require.Equal(t, swapResult, sdk.ZeroUint()) } @@ -256,255 +238,6 @@ func TestKeeper_CalculateAssetsForLP(t *testing.T) { assert.Equal(t, "1000", native.String()) } -func TestKeeper_CalculatePoolUnits(t *testing.T) { - testcases := []struct { - name string - oldPoolUnits sdk.Uint - nativeAssetBalance sdk.Uint - externalAssetBalance sdk.Uint - nativeAssetAmount sdk.Uint - externalAssetAmount sdk.Uint - externalDecimals uint8 - poolUnits sdk.Uint - lpunits sdk.Uint - err error - errString error - panicErr string - }{ - { - name: "tx amount too low throws error", - oldPoolUnits: sdk.ZeroUint(), - nativeAssetBalance: sdk.ZeroUint(), - externalAssetBalance: sdk.ZeroUint(), - nativeAssetAmount: sdk.ZeroUint(), - externalAssetAmount: sdk.ZeroUint(), - externalDecimals: 18, - errString: errors.New("Tx amount is too low"), - }, - { - name: "tx amount too low with no adjustment throws error", - oldPoolUnits: sdk.ZeroUint(), - nativeAssetBalance: sdk.ZeroUint(), - externalAssetBalance: sdk.ZeroUint(), - nativeAssetAmount: sdk.ZeroUint(), - externalAssetAmount: sdk.ZeroUint(), - externalDecimals: 18, - errString: errors.New("Tx amount is too low"), - }, - { - name: "insufficient native funds throws error", - oldPoolUnits: sdk.ZeroUint(), - nativeAssetBalance: sdk.ZeroUint(), - externalAssetBalance: sdk.ZeroUint(), - nativeAssetAmount: sdk.ZeroUint(), - externalAssetAmount: sdk.OneUint(), - externalDecimals: 18, - errString: errors.New("0: insufficient funds"), - }, - { - name: "insufficient external funds throws error", - oldPoolUnits: sdk.ZeroUint(), - nativeAssetBalance: sdk.NewUint(100), - externalAssetBalance: sdk.ZeroUint(), - nativeAssetAmount: sdk.OneUint(), - externalAssetAmount: sdk.ZeroUint(), - externalDecimals: 18, - errString: errors.New("0: insufficient funds"), - }, - { - name: "as native asset balance zero then returns native asset amount", - oldPoolUnits: sdk.ZeroUint(), - nativeAssetBalance: sdk.ZeroUint(), - externalAssetBalance: sdk.NewUint(100), - nativeAssetAmount: sdk.OneUint(), - externalAssetAmount: sdk.OneUint(), - externalDecimals: 18, - poolUnits: sdk.OneUint(), - lpunits: sdk.OneUint(), - }, - { - name: "successful", - oldPoolUnits: sdk.ZeroUint(), - nativeAssetBalance: sdk.NewUint(100), - externalAssetBalance: sdk.NewUint(100), - nativeAssetAmount: sdk.OneUint(), - externalAssetAmount: sdk.OneUint(), - externalDecimals: 18, - poolUnits: sdk.ZeroUint(), - lpunits: sdk.ZeroUint(), - }, - { - name: "fail asymmetric", - oldPoolUnits: sdk.ZeroUint(), - nativeAssetBalance: sdk.NewUint(10000), - externalAssetBalance: sdk.NewUint(100), - nativeAssetAmount: sdk.OneUint(), - externalAssetAmount: sdk.OneUint(), - externalDecimals: 18, - poolUnits: sdk.ZeroUint(), - lpunits: sdk.ZeroUint(), - errString: errors.New("Cannot add liquidity asymmetrically"), - }, - { - name: "successful", - oldPoolUnits: sdk.NewUint(1), - nativeAssetBalance: sdk.NewUint(1), - externalAssetBalance: sdk.NewUint(1), - nativeAssetAmount: sdk.NewUint(1), - externalAssetAmount: sdk.NewUint(1), - externalDecimals: 18, - poolUnits: sdk.NewUint(2), - lpunits: sdk.NewUint(1), - }, - { - name: "successful no slip", - oldPoolUnits: sdk.NewUint(1099511627776), //2**40 - nativeAssetBalance: sdk.NewUint(1099511627776), - externalAssetBalance: sdk.NewUint(1099511627776), - nativeAssetAmount: sdk.NewUint(1099511627776), - externalAssetAmount: sdk.NewUint(1099511627776), - externalDecimals: 18, - poolUnits: sdk.NewUint(2199023255552), - lpunits: sdk.NewUint(1099511627776), - }, - { - name: "no asymmetric", - oldPoolUnits: sdk.NewUint(1099511627776), //2**40 - nativeAssetBalance: sdk.NewUint(1048576), - externalAssetBalance: sdk.NewUint(1024123), - nativeAssetAmount: sdk.NewUint(999), - externalAssetAmount: sdk.NewUint(111), - externalDecimals: 18, - poolUnits: sdk.NewUintFromString("1100094484982"), - lpunits: sdk.NewUintFromString("582857206"), - errString: errors.New("Cannot add liquidity asymmetrically"), - }, - { - name: "successful - very big", - oldPoolUnits: sdk.NewUintFromString("1606938044258990275541962092341162602522202993782792835301376"), //2**200 - nativeAssetBalance: sdk.NewUintFromString("1606938044258990275541962092341162602522202993782792835301376"), - externalAssetBalance: sdk.NewUintFromString("1606938044258990275541962092341162602522202993782792835301376"), - nativeAssetAmount: sdk.NewUint(1099511627776), // 2**40 - externalAssetAmount: sdk.NewUint(1099511627776), - externalDecimals: 18, - poolUnits: sdk.NewUintFromString("1606938044258990275541962092341162602522202993783892346929152"), - lpunits: sdk.NewUint(1099511627776), - }, - { - name: "failure - asymmetric", - oldPoolUnits: sdk.NewUintFromString("23662660550457383692937954"), - nativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), - externalAssetBalance: sdk.NewUint(2674623482959), - nativeAssetAmount: sdk.NewUint(0), - externalAssetAmount: sdk.NewUint(200000000), - externalDecimals: 18, - errString: errors.New("Cannot add liquidity with asymmetric ratio"), - }, - { - name: "opportunist scenario - fails trivially due to div zero", - oldPoolUnits: sdk.NewUintFromString("23662660550457383692937954"), - nativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), - externalAssetBalance: sdk.NewUint(2674623482959), - nativeAssetAmount: sdk.NewUint(0), - externalAssetAmount: sdk.NewUint(200000000), - externalDecimals: 6, - errString: errors.New("Cannot add liquidity with asymmetric ratio"), - }, - { - name: "opportunist scenario with one native asset - avoids div zero trivial fail", - oldPoolUnits: sdk.NewUintFromString("23662660550457383692937954"), - nativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), - externalAssetBalance: sdk.NewUint(2674623482959), - nativeAssetAmount: sdk.NewUint(1), - externalAssetAmount: sdk.NewUint(200000000), - externalDecimals: 6, - errString: errors.New("Cannot add liquidity with asymmetric ratio"), - }, - { - name: "success", - oldPoolUnits: sdk.NewUintFromString("23662660550457383692937954"), - nativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), - externalAssetBalance: sdk.NewUint(2674623482959), - nativeAssetAmount: sdk.NewUintFromString("4000000000000000000"), - externalAssetAmount: sdk.NewUint(68140), - externalDecimals: 6, - poolUnits: sdk.NewUintFromString("23662661153298835875523384"), - lpunits: sdk.NewUintFromString("602841452182585430"), - }, - { - // Same test as above but with external asset amount just below top limit - name: "success (normalized) ratios diff = 0.00496468840", - oldPoolUnits: sdk.NewUintFromString("23662660550457383692937954"), - nativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), - externalAssetBalance: sdk.NewUint(2674623482959), - nativeAssetAmount: sdk.NewUintFromString("4000000000000000000"), - externalAssetAmount: sdk.NewUint(68480), - externalDecimals: 6, - poolUnits: sdk.NewUintFromString("23662661154802842743687067"), - lpunits: sdk.NewUintFromString("604345459050749113"), - }, - { - // Same test as above but with external asset amount just above top limit - name: "failure (normalized) ratios diff = 0.0050954439", - oldPoolUnits: sdk.NewUintFromString("23662660550457383692937954"), - nativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), - externalAssetBalance: sdk.NewUint(2674623482959), - nativeAssetAmount: sdk.NewUintFromString("4000000000000000000"), - externalAssetAmount: sdk.NewUint(68489), - externalDecimals: 6, - errString: errors.New("Cannot add liquidity with asymmetric ratio"), - }, - } - - symmetryThreshold := sdk.NewDecWithPrec(1, 4) - ratioThreshold := sdk.NewDecWithPrec(5, 3) - for _, tc := range testcases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - if tc.panicErr != "" { - // nolint:errcheck - require.PanicsWithError(t, tc.panicErr, func() { - clpkeeper.CalculatePoolUnits( - tc.oldPoolUnits, - tc.nativeAssetBalance, - tc.externalAssetBalance, - tc.nativeAssetAmount, - tc.externalAssetAmount, - tc.externalDecimals, - symmetryThreshold, - ratioThreshold, - ) - }) - return - } - - poolUnits, lpunits, err := clpkeeper.CalculatePoolUnits( - tc.oldPoolUnits, - tc.nativeAssetBalance, - tc.externalAssetBalance, - tc.nativeAssetAmount, - tc.externalAssetAmount, - tc.externalDecimals, - symmetryThreshold, - ratioThreshold, - ) - - if tc.errString != nil { - require.EqualError(t, err, tc.errString.Error()) - return - } - if tc.err != nil { - require.ErrorIs(t, err, tc.err) - return - } - - require.NoError(t, err) - require.Equal(t, tc.poolUnits.String(), poolUnits.String()) // compare strings so that the expected amounts can be read from the failure message - require.Equal(t, tc.lpunits.String(), lpunits.String()) - }) - } -} - func TestKeeper_CalculateWithdrawal(t *testing.T) { testcases := []struct { name string @@ -579,7 +312,6 @@ func TestKeeper_CalculateWithdrawal(t *testing.T) { } for _, tc := range testcases { - tc := tc t.Run(tc.name, func(t *testing.T) { if tc.panicErr != "" { require.PanicsWithError(t, tc.panicErr, func() { @@ -600,13 +332,13 @@ func TestKeeper_CalculateWithdrawal(t *testing.T) { func TestKeeper_CalcSwapResult(t *testing.T) { testcases := []struct { - name string - toRowan bool - X, x, Y, y, minSwapFee, expectedFee sdk.Uint - pmtpCurrentRunningRate sdk.Dec - swapFeeRate sdk.Dec - err error - errString error + name string + toRowan bool + X, x, Y, y, expectedFee sdk.Uint + pmtpCurrentRunningRate sdk.Dec + swapFeeRate sdk.Dec + err error + errString error }{ { name: "one side of pool empty", @@ -618,7 +350,6 @@ func TestKeeper_CalcSwapResult(t *testing.T) { expectedFee: sdk.NewUint(0), pmtpCurrentRunningRate: sdk.NewDec(2), swapFeeRate: sdk.NewDecWithPrec(3, 3), - minSwapFee: sdk.NewUint(500), }, { name: "swap amount zero", @@ -630,7 +361,6 @@ func TestKeeper_CalcSwapResult(t *testing.T) { expectedFee: sdk.NewUint(0), pmtpCurrentRunningRate: sdk.NewDec(2), swapFeeRate: sdk.NewDecWithPrec(3, 3), - minSwapFee: sdk.NewUint(500), }, { name: "real world amounts, buy rowan", @@ -642,7 +372,6 @@ func TestKeeper_CalcSwapResult(t *testing.T) { expectedFee: sdk.NewUint(200019938000), pmtpCurrentRunningRate: sdk.NewDec(2), swapFeeRate: sdk.NewDecWithPrec(3, 3), - minSwapFee: sdk.NewUint(0), }, { name: "real world amounts, sell rowan", @@ -654,31 +383,6 @@ func TestKeeper_CalcSwapResult(t *testing.T) { expectedFee: sdk.NewUint(1800179442000), pmtpCurrentRunningRate: sdk.NewDec(2), swapFeeRate: sdk.NewDecWithPrec(3, 3), - minSwapFee: sdk.NewUint(0), - }, - { - name: "real world amounts, sell rowan, fee < minSwapFee & minSwapFee > adjustedAmount", - toRowan: false, - X: sdk.NewUint(1999800619938006200), - x: sdk.NewUint(200000000000000), - Y: sdk.NewUint(2000200000000000000), - y: sdk.NewUint(0), - expectedFee: sdk.NewUint(600059814000057), - pmtpCurrentRunningRate: sdk.NewDec(2), - swapFeeRate: sdk.NewDecWithPrec(3, 3), - minSwapFee: sdk.NewUint(20000000000000000), - }, - { - name: "real world amounts, sell rowan, fee < minSwapFee", - toRowan: false, - X: sdk.NewUint(1999800619938006200), - x: sdk.NewUint(200000000000000), - Y: sdk.NewUint(2000200000000000000), - y: sdk.NewUint(598059814000057), - expectedFee: sdk.NewUint(2000000000000), - pmtpCurrentRunningRate: sdk.NewDec(2), - swapFeeRate: sdk.NewDecWithPrec(3, 3), - minSwapFee: sdk.NewUint(2000000000000), }, { name: "big numbers", @@ -690,14 +394,12 @@ func TestKeeper_CalcSwapResult(t *testing.T) { expectedFee: sdk.NewUintFromString("3300330033003300475524186081974530927560579473952430682017779080504977"), pmtpCurrentRunningRate: sdk.NewDec(2), swapFeeRate: sdk.NewDecWithPrec(3, 3), - minSwapFee: sdk.NewUint(0), }, } for _, tc := range testcases { - tc := tc t.Run(tc.name, func(t *testing.T) { - y, fee := clpkeeper.CalcSwapResult(tc.toRowan, tc.X, tc.x, tc.Y, tc.pmtpCurrentRunningRate, tc.swapFeeRate, tc.minSwapFee) + y, fee := clpkeeper.CalcSwapResult(tc.toRowan, tc.X, tc.x, tc.Y, tc.pmtpCurrentRunningRate, tc.swapFeeRate) require.Equal(t, tc.y.String(), y.String()) // compare strings so that the expected amounts can be read from the failure message require.Equal(t, tc.expectedFee.String(), fee.String()) @@ -767,7 +469,6 @@ func TestKeeper_CalcDenomChangeMultiplier(t *testing.T) { } for _, tc := range testcases { - tc := tc t.Run(tc.name, func(t *testing.T) { y := clpkeeper.CalcDenomChangeMultiplier(tc.decimalsX, tc.decimalsY) @@ -884,7 +585,6 @@ func TestKeeper_CalcSpotPriceX(t *testing.T) { } for _, tc := range testcases { - tc := tc t.Run(tc.name, func(t *testing.T) { price, err := clpkeeper.CalcSpotPriceX(tc.X, tc.Y, tc.decimalsX, tc.decimalsY, tc.pmtpCurrentRunningRate, tc.isXNative) @@ -986,7 +686,6 @@ func TestKeeper_CalcSpotPriceNative(t *testing.T) { } for _, tc := range testcases { - tc := tc t.Run(tc.name, func(t *testing.T) { pool := types.Pool{ NativeAssetBalance: tc.nativeAssetBalance, @@ -1096,7 +795,6 @@ func TestKeeper_CalcSpotPriceExternal(t *testing.T) { } for _, tc := range testcases { - tc := tc t.Run(tc.name, func(t *testing.T) { pool := types.Pool{ NativeAssetBalance: tc.nativeAssetBalance, @@ -1120,66 +818,12 @@ func TestKeeper_CalcSpotPriceExternal(t *testing.T) { } } -func TestKeeper_CalculateRatioPercentDiff(t *testing.T) { - - testcases := []struct { - name string - A, R, a, r *big.Int - expected sdk.Dec - errString error - }{ - { - name: "symmetric", - A: big.NewInt(20), - R: big.NewInt(10), - a: big.NewInt(8), - r: big.NewInt(4), - expected: sdk.MustNewDecFromStr("1.000000000000000000"), - }, - { - name: "not symmetric", - A: big.NewInt(20), - R: big.NewInt(10), - a: big.NewInt(16), - r: big.NewInt(4), - expected: sdk.MustNewDecFromStr("0.500000000000000000"), - }, - { - name: "not symmetric", - A: big.NewInt(501), - R: big.NewInt(100), - a: big.NewInt(5), - r: big.NewInt(1), - expected: sdk.MustNewDecFromStr("1.002000000000000000"), - }, - } - - for _, tc := range testcases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - - ratio, err := clpkeeper.CalculateRatioPercentDiff(tc.A, tc.R, tc.a, tc.r) - - if tc.errString != nil { - require.EqualError(t, err, tc.errString.Error()) - return - } - - require.NoError(t, err) - - ratioDec, _ := clpkeeper.RatToDec(&ratio) - - require.Equal(t, tc.expected.String(), ratioDec.String()) - }) - } -} - func TestKeeper_CalcRowanSpotPrice(t *testing.T) { testcases := []struct { name string rowanBalance, externalBalance sdk.Uint pmtpCurrentRunningRate sdk.Dec - expectedSpotPrice sdk.Dec + expectedPrice sdk.Dec expectedError error }{ { @@ -1187,14 +831,14 @@ func TestKeeper_CalcRowanSpotPrice(t *testing.T) { rowanBalance: sdk.NewUint(1), externalBalance: sdk.NewUint(1), pmtpCurrentRunningRate: sdk.NewDec(1), - expectedSpotPrice: sdk.MustNewDecFromStr("2"), + expectedPrice: sdk.MustNewDecFromStr("2"), }, { name: "success small", rowanBalance: sdk.NewUint(1000000000123), externalBalance: sdk.NewUint(20000000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("1.4"), - expectedSpotPrice: sdk.MustNewDecFromStr("0.000047999999994096"), + expectedPrice: sdk.MustNewDecFromStr("0.000047999999994096"), }, { @@ -1202,7 +846,7 @@ func TestKeeper_CalcRowanSpotPrice(t *testing.T) { rowanBalance: sdk.NewUint(1000), externalBalance: sdk.NewUint(2000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("1.4"), - expectedSpotPrice: sdk.MustNewDecFromStr("4.8"), + expectedPrice: sdk.MustNewDecFromStr("4.8"), }, { name: "fail - rowan balance zero", @@ -1214,7 +858,6 @@ func TestKeeper_CalcRowanSpotPrice(t *testing.T) { } for _, tc := range testcases { - tc := tc t.Run(tc.name, func(t *testing.T) { pool := types.Pool{ NativeAssetBalance: tc.rowanBalance, @@ -1225,79 +868,480 @@ func TestKeeper_CalcRowanSpotPrice(t *testing.T) { ExternalCustody: sdk.ZeroUint(), } - spotPrice, err := clpkeeper.CalcRowanSpotPrice(&pool, tc.pmtpCurrentRunningRate) + calcPrice, err := clpkeeper.CalcRowanSpotPrice(&pool, tc.pmtpCurrentRunningRate) if tc.expectedError != nil { require.EqualError(t, tc.expectedError, err.Error()) return } require.NoError(t, err) - require.Equal(t, tc.expectedSpotPrice, spotPrice) + require.Equal(t, tc.expectedPrice, calcPrice) }) } } func TestKeeper_CalcRowanValue(t *testing.T) { testcases := []struct { - name string - rowanBalance, externalBalance sdk.Uint - rowanAmount sdk.Uint - pmtpCurrentRunningRate sdk.Dec - expectedValue sdk.Uint - expectedError error + name string + rowanAmount sdk.Uint + price sdk.Dec + expectedValue sdk.Uint }{ { - name: "success simple", - rowanBalance: sdk.NewUint(1), - externalBalance: sdk.NewUint(1), - pmtpCurrentRunningRate: sdk.NewDec(1), - rowanAmount: sdk.NewUint(100), - expectedValue: sdk.NewUint(200), + name: "success simple", + rowanAmount: sdk.NewUint(100), + price: sdk.NewDecWithPrec(232, 2), + expectedValue: sdk.NewUint(232), + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + rowanValue := clpkeeper.CalcRowanValue(tc.rowanAmount, tc.price) + require.Equal(t, tc.expectedValue.String(), rowanValue.String()) + }) + } +} + +// // Used only to generate expected results for TestKeeper_CalculateExternalSwapAmountAsymmetricRat +// // Useful to keep around if more test cases are needed in future +// func TestKeeper_GenerateCalculateExternalSwapAmountAsymmetricRatTestCases(t *testing.T) { +// testcases := []struct { +// Y, X, y, x, f, r float64 +// expectedValue float64 +// }{ +// { +// Y: 100000, +// X: 100000, +// y: 2000, +// x: 8000, +// f: 0.003, +// r: 0.01, +// }, +// { +// Y: 3456789887, +// X: 1244516357, +// y: 2000, +// x: 99887776, +// f: 0.003, +// r: 0.01, +// }, +// { +// Y: 157007500498726220240179086, +// X: 2674623482959, +// y: 0, +// x: 200000000, +// f: 0.003, +// r: 0.01, +// }, +// } + +// for _, tc := range testcases { +// Y := tc.Y +// X := tc.X +// y := tc.y +// x := tc.x +// f := tc.f +// r := tc.r +// expected := math.Abs((math.Sqrt(Y*(-1*(x+X))*(-1*f*f*x*Y-f*f*X*Y-2*f*r*x*Y+4*f*r*X*y+2*f*r*X*Y+4*f*X*y+4*f*X*Y-r*r*x*Y-r*r*X*Y-4*r*X*y-4*r*X*Y-4*X*y-4*X*Y)) + f*x*Y + f*X*Y + r*x*Y - 2*r*X*y - r*X*Y - 2*X*y - 2*X*Y) / (2 * (r + 1) * (y + Y))) +// fmt.Println(expected) +// } + +// } +func TestKeeper_CalculateExternalSwapAmountAsymmetricRat(t *testing.T) { + testcases := []struct { + name string + Y, X, y, x, f, r *big.Rat + expectedValue sdk.Dec + }{ + { + name: "test1", + Y: big.NewRat(100000, 1), + X: big.NewRat(100000, 1), + y: big.NewRat(2000, 1), + x: big.NewRat(8000, 1), + f: big.NewRat(3, 1000), // 0.003 + r: big.NewRat(1, 100), // 0.01 + expectedValue: sdk.MustNewDecFromStr("2918.476067753834206950"), + }, + { + name: "test2", + Y: big.NewRat(3456789887, 1), + X: big.NewRat(1244516357, 1), + y: big.NewRat(2000, 1), + x: big.NewRat(99887776, 1), + f: big.NewRat(3, 1000), // 0.003 + r: big.NewRat(1, 100), // 0.01 + expectedValue: sdk.MustNewDecFromStr("49309453.001511112834211406"), + }, + { + name: "test3", + Y: MustRatFromString("157007500498726220240179086"), + X: big.NewRat(2674623482959, 1), + y: big.NewRat(0, 1), + x: big.NewRat(200000000, 1), + f: big.NewRat(3, 1000), // 0.003 + r: big.NewRat(1, 100), // 0.01 + expectedValue: sdk.MustNewDecFromStr("100645875.768947133021515445"), }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + res := clpkeeper.CalculateExternalSwapAmountAsymmetricRat(tc.Y, tc.X, tc.y, tc.x, tc.f, tc.r) + got, _ := clpkeeper.RatToDec(&res) + + require.Equal(t, tc.expectedValue.String(), got.String()) + }) + } + +} + +// // Used only to generate expected results for TestKeeper_CalculateNativeSwapAmountAsymmetricRat +// // Useful to keep around if more test cases are needed in future +// func TestKeeper_GenerateCalculateNativeSwapAmountAsymmetricRatTestCases(t *testing.T) { +// testcases := []struct { +// Y, X, y, x, f, r float64 +// expectedValue float64 +// }{ +// { +// Y: 100000, +// X: 100000, +// y: 8000, +// x: 2000, +// f: 0.003, +// r: 0.01, +// }, +// { +// Y: 3456789887, +// X: 1244516357, +// y: 99887776, +// x: 2000, +// f: 0.003, +// r: 0.01, +// }, +// } + +// for _, tc := range testcases { +// Y := tc.Y +// X := tc.X +// y := tc.y +// x := tc.x +// f := tc.f +// r := tc.r +// expected := math.Abs((math.Sqrt(math.Pow((-1*f*r*X*y-f*r*X*Y-f*X*y-f*X*Y+r*X*y+r*X*Y+2*x*Y+2*X*Y), 2)-4*(x+X)*(x*Y*Y-X*y*Y)) + f*r*X*y + f*r*X*Y + f*X*y + f*X*Y - r*X*y - r*X*Y - 2*x*Y - 2*X*Y) / (2 * (x + X))) +// fmt.Println(expected) +// } + +// } +func TestKeeper_CalculateNativeSwapAmountAsymmetricRat(t *testing.T) { + testcases := []struct { + name string + Y, X, y, x, f, r *big.Rat + expectedValue sdk.Dec + }{ { - name: "success zero", - rowanBalance: sdk.NewUint(1000000000123), - externalBalance: sdk.NewUint(20000000), - pmtpCurrentRunningRate: sdk.MustNewDecFromStr("1.4"), - rowanAmount: sdk.NewUint(100), - expectedValue: sdk.NewUint(0), + name: "test1", + Y: big.NewRat(100000, 1), + X: big.NewRat(100000, 1), + y: big.NewRat(8000, 1), + x: big.NewRat(2000, 1), + f: big.NewRat(3, 1000), // 0.003 + r: big.NewRat(1, 100), // 0.01 + expectedValue: sdk.MustNewDecFromStr("2888.791254901960784313"), + }, + { + name: "test2", + Y: big.NewRat(3456789887, 1), + X: big.NewRat(1244516357, 1), + y: big.NewRat(99887776, 1), + x: big.NewRat(2000, 1), + f: big.NewRat(3, 1000), // 0.003 + r: big.NewRat(1, 100), // 0.01 + expectedValue: sdk.MustNewDecFromStr("49410724.289235769454274911"), + }, + { + //NOTE: cannot be confirmed with the float64 model above since that runs out of precision. + // However the expectedValue is about half the value of y, which is as expected + name: "test3", + Y: MustRatFromString("157007500498726220240179086"), + X: big.NewRat(2674623482959, 1), + y: big.NewRat(200000000, 1), + x: big.NewRat(0, 1), + f: big.NewRat(3, 1000), // 0.003 + r: big.NewRat(1, 100), // 0.01 + expectedValue: sdk.MustNewDecFromStr("99652710.304588509013984918"), }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + res := clpkeeper.CalculateNativeSwapAmountAsymmetricRat(tc.Y, tc.X, tc.y, tc.x, tc.f, tc.r) + got, _ := clpkeeper.RatToDec(&res) + + require.Equal(t, tc.expectedValue.String(), got.String()) + + }) + } + +} + +func MustRatFromString(x string) *big.Rat { + res, success := big.NewRat(1, 1).SetString("157007500498726220240179086") + if success == false { + panic("Could not create rat from string") + } + return res +} + +func TestKeeper_GetLiquidityAddSymmetryType(t *testing.T) { + testcases := []struct { + name string + X, x, Y, y sdk.Uint + expectedValue int + }{ { - name: "success", - rowanBalance: sdk.NewUint(1000), - externalBalance: sdk.NewUint(2000), - pmtpCurrentRunningRate: sdk.MustNewDecFromStr("1.4"), - rowanAmount: sdk.NewUint(100), - expectedValue: sdk.NewUint(480), + name: "one side of the pool empty", + X: sdk.ZeroUint(), + x: sdk.NewUint(11200), + Y: sdk.NewUint(100), + y: sdk.NewUint(100), + expectedValue: clpkeeper.ErrorEmptyPool, }, { - name: "fail - rowan balance zero", - rowanBalance: sdk.NewUint(0), - externalBalance: sdk.NewUint(2000), - pmtpCurrentRunningRate: sdk.MustNewDecFromStr("1.4"), - rowanAmount: sdk.NewUint(100), - expectedError: errors.New("amount is invalid"), + name: "nothing added", + X: sdk.NewUint(11200), + x: sdk.ZeroUint(), + Y: sdk.NewUint(1000), + y: sdk.ZeroUint(), + expectedValue: clpkeeper.ErrorNothingAdded, + }, + { + name: "negative symmetry - x zero", + X: sdk.NewUint(11200), + x: sdk.ZeroUint(), + Y: sdk.NewUint(1000), + y: sdk.NewUint(100), + expectedValue: clpkeeper.NeedMoreX, + }, + { + name: "negative symmetry - x > 0", + X: sdk.NewUint(11200), + x: sdk.NewUint(15), + Y: sdk.NewUint(1000), + y: sdk.NewUint(100), + expectedValue: clpkeeper.NeedMoreX, + }, + { + name: "symmetric", + X: sdk.NewUint(11200), + x: sdk.NewUint(1120), + Y: sdk.NewUint(1000), + y: sdk.NewUint(100), + expectedValue: clpkeeper.Symmetric, + }, + { + name: "positive symmetry", + X: sdk.NewUint(11200), + x: sdk.NewUint(100), + Y: sdk.NewUint(1000), + y: sdk.NewUint(5), + expectedValue: clpkeeper.NeedMoreY, }, } for _, tc := range testcases { - tc := tc t.Run(tc.name, func(t *testing.T) { - pool := types.Pool{ - NativeAssetBalance: tc.rowanBalance, - ExternalAssetBalance: tc.externalBalance, - NativeLiabilities: sdk.ZeroUint(), - NativeCustody: sdk.ZeroUint(), - ExternalLiabilities: sdk.ZeroUint(), - ExternalCustody: sdk.ZeroUint(), - } + res := clpkeeper.GetLiquidityAddSymmetryState(tc.X, tc.x, tc.Y, tc.y) + + require.Equal(t, tc.expectedValue, res) + }) + } +} + +func TestKeeper_CalculatePoolUnits(t *testing.T) { + testcases := []struct { + name string + oldPoolUnits sdk.Uint + nativeAssetBalance sdk.Uint + externalAssetBalance sdk.Uint + nativeAssetAmount sdk.Uint + externalAssetAmount sdk.Uint + expectedPoolUnits sdk.Uint + expectedLPunits sdk.Uint + expectedSwapStatus int + expectedSwapAmount sdk.Uint + expectedError error + }{ + { + name: "empty pool", + oldPoolUnits: sdk.ZeroUint(), + nativeAssetBalance: sdk.ZeroUint(), + externalAssetBalance: sdk.ZeroUint(), + nativeAssetAmount: sdk.NewUint(100), + externalAssetAmount: sdk.NewUint(90), + expectedPoolUnits: sdk.NewUint(100), + expectedLPunits: sdk.NewUint(100), + expectedSwapStatus: clpkeeper.NoSwap, + }, + { + name: "empty pool - no external asset added", + oldPoolUnits: sdk.ZeroUint(), + nativeAssetBalance: sdk.ZeroUint(), + externalAssetBalance: sdk.ZeroUint(), + nativeAssetAmount: sdk.NewUint(100), + externalAssetAmount: sdk.ZeroUint(), + expectedError: errors.New("amount is invalid"), + }, + { + name: "add nothing", + oldPoolUnits: sdk.NewUint(1000), + nativeAssetBalance: sdk.NewUint(12327), + externalAssetBalance: sdk.NewUint(132233), + nativeAssetAmount: sdk.ZeroUint(), + externalAssetAmount: sdk.ZeroUint(), + expectedPoolUnits: sdk.NewUint(1000), + expectedLPunits: sdk.ZeroUint(), + expectedSwapStatus: clpkeeper.NoSwap, + }, + { + name: "positive symmetry", + oldPoolUnits: sdk.NewUint(7656454334323412), + nativeAssetBalance: sdk.NewUint(16767626535600), + externalAssetBalance: sdk.NewUint(2345454545400), + nativeAssetAmount: sdk.ZeroUint(), + externalAssetAmount: sdk.NewUint(4556664545), + expectedPoolUnits: sdk.NewUint(7663887695258361), + expectedLPunits: sdk.NewUint(7433360934949), + expectedSwapStatus: clpkeeper.BuyNative, + expectedSwapAmount: sdk.NewUint(2277340758), + }, + { + name: "symmetric", + oldPoolUnits: sdk.NewUint(7656454334323412), + nativeAssetBalance: sdk.NewUint(16767626535600), + externalAssetBalance: sdk.NewUint(2345454545400), + nativeAssetAmount: sdk.NewUint(167676265356), + externalAssetAmount: sdk.NewUint(23454545454), + expectedPoolUnits: sdk.NewUint(7733018877666646), + expectedLPunits: sdk.NewUint(76564543343234), + expectedSwapStatus: clpkeeper.NoSwap, + }, + { + name: "negative symmetry - zero external", + oldPoolUnits: sdk.NewUint(7656454334323412), + nativeAssetBalance: sdk.NewUint(16767626535600), + externalAssetBalance: sdk.NewUint(2345454545400), + nativeAssetAmount: sdk.NewUint(167676265356), + externalAssetAmount: sdk.ZeroUint(), + expectedPoolUnits: sdk.NewUint(7694639456903696), + expectedLPunits: sdk.NewUint(38185122580284), + expectedSwapStatus: clpkeeper.SellNative, + expectedSwapAmount: sdk.NewUint(83633781363), + }, + { + name: "positive symmetry - non zero external", + oldPoolUnits: sdk.NewUint(7656454334323412), + nativeAssetBalance: sdk.NewUint(16767626535600), + externalAssetBalance: sdk.NewUint(2345454545400), + nativeAssetAmount: sdk.NewUint(167676265356), + externalAssetAmount: sdk.NewUint(46798998888), + expectedPoolUnits: sdk.NewUint(7771026137435008), + expectedLPunits: sdk.NewUint(114571803111596), + expectedSwapStatus: clpkeeper.BuyNative, + expectedSwapAmount: sdk.NewUint(11528907497), + }, + { + name: "very big - positive symmetry", + oldPoolUnits: sdk.NewUintFromString("1606938044258990275541962092341162602522202993782792835301376"), //2**200 + nativeAssetBalance: sdk.NewUintFromString("1606938044258990275541962092341162602522202993782792835301376"), + externalAssetBalance: sdk.NewUintFromString("1606938044258990275541962092341162602522202993782792835301376"), + nativeAssetAmount: sdk.NewUint(0), + externalAssetAmount: sdk.NewUint(1099511627776), // 2**40 + expectedPoolUnits: sdk.NewUintFromString("1606938044258990275541962092341162602522202993783342563626098"), + expectedLPunits: sdk.NewUint(549728324722), + expectedSwapStatus: clpkeeper.BuyNative, + expectedSwapAmount: sdk.NewUint(549783303053), + }, + { + name: "very big - symmetric", + oldPoolUnits: sdk.NewUintFromString("1606938044258990275541962092341162602522202993782792835301376"), //2**200 + nativeAssetBalance: sdk.NewUintFromString("1606938044258990275541962092341162602522202993782792835301376"), + externalAssetBalance: sdk.NewUintFromString("1606938044258990275541962092341162602522202993782792835301376"), + nativeAssetAmount: sdk.NewUint(1099511627776), // 2**40 + externalAssetAmount: sdk.NewUint(1099511627776), + expectedPoolUnits: sdk.NewUintFromString("1606938044258990275541962092341162602522202993783892346929152"), + expectedLPunits: sdk.NewUint(1099511627776), + expectedSwapStatus: clpkeeper.NoSwap, + }, + { + name: "very big - negative symmetry", + oldPoolUnits: sdk.NewUintFromString("1606938044258990275541962092341162602522202993782792835301376"), //2**200 + nativeAssetBalance: sdk.NewUintFromString("1606938044258990275541962092341162602522202993782792835301376"), + externalAssetBalance: sdk.NewUintFromString("1606938044258990275541962092341162602522202993782792835301376"), + nativeAssetAmount: sdk.NewUint(1099511627776), // 2**40 + externalAssetAmount: sdk.ZeroUint(), + expectedPoolUnits: sdk.NewUintFromString("1606938044258990275541962092341162602522202993783342563626098"), + expectedLPunits: sdk.NewUint(549728324722), + expectedSwapStatus: clpkeeper.SellNative, + expectedSwapAmount: sdk.NewUint(549783303053), + }, + } + + sellNativeSwapFeeRate := sdk.NewDecWithPrec(1, 4) + buyNativeSwapFeeRate := sdk.NewDecWithPrec(1, 4) + pmtpCurrentRunningRate := sdk.ZeroDec() + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + + poolUnits, lpunits, swapStatus, swapAmount, err := clpkeeper.CalculatePoolUnits( + tc.oldPoolUnits, + tc.nativeAssetBalance, + tc.externalAssetBalance, + tc.nativeAssetAmount, + tc.externalAssetAmount, + sellNativeSwapFeeRate, + buyNativeSwapFeeRate, + pmtpCurrentRunningRate, + ) - rowanValue, err := clpkeeper.CalcRowanValue(&pool, tc.pmtpCurrentRunningRate, tc.rowanAmount) if tc.expectedError != nil { - require.EqualError(t, tc.expectedError, err.Error()) + require.EqualError(t, err, tc.expectedError.Error()) return } require.NoError(t, err) - require.Equal(t, tc.expectedValue.String(), rowanValue.String()) + + require.Equal(t, tc.expectedPoolUnits.String(), poolUnits.String()) // compare strings so that the expected amounts can be read from the failure message + require.Equal(t, tc.expectedLPunits.String(), lpunits.String()) + require.Equal(t, tc.expectedSwapStatus, swapStatus) + require.Equal(t, tc.expectedSwapAmount.String(), swapAmount.String()) + + }) + } +} + +func TestKeeper_CalculatePoolUnitsSymmetric(t *testing.T) { + testcases := []struct { + name string + X, x, P sdk.Uint + expectedPoolUnits sdk.Uint + expectedLPUnits sdk.Uint + }{ + { + name: "test 1", + X: sdk.NewUint(167676265356), + x: sdk.NewUint(5120000099), + P: sdk.NewUint(112323227872), + expectedPoolUnits: sdk.NewUint(115753021209), + expectedLPUnits: sdk.NewUint(3429793337), + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + poolUnits, lpUnits := clpkeeper.CalculatePoolUnitsSymmetric(tc.X, tc.x, tc.P) + + require.Equal(t, tc.expectedPoolUnits.String(), poolUnits.String()) + require.Equal(t, tc.expectedLPUnits.String(), lpUnits.String()) }) } } @@ -1345,13 +1389,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.OneDec(), - swapResult: sdk.NewUint(181), + swapResult: sdk.NewUint(180), liquidityFee: sdk.NewUint(0), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(817), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(818), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1419,7 +1463,7 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, NativeAssetBalance: sdk.NewUint(953), - ExternalAssetBalance: sdk.NewUint(1098), + ExternalAssetBalance: sdk.NewUint(1097), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1450,7 +1494,7 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), + NativeAssetBalance: sdk.NewUint(1097), ExternalAssetBalance: sdk.NewUint(908), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), @@ -1482,7 +1526,7 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), + NativeAssetBalance: sdk.NewUint(1097), ExternalAssetBalance: sdk.NewUint(899), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), @@ -1509,13 +1553,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("0.2"), - swapResult: sdk.NewUint(109), + swapResult: sdk.NewUint(108), liquidityFee: sdk.NewUint(0), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(889), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(890), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1541,13 +1585,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("0.3"), - swapResult: sdk.NewUint(118), + swapResult: sdk.NewUint(117), liquidityFee: sdk.NewUint(0), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(880), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(881), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1573,13 +1617,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("0.4"), - swapResult: sdk.NewUint(127), + swapResult: sdk.NewUint(126), liquidityFee: sdk.NewUint(0), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(871), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(872), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1605,13 +1649,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("0.5"), - swapResult: sdk.NewUint(136), + swapResult: sdk.NewUint(135), liquidityFee: sdk.NewUint(0), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(862), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(863), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1637,13 +1681,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("0.6"), - swapResult: sdk.NewUint(145), + swapResult: sdk.NewUint(144), liquidityFee: sdk.NewUint(0), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(853), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(854), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1669,13 +1713,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("0.7"), - swapResult: sdk.NewUint(154), + swapResult: sdk.NewUint(153), liquidityFee: sdk.NewUint(0), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(844), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(845), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1701,13 +1745,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("0.8"), - swapResult: sdk.NewUint(163), + swapResult: sdk.NewUint(162), liquidityFee: sdk.NewUint(0), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(835), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(836), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1733,13 +1777,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("0.9"), - swapResult: sdk.NewUint(172), + swapResult: sdk.NewUint(171), liquidityFee: sdk.NewUint(0), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(826), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(827), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1765,13 +1809,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("1.0"), - swapResult: sdk.NewUint(181), + swapResult: sdk.NewUint(180), liquidityFee: sdk.NewUint(0), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(817), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(818), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1797,13 +1841,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("2.0"), - swapResult: sdk.NewUint(272), + swapResult: sdk.NewUint(270), liquidityFee: sdk.NewUint(0), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(726), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(728), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1829,13 +1873,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("3.0"), - swapResult: sdk.NewUint(362), + swapResult: sdk.NewUint(359), liquidityFee: sdk.NewUint(1), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(636), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(639), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1861,13 +1905,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("4.0"), - swapResult: sdk.NewUint(453), + swapResult: sdk.NewUint(449), liquidityFee: sdk.NewUint(1), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(545), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(549), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1893,13 +1937,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("5.0"), - swapResult: sdk.NewUint(544), + swapResult: sdk.NewUint(539), liquidityFee: sdk.NewUint(1), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(454), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(459), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1925,13 +1969,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("6.0"), - swapResult: sdk.NewUint(635), + swapResult: sdk.NewUint(629), liquidityFee: sdk.NewUint(1), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(363), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(369), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1957,13 +2001,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("7.0"), - swapResult: sdk.NewUint(725), + swapResult: sdk.NewUint(718), liquidityFee: sdk.NewUint(2), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(273), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(280), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -1989,13 +2033,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("8.0"), - swapResult: sdk.NewUint(816), + swapResult: sdk.NewUint(808), liquidityFee: sdk.NewUint(2), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(182), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(190), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -2021,13 +2065,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("9.0"), - swapResult: sdk.NewUint(906), + swapResult: sdk.NewUint(898), liquidityFee: sdk.NewUint(2), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(92), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(100), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -2053,13 +2097,13 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { wBasis: sdk.NewInt(1000), asymmetry: sdk.NewInt(10000), pmtpCurrentRunningRate: sdk.MustNewDecFromStr("10.0"), - swapResult: sdk.NewUint(997), + swapResult: sdk.NewUint(988), liquidityFee: sdk.NewUint(2), priceImpact: sdk.ZeroUint(), expectedPool: types.Pool{ ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetBalance: sdk.NewUint(1098), - ExternalAssetBalance: sdk.NewUint(1), + NativeAssetBalance: sdk.NewUint(1097), + ExternalAssetBalance: sdk.NewUint(10), PoolUnits: sdk.NewUint(998), NativeCustody: sdk.ZeroUint(), ExternalCustody: sdk.ZeroUint(), @@ -2220,9 +2264,9 @@ func TestKeeper_SwapOneFromGenesis(t *testing.T) { to = types.Asset{Symbol: tc.poolAsset} } - swapFeeParams := types.SwapFeeParams{SwapFeeRate: sdk.NewDecWithPrec(3, 3)} + swapFeeRate := sdk.NewDecWithPrec(3, 3) - swapResult, liquidityFee, priceImpact, newPool, err := clpkeeper.SwapOne(from, swapAmount, to, pool, tc.pmtpCurrentRunningRate, swapFeeParams) + swapResult, liquidityFee, priceImpact, newPool, err := clpkeeper.SwapOne(from, swapAmount, to, pool, tc.pmtpCurrentRunningRate, swapFeeRate) if tc.errString != nil { require.EqualError(t, err, tc.errString.Error()) diff --git a/x/clp/keeper/executors.go b/x/clp/keeper/executors.go index 82b5992b72..e9aeb56468 100644 --- a/x/clp/keeper/executors.go +++ b/x/clp/keeper/executors.go @@ -252,7 +252,8 @@ func (k Keeper) ProcessRemoveLiquidityMsg(ctx sdk.Context, msg *types.MsgRemoveL poolOriginalEB := pool.ExternalAssetBalance poolOriginalNB := pool.NativeAssetBalance pmtpCurrentRunningRate := k.GetPmtpRateParams(ctx).PmtpCurrentRunningRate - swapFeeParams := k.GetSwapFeeParams(ctx) + externalSwapFeeRate := k.GetSwapFeeRate(ctx, *msg.ExternalAsset, false) + nativeSwapFeeRate := k.GetSwapFeeRate(ctx, types.GetSettlementAsset(), false) nativeAssetDepth, externalAssetDepth := pool.ExtractDebt(pool.NativeAssetBalance, pool.ExternalAssetBalance, false) @@ -261,7 +262,7 @@ func (k Keeper) ProcessRemoveLiquidityMsg(ctx sdk.Context, msg *types.MsgRemoveL nativeAssetDepth.String(), externalAssetDepth.String(), lp.LiquidityProviderUnits.String(), msg.WBasisPoints.String(), msg.Asymmetry) - extRowanValue := CalculateWithdrawalRowanValue(withdrawExternalAssetAmount, types.GetSettlementAsset(), pool, pmtpCurrentRunningRate, swapFeeParams) + extRowanValue := CalculateWithdrawalRowanValue(withdrawExternalAssetAmount, types.GetSettlementAsset(), pool, pmtpCurrentRunningRate, externalSwapFeeRate) withdrawExternalAssetAmountInt, ok := k.ParseToInt(withdrawExternalAssetAmount.String()) if !ok { @@ -283,7 +284,7 @@ func (k Keeper) ProcessRemoveLiquidityMsg(ctx sdk.Context, msg *types.MsgRemoveL } // Swapping between Native and External based on Asymmetry if msg.Asymmetry.IsPositive() { - swapResult, _, _, swappedPool, err := SwapOne(types.GetSettlementAsset(), swapAmount, *msg.ExternalAsset, pool, pmtpCurrentRunningRate, swapFeeParams) + swapResult, _, _, swappedPool, err := SwapOne(types.GetSettlementAsset(), swapAmount, *msg.ExternalAsset, pool, pmtpCurrentRunningRate, nativeSwapFeeRate) if err != nil { return sdk.ZeroInt(), sdk.ZeroInt(), sdk.ZeroUint(), sdkerrors.Wrap(types.ErrUnableToSwap, err.Error()) } @@ -304,7 +305,7 @@ func (k Keeper) ProcessRemoveLiquidityMsg(ctx sdk.Context, msg *types.MsgRemoveL pool = swappedPool } if msg.Asymmetry.IsNegative() { - swapResult, _, _, swappedPool, err := SwapOne(*msg.ExternalAsset, swapAmount, types.GetSettlementAsset(), pool, pmtpCurrentRunningRate, swapFeeParams) + swapResult, _, _, swappedPool, err := SwapOne(*msg.ExternalAsset, swapAmount, types.GetSettlementAsset(), pool, pmtpCurrentRunningRate, externalSwapFeeRate) if err != nil { return sdk.ZeroInt(), sdk.ZeroInt(), sdk.ZeroUint(), sdkerrors.Wrap(types.ErrUnableToSwap, err.Error()) } diff --git a/x/clp/keeper/grpc_query.go b/x/clp/keeper/grpc_query.go index 75b401f79c..3818f60a04 100755 --- a/x/clp/keeper/grpc_query.go +++ b/x/clp/keeper/grpc_query.go @@ -272,5 +272,5 @@ func (k Querier) GetSwapFeeParams(c context.Context, _ *types.SwapFeeParamsReq) ctx := sdk.UnwrapSDKContext(c) swapFeeParams := k.Keeper.GetSwapFeeParams(ctx) - return &types.SwapFeeParamsRes{SwapFeeRate: swapFeeParams.SwapFeeRate, TokenParams: swapFeeParams.TokenParams}, nil + return &types.SwapFeeParamsRes{DefaultSwapFeeRate: swapFeeParams.DefaultSwapFeeRate, TokenParams: swapFeeParams.TokenParams}, nil } diff --git a/x/clp/keeper/liquidityprotection.go b/x/clp/keeper/liquidityprotection.go new file mode 100644 index 0000000000..1a37a472ee --- /dev/null +++ b/x/clp/keeper/liquidityprotection.go @@ -0,0 +1,82 @@ +package keeper + +import ( + "errors" + + "github.com/Sifchain/sifnode/x/clp/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (k Keeper) SetLiquidityProtectionParams(ctx sdk.Context, params *types.LiquidityProtectionParams) { + store := ctx.KVStore(k.storeKey) + store.Set(types.LiquidityProtectionParamsPrefix, k.cdc.MustMarshal(params)) +} + +func (k Keeper) GetLiquidityProtectionParams(ctx sdk.Context) *types.LiquidityProtectionParams { + params := types.LiquidityProtectionParams{} + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.LiquidityProtectionParamsPrefix) + k.cdc.MustUnmarshal(bz, ¶ms) + return ¶ms +} + +// This method should only be called if buying or selling native asset. +// If sellNative is false then this method assumes that buyNative is true. +// The nativePrice should be in MaxRowanLiquidityThresholdAsset +// NOTE: this method panics if sellNative is true and the value of the sell amount +// is greater than the value of currentRowanLiquidityThreshold. Call IsBlockedByLiquidityProtection +// before if unsure. +func (k Keeper) MustUpdateLiquidityProtectionThreshold(ctx sdk.Context, sellNative bool, nativeAmount sdk.Uint, nativePrice sdk.Dec) { + liquidityProtectionParams := k.GetLiquidityProtectionParams(ctx) + maxRowanLiquidityThreshold := liquidityProtectionParams.MaxRowanLiquidityThreshold + currentRowanLiquidityThreshold := k.GetLiquidityProtectionRateParams(ctx).CurrentRowanLiquidityThreshold + + if liquidityProtectionParams.IsActive { + nativeValue := CalcRowanValue(nativeAmount, nativePrice) + + var updatedRowanLiquidityThreshold sdk.Uint + if sellNative { + if currentRowanLiquidityThreshold.LT(nativeValue) { + panic(errors.New("expect sell native value to be less than currentRowanLiquidityThreshold")) + } else { + updatedRowanLiquidityThreshold = currentRowanLiquidityThreshold.Sub(nativeValue) + } + } else { + // This is equivalent to currentRowanLiquidityThreshold := sdk.MinUint(currentRowanLiquidityThreshold.Add(nativeValue), maxRowanLiquidityThreshold) + // except it prevents any overflows when adding the nativeValue + // Assume that maxRowanLiquidityThreshold >= currentRowanLiquidityThreshold + if maxRowanLiquidityThreshold.Sub(currentRowanLiquidityThreshold).LT(nativeValue) { + updatedRowanLiquidityThreshold = maxRowanLiquidityThreshold + } else { + updatedRowanLiquidityThreshold = currentRowanLiquidityThreshold.Add(nativeValue) + } + } + + k.SetLiquidityProtectionCurrentRowanLiquidityThreshold(ctx, updatedRowanLiquidityThreshold) + } +} + +// Currently this calculates the native price on the fly +// Calculates the price of the native token in MaxRowanLiquidityThresholdAsset +func (k Keeper) GetNativePrice(ctx sdk.Context) (sdk.Dec, error) { + liquidityProtectionParams := k.GetLiquidityProtectionParams(ctx) + maxRowanLiquidityThresholdAsset := liquidityProtectionParams.MaxRowanLiquidityThresholdAsset + + if types.StringCompare(maxRowanLiquidityThresholdAsset, types.NativeSymbol) { + return sdk.OneDec(), nil + } + pool, err := k.GetPool(ctx, maxRowanLiquidityThresholdAsset) + if err != nil { + return sdk.Dec{}, types.ErrMaxRowanLiquidityThresholdAssetPoolDoesNotExist + } + + return CalcRowanSpotPrice(&pool, k.GetPmtpRateParams(ctx).PmtpCurrentRunningRate) + +} + +// The nativePrice should be in MaxRowanLiquidityThresholdAsset +func (k Keeper) IsBlockedByLiquidityProtection(ctx sdk.Context, nativeAmount sdk.Uint, nativePrice sdk.Dec) bool { + value := CalcRowanValue(nativeAmount, nativePrice) + currentRowanLiquidityThreshold := k.GetLiquidityProtectionRateParams(ctx).CurrentRowanLiquidityThreshold + return currentRowanLiquidityThreshold.LT(value) +} diff --git a/x/clp/keeper/liquidityprotection_params.go b/x/clp/keeper/liquidityprotection_params.go deleted file mode 100644 index 16f816f0ed..0000000000 --- a/x/clp/keeper/liquidityprotection_params.go +++ /dev/null @@ -1,19 +0,0 @@ -package keeper - -import ( - "github.com/Sifchain/sifnode/x/clp/types" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func (k Keeper) SetLiquidityProtectionParams(ctx sdk.Context, params *types.LiquidityProtectionParams) { - store := ctx.KVStore(k.storeKey) - store.Set(types.LiquidityProtectionParamsPrefix, k.cdc.MustMarshal(params)) -} - -func (k Keeper) GetLiquidityProtectionParams(ctx sdk.Context) *types.LiquidityProtectionParams { - params := types.LiquidityProtectionParams{} - store := ctx.KVStore(k.storeKey) - bz := store.Get(types.LiquidityProtectionParamsPrefix) - k.cdc.MustUnmarshal(bz, ¶ms) - return ¶ms -} diff --git a/x/clp/keeper/liquidityprotection_test.go b/x/clp/keeper/liquidityprotection_test.go new file mode 100644 index 0000000000..f31277c42e --- /dev/null +++ b/x/clp/keeper/liquidityprotection_test.go @@ -0,0 +1,216 @@ +package keeper_test + +import ( + "testing" + + sifapp "github.com/Sifchain/sifnode/app" + "github.com/Sifchain/sifnode/x/clp/test" + "github.com/Sifchain/sifnode/x/clp/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +func TestKeeper_GetNativePrice(t *testing.T) { + testcases := []struct { + name string + pricingAsset string + createPool bool + poolNativeAssetBalance sdk.Uint + poolExternalAssetBalance sdk.Uint + pmtpCurrentRunningRate sdk.Dec + expectedPrice sdk.Dec + expectedError error + }{ + { + name: "success", + pricingAsset: types.NativeSymbol, + expectedPrice: sdk.NewDec(1), + }, + { + name: "fail pool does not exist", + pricingAsset: "usdc", + expectedError: types.ErrMaxRowanLiquidityThresholdAssetPoolDoesNotExist, + }, + { + name: "success non rowan pricing asset", + pricingAsset: "usdc", + createPool: true, + poolNativeAssetBalance: sdk.NewUint(100000), + poolExternalAssetBalance: sdk.NewUint(1000), + pmtpCurrentRunningRate: sdk.OneDec(), + expectedPrice: sdk.MustNewDecFromStr("0.02"), + }, + } + + for _, tc := range testcases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + ctx, app := test.CreateTestAppClpFromGenesis(false, func(app *sifapp.SifchainApp, genesisState sifapp.GenesisState) sifapp.GenesisState { + + if tc.createPool { + pools := []*types.Pool{ + { + ExternalAsset: &types.Asset{Symbol: tc.pricingAsset}, + NativeAssetBalance: tc.poolNativeAssetBalance, + ExternalAssetBalance: tc.poolExternalAssetBalance, + }, + } + clpGs := types.DefaultGenesisState() + + clpGs.Params = types.Params{ + MinCreatePoolThreshold: 1, + } + clpGs.PoolList = append(clpGs.PoolList, pools...) + bz, _ := app.AppCodec().MarshalJSON(clpGs) + genesisState["clp"] = bz + } + + return genesisState + }) + + liquidityProtectionParams := app.ClpKeeper.GetLiquidityProtectionParams(ctx) + liquidityProtectionParams.MaxRowanLiquidityThresholdAsset = tc.pricingAsset + app.ClpKeeper.SetLiquidityProtectionParams(ctx, liquidityProtectionParams) + app.ClpKeeper.SetPmtpCurrentRunningRate(ctx, tc.pmtpCurrentRunningRate) + + price, err := app.ClpKeeper.GetNativePrice(ctx) + + if tc.expectedError != nil { + require.EqualError(t, err, tc.expectedError.Error()) + return + } + require.NoError(t, err) + require.Equal(t, tc.expectedPrice.String(), price.String()) // compare strings so that the expected amounts can be read from the failure message + }) + } +} + +func TestKeeper_IsBlockedByLiquidityProtection(t *testing.T) { + testcases := []struct { + name string + currentRowanLiquidityThreshold sdk.Uint + nativeAmount sdk.Uint + nativePrice sdk.Dec + expectedIsBlocked bool + }{ + { + name: "not blocked", + currentRowanLiquidityThreshold: sdk.NewUint(180), + nativeAmount: sdk.NewUint(900), + nativePrice: sdk.MustNewDecFromStr("0.2"), + expectedIsBlocked: false, + }, + { + name: "blocked", + currentRowanLiquidityThreshold: sdk.NewUint(179), + nativeAmount: sdk.NewUint(900), + nativePrice: sdk.MustNewDecFromStr("0.2"), + expectedIsBlocked: true, + }, + } + + for _, tc := range testcases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + app, ctx := test.CreateTestApp(false) + + liquidityProtectionRateParams := app.ClpKeeper.GetLiquidityProtectionRateParams(ctx) + liquidityProtectionRateParams.CurrentRowanLiquidityThreshold = tc.currentRowanLiquidityThreshold + app.ClpKeeper.SetLiquidityProtectionRateParams(ctx, liquidityProtectionRateParams) + + isBlocked := app.ClpKeeper.IsBlockedByLiquidityProtection(ctx, tc.nativeAmount, tc.nativePrice) + + require.Equal(t, tc.expectedIsBlocked, isBlocked) + }) + } +} + +func TestKeeper_MustUpdateLiquidityProtectionThreshold(t *testing.T) { + testcases := []struct { + name string + maxRowanLiquidityThreshold sdk.Uint + currentRowanLiquidityThreshold sdk.Uint + isActive bool + nativeAmount sdk.Uint + nativePrice sdk.Dec + sellNative bool + expectedUpdatedThreshold sdk.Uint + expectedPanicError string + }{ + { + name: "sell native", + maxRowanLiquidityThreshold: sdk.NewUint(100000000), + currentRowanLiquidityThreshold: sdk.NewUint(180), + isActive: true, + nativeAmount: sdk.NewUint(900), + nativePrice: sdk.MustNewDecFromStr("0.2"), + sellNative: true, + expectedUpdatedThreshold: sdk.ZeroUint(), + }, + { + name: "buy native", + maxRowanLiquidityThreshold: sdk.NewUint(100000000), + currentRowanLiquidityThreshold: sdk.NewUint(180), + isActive: true, + nativeAmount: sdk.NewUint(900), + nativePrice: sdk.MustNewDecFromStr("0.2"), + sellNative: false, + expectedUpdatedThreshold: sdk.NewUint(360), + }, + { + name: "liquidity protection disabled", + maxRowanLiquidityThreshold: sdk.NewUint(100000000), + currentRowanLiquidityThreshold: sdk.NewUint(180), + isActive: false, + expectedUpdatedThreshold: sdk.NewUint(180), + }, + { + name: "panics if sell native value greater than current threshold", + currentRowanLiquidityThreshold: sdk.NewUint(180), + isActive: true, + nativeAmount: sdk.NewUint(900), + nativePrice: sdk.MustNewDecFromStr("1"), + sellNative: true, + expectedPanicError: "expect sell native value to be less than currentRowanLiquidityThreshold", + }, + { + name: "does not exceed max", + maxRowanLiquidityThreshold: sdk.NewUint(150), + currentRowanLiquidityThreshold: sdk.NewUint(100), + isActive: true, + nativeAmount: sdk.NewUint(80), + nativePrice: sdk.MustNewDecFromStr("1"), + sellNative: false, + expectedUpdatedThreshold: sdk.NewUint(150), + }, + } + + for _, tc := range testcases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + app, ctx := test.CreateTestApp(false) + + liquidityProtectionParams := app.ClpKeeper.GetLiquidityProtectionParams(ctx) + liquidityProtectionParams.IsActive = tc.isActive + liquidityProtectionParams.MaxRowanLiquidityThreshold = tc.maxRowanLiquidityThreshold + app.ClpKeeper.SetLiquidityProtectionParams(ctx, liquidityProtectionParams) + + liquidityProtectionRateParams := app.ClpKeeper.GetLiquidityProtectionRateParams(ctx) + liquidityProtectionRateParams.CurrentRowanLiquidityThreshold = tc.currentRowanLiquidityThreshold + app.ClpKeeper.SetLiquidityProtectionRateParams(ctx, liquidityProtectionRateParams) + + if tc.expectedPanicError != "" { + require.PanicsWithError(t, tc.expectedPanicError, func() { + app.ClpKeeper.MustUpdateLiquidityProtectionThreshold(ctx, tc.sellNative, tc.nativeAmount, tc.nativePrice) + }) + return + } + + app.ClpKeeper.MustUpdateLiquidityProtectionThreshold(ctx, tc.sellNative, tc.nativeAmount, tc.nativePrice) + + liquidityProtectionRateParams = app.ClpKeeper.GetLiquidityProtectionRateParams(ctx) + + require.Equal(t, tc.expectedUpdatedThreshold.String(), liquidityProtectionRateParams.CurrentRowanLiquidityThreshold.String()) + }) + } +} diff --git a/x/clp/keeper/msg_server.go b/x/clp/keeper/msg_server.go index fdd1eec44f..f88515f391 100644 --- a/x/clp/keeper/msg_server.go +++ b/x/clp/keeper/msg_server.go @@ -407,15 +407,12 @@ func (k msgServer) CreatePool(goCtx context.Context, msg *types.MsgCreatePool) ( return nil, types.ErrUnableToCreatePool } - nativeBalance := msg.NativeAssetAmount - externalBalance := msg.ExternalAssetAmount - externalDecimals, err := Int64ToUint8Safe(eAsset.Decimals) - if err != nil { - return nil, err - } + pmtpCurrentRunningRate := k.GetPmtpRateParams(ctx).PmtpCurrentRunningRate + sellNativeSwapFeeRate := k.GetSwapFeeRate(ctx, types.GetSettlementAsset(), false) + buyNativeSwapFeeRate := k.GetSwapFeeRate(ctx, *msg.ExternalAsset, false) - poolUnits, lpunits, err := CalculatePoolUnits(sdk.ZeroUint(), sdk.ZeroUint(), sdk.ZeroUint(), - nativeBalance, externalBalance, externalDecimals, k.GetSymmetryThreshold(ctx), k.GetSymmetryRatio(ctx)) + poolUnits, lpunits, _, _, err := CalculatePoolUnits(sdk.ZeroUint(), sdk.ZeroUint(), sdk.ZeroUint(), + msg.NativeAssetAmount, msg.ExternalAssetAmount, sellNativeSwapFeeRate, buyNativeSwapFeeRate, pmtpCurrentRunningRate) if err != nil { return nil, sdkerrors.Wrap(types.ErrUnableToCreatePool, err.Error()) } @@ -482,35 +479,21 @@ func (k msgServer) Swap(goCtx context.Context, msg *types.MsgSwap) (*types.MsgSw } pmtpCurrentRunningRate := k.GetPmtpRateParams(ctx).PmtpCurrentRunningRate - swapFeeParams := k.GetSwapFeeParams(ctx) - - liquidityProtectionParams := k.GetLiquidityProtectionParams(ctx) - maxRowanLiquidityThreshold := liquidityProtectionParams.MaxRowanLiquidityThreshold - maxRowanLiquidityThresholdAsset := liquidityProtectionParams.MaxRowanLiquidityThresholdAsset - currentRowanLiquidityThreshold := k.GetLiquidityProtectionRateParams(ctx).CurrentRowanLiquidityThreshold - var ( - sentValue sdk.Uint - ) + swapFeeRate := k.GetSwapFeeRate(ctx, *msg.SentAsset, false) - // if liquidity protection is active and selling rowan - if liquidityProtectionParams.IsActive && types.StringCompare(sAsset.Denom, types.NativeSymbol) { - if types.StringCompare(maxRowanLiquidityThresholdAsset, types.NativeSymbol) { - sentValue = msg.SentAmount - } else { - pool, err := k.GetPool(ctx, maxRowanLiquidityThresholdAsset) - if err != nil { - return nil, types.ErrMaxRowanLiquidityThresholdAssetPoolDoesNotExist - } - - sentValue, err = CalcRowanValue(&pool, pmtpCurrentRunningRate, msg.SentAmount) - - if err != nil { - return nil, err - } + var price sdk.Dec + if k.GetLiquidityProtectionParams(ctx).IsActive { + // we'll need the price later as well - calculate it before any + // changes are made to the pool which could change the price + price, err = k.GetNativePrice(ctx) + if err != nil { + return nil, err } - if currentRowanLiquidityThreshold.LT(sentValue) { - return nil, types.ErrReachedMaxRowanLiquidityThreshold + if types.StringCompare(sAsset.Denom, types.NativeSymbol) { + if k.IsBlockedByLiquidityProtection(ctx, msg.SentAmount, price) { + return nil, types.ErrReachedMaxRowanLiquidityThreshold + } } } @@ -547,7 +530,7 @@ func (k msgServer) Swap(goCtx context.Context, msg *types.MsgSwap) (*types.MsgSw // Check if its a two way swap, swapping non native fro non native . // If its one way we can skip this if condition and add balance to users account from outpool if !msg.SentAsset.Equals(nativeAsset) && !msg.ReceivedAsset.Equals(nativeAsset) { - emitAmount, lp, ts, finalPool, err := SwapOne(*sentAsset, sentAmount, nativeAsset, inPool, pmtpCurrentRunningRate, swapFeeParams) + emitAmount, lp, ts, finalPool, err := SwapOne(*sentAsset, sentAmount, nativeAsset, inPool, pmtpCurrentRunningRate, swapFeeRate) if err != nil { return nil, err } @@ -573,7 +556,7 @@ func (k msgServer) Swap(goCtx context.Context, msg *types.MsgSwap) (*types.MsgSw } } // Calculating amount user receives - emitAmount, lp, ts, finalPool, err := SwapOne(*sentAsset, sentAmount, *receivedAsset, outPool, pmtpCurrentRunningRate, swapFeeParams) + emitAmount, lp, ts, finalPool, err := SwapOne(*sentAsset, sentAmount, *receivedAsset, outPool, pmtpCurrentRunningRate, swapFeeRate) if err != nil { return nil, err } @@ -602,7 +585,7 @@ func (k msgServer) Swap(goCtx context.Context, msg *types.MsgSwap) (*types.MsgSw } if liquidityFeeNative.GT(sdk.ZeroUint()) { liquidityFeeExternal = liquidityFeeExternal.Add(lp) - firstSwapFeeInOutputAsset := GetSwapFee(liquidityFeeNative, *msg.ReceivedAsset, outPool, pmtpCurrentRunningRate, swapFeeParams) + firstSwapFeeInOutputAsset := GetSwapFee(liquidityFeeNative, *msg.ReceivedAsset, outPool, pmtpCurrentRunningRate, swapFeeRate) totalLiquidityFee = liquidityFeeExternal.Add(firstSwapFeeInOutputAsset) } else { totalLiquidityFee = liquidityFeeNative.Add(lp) @@ -627,42 +610,16 @@ func (k msgServer) Swap(goCtx context.Context, msg *types.MsgSwap) (*types.MsgSw ), }) - if liquidityProtectionParams.IsActive { - // if sell rowan + if k.GetLiquidityProtectionParams(ctx).IsActive { if types.StringCompare(sAsset.Denom, types.NativeSymbol) { - // we know that sentValue < currentRowanLiquidityThreshold so we can do the - // substitution knowing it won't panic - currentRowanLiquidityThreshold = currentRowanLiquidityThreshold.Sub(sentValue) - k.SetLiquidityProtectionCurrentRowanLiquidityThreshold(ctx, currentRowanLiquidityThreshold) + // selling rowan + discountedSentAmount := CalculateDiscountedSentAmount(msg.SentAmount, swapFeeRate) + k.MustUpdateLiquidityProtectionThreshold(ctx, true, discountedSentAmount, price) } - // if buy rowan if types.StringCompare(rAsset.Denom, types.NativeSymbol) { - var emitValue sdk.Uint - if types.StringCompare(maxRowanLiquidityThresholdAsset, types.NativeSymbol) { - emitValue = emitAmount - } else { - pool, err := k.GetPool(ctx, maxRowanLiquidityThresholdAsset) - if err != nil { - return nil, types.ErrMaxRowanLiquidityThresholdAssetPoolDoesNotExist - } - - emitValue, err = CalcRowanValue(&pool, pmtpCurrentRunningRate, emitAmount) - - if err != nil { - return nil, err - } - } - - // This is equivalent to currentRowanLiquidityThreshold := sdk.MinUint(currentRowanLiquidityThreshold.Add(emitValue), maxRowanLiquidityThreshold) - // except it prevents any overflows when adding the emitValue - if maxRowanLiquidityThreshold.Sub(currentRowanLiquidityThreshold).LT(emitValue) { - currentRowanLiquidityThreshold = maxRowanLiquidityThreshold - } else { - currentRowanLiquidityThreshold = currentRowanLiquidityThreshold.Add(emitValue) - } - - k.SetLiquidityProtectionCurrentRowanLiquidityThreshold(ctx, currentRowanLiquidityThreshold) + // buying rowan + k.MustUpdateLiquidityProtectionThreshold(ctx, false, emitAmount, price) } } @@ -684,38 +641,95 @@ func (k msgServer) Swap(goCtx context.Context, msg *types.MsgSwap) (*types.MsgSw func (k msgServer) AddLiquidity(goCtx context.Context, msg *types.MsgAddLiquidity) (*types.MsgAddLiquidityResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + + nAsset, err := k.tokenRegistryKeeper.GetRegistryEntry(ctx, types.NativeSymbol) + if err != nil { + return nil, types.ErrTokenNotSupported + } + eAsset, err := k.tokenRegistryKeeper.GetRegistryEntry(ctx, msg.ExternalAsset.Symbol) if err != nil { return nil, types.ErrTokenNotSupported } + if !k.tokenRegistryKeeper.CheckEntryPermissions(eAsset, []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}) { return nil, tokenregistrytypes.ErrPermissionDenied } - // Get pool + pool, err := k.Keeper.GetPool(ctx, msg.ExternalAsset.Symbol) if err != nil { return nil, types.ErrPoolDoesNotExist } - externalDecimals, err := Int64ToUint8Safe(eAsset.Decimals) - if err != nil { - return nil, err - } + pmtpCurrentRunningRate := k.GetPmtpRateParams(ctx).PmtpCurrentRunningRate + sellNativeSwapFeeRate := k.GetSwapFeeRate(ctx, types.GetSettlementAsset(), false) + buyNativeSwapFeeRate := k.GetSwapFeeRate(ctx, *msg.ExternalAsset, false) nativeAssetDepth, externalAssetDepth := pool.ExtractDebt(pool.NativeAssetBalance, pool.ExternalAssetBalance, false) - newPoolUnits, lpUnits, err := CalculatePoolUnits( + newPoolUnits, lpUnits, swapStatus, swapAmount, err := CalculatePoolUnits( pool.PoolUnits, nativeAssetDepth, externalAssetDepth, msg.NativeAssetAmount, msg.ExternalAssetAmount, - externalDecimals, - k.GetSymmetryThreshold(ctx), - k.GetSymmetryRatio(ctx)) + sellNativeSwapFeeRate, + buyNativeSwapFeeRate, + pmtpCurrentRunningRate) if err != nil { return nil, err } + + switch swapStatus { + case NoSwap: + // do nothing + case SellNative: + // check sell permission for native + if k.tokenRegistryKeeper.CheckEntryPermissions(nAsset, []tokenregistrytypes.Permission{tokenregistrytypes.Permission_DISABLE_SELL}) { + return nil, tokenregistrytypes.ErrNotAllowedToSellAsset + } + // check buy permission for external + if k.tokenRegistryKeeper.CheckEntryPermissions(eAsset, []tokenregistrytypes.Permission{tokenregistrytypes.Permission_DISABLE_BUY}) { + return nil, tokenregistrytypes.ErrNotAllowedToBuyAsset + } + + if k.GetLiquidityProtectionParams(ctx).IsActive { + price, err := k.GetNativePrice(ctx) + if err != nil { + return nil, err + } + + if k.IsBlockedByLiquidityProtection(ctx, swapAmount, price) { + return nil, types.ErrReachedMaxRowanLiquidityThreshold + } + + discountedSentAmount := CalculateDiscountedSentAmount(swapAmount, sellNativeSwapFeeRate) + k.MustUpdateLiquidityProtectionThreshold(ctx, true, discountedSentAmount, price) + } + + case BuyNative: + // check sell permission for external + if k.tokenRegistryKeeper.CheckEntryPermissions(eAsset, []tokenregistrytypes.Permission{tokenregistrytypes.Permission_DISABLE_SELL}) { + return nil, tokenregistrytypes.ErrNotAllowedToSellAsset + } + // check buy permission for native + if k.tokenRegistryKeeper.CheckEntryPermissions(nAsset, []tokenregistrytypes.Permission{tokenregistrytypes.Permission_DISABLE_BUY}) { + return nil, tokenregistrytypes.ErrNotAllowedToBuyAsset + } + + if k.GetLiquidityProtectionParams(ctx).IsActive { + nativeAmount, _ := CalcSwapResult(true, externalAssetDepth, swapAmount, nativeAssetDepth, pmtpCurrentRunningRate, buyNativeSwapFeeRate) + price, err := k.GetNativePrice(ctx) + if err != nil { + return nil, err + } + + k.MustUpdateLiquidityProtectionThreshold(ctx, false, nativeAmount, price) + } + default: + panic("expect not to reach here!") + } + // Get lp , if lp doesnt exist create lp lp, err := k.Keeper.AddLiquidity(ctx, msg, pool, newPoolUnits, lpUnits) if err != nil { @@ -777,7 +791,7 @@ func (k msgServer) RemoveLiquidityUnits(goCtx context.Context, msg *types.MsgRem } pmtpCurrentRunningRate := k.GetPmtpRateParams(ctx).PmtpCurrentRunningRate - swapFeeParams := k.GetSwapFeeParams(ctx) + swapFeeRate := k.GetSwapFeeRate(ctx, *msg.ExternalAsset, false) // Prune pools params := k.GetRewardsParams(ctx) k.PruneUnlockRecords(ctx, &lp, params.LiquidityRemovalLockPeriod, params.LiquidityRemovalCancelPeriod) @@ -796,7 +810,7 @@ func (k msgServer) RemoveLiquidityUnits(goCtx context.Context, msg *types.MsgRem // Skip pools that are not margin enabled, to avoid health being zero and queueing being triggered. if k.GetMarginKeeper().IsPoolEnabled(ctx, eAsset.Denom) { - extRowanValue := CalculateWithdrawalRowanValue(withdrawExternalAssetAmount, types.GetSettlementAsset(), pool, pmtpCurrentRunningRate, swapFeeParams) + extRowanValue := CalculateWithdrawalRowanValue(withdrawExternalAssetAmount, types.GetSettlementAsset(), pool, pmtpCurrentRunningRate, swapFeeRate) futurePool := pool futurePool.NativeAssetBalance = futurePool.NativeAssetBalance.Sub(withdrawNativeAssetAmount) @@ -880,7 +894,8 @@ func (k msgServer) RemoveLiquidity(goCtx context.Context, msg *types.MsgRemoveLi } pmtpCurrentRunningRate := k.GetPmtpRateParams(ctx).PmtpCurrentRunningRate - swapFeeParams := k.GetSwapFeeParams(ctx) + externalSwapFeeRate := k.GetSwapFeeRate(ctx, *msg.ExternalAsset, false) + nativeSwapFeeRate := k.GetSwapFeeRate(ctx, types.GetSettlementAsset(), false) // Prune pools params := k.GetRewardsParams(ctx) k.PruneUnlockRecords(ctx, &lp, params.LiquidityRemovalLockPeriod, params.LiquidityRemovalCancelPeriod) @@ -910,7 +925,7 @@ func (k msgServer) RemoveLiquidity(goCtx context.Context, msg *types.MsgRemoveLi // Skip pools that are not margin enabled, to avoid health being zero and queueing being triggered. if k.GetMarginKeeper().IsPoolEnabled(ctx, eAsset.Denom) { - extRowanValue := CalculateWithdrawalRowanValue(withdrawExternalAssetAmount, types.GetSettlementAsset(), pool, pmtpCurrentRunningRate, swapFeeParams) + extRowanValue := CalculateWithdrawalRowanValue(withdrawExternalAssetAmount, types.GetSettlementAsset(), pool, pmtpCurrentRunningRate, externalSwapFeeRate) futurePool := pool futurePool.NativeAssetBalance = futurePool.NativeAssetBalance.Sub(withdrawNativeAssetAmount) @@ -945,7 +960,7 @@ func (k msgServer) RemoveLiquidity(goCtx context.Context, msg *types.MsgRemoveLi } // Swapping between Native and External based on Asymmetry if msg.Asymmetry.IsPositive() { - swapResult, _, _, swappedPool, err := SwapOne(types.GetSettlementAsset(), swapAmount, *msg.ExternalAsset, pool, pmtpCurrentRunningRate, swapFeeParams) + swapResult, _, _, swappedPool, err := SwapOne(types.GetSettlementAsset(), swapAmount, *msg.ExternalAsset, pool, pmtpCurrentRunningRate, nativeSwapFeeRate) if err != nil { return nil, sdkerrors.Wrap(types.ErrUnableToSwap, err.Error()) @@ -967,7 +982,7 @@ func (k msgServer) RemoveLiquidity(goCtx context.Context, msg *types.MsgRemoveLi pool = swappedPool } if msg.Asymmetry.IsNegative() { - swapResult, _, _, swappedPool, err := SwapOne(*msg.ExternalAsset, swapAmount, types.GetSettlementAsset(), pool, pmtpCurrentRunningRate, swapFeeParams) + swapResult, _, _, swappedPool, err := SwapOne(*msg.ExternalAsset, swapAmount, types.GetSettlementAsset(), pool, pmtpCurrentRunningRate, externalSwapFeeRate) if err != nil { return nil, sdkerrors.Wrap(types.ErrUnableToSwap, err.Error()) @@ -1092,7 +1107,7 @@ func (k msgServer) UpdateSwapFeeParams(goCtx context.Context, msg *types.MsgUpda return response, errors.Wrap(types.ErrNotEnoughPermissions, fmt.Sprintf("Sending Account : %s", msg.Signer)) } - k.SetSwapFeeParams(ctx, &types.SwapFeeParams{SwapFeeRate: msg.SwapFeeRate, TokenParams: msg.TokenParams}) + k.SetSwapFeeParams(ctx, &types.SwapFeeParams{DefaultSwapFeeRate: msg.DefaultSwapFeeRate, TokenParams: msg.TokenParams}) return response, nil } diff --git a/x/clp/keeper/msg_server_test.go b/x/clp/keeper/msg_server_test.go index 3af70772b4..7d0c59fa24 100644 --- a/x/clp/keeper/msg_server_test.go +++ b/x/clp/keeper/msg_server_test.go @@ -228,6 +228,7 @@ func TestMsgServer_Swap(t *testing.T) { decimals int64 address string maxRowanLiquidityThresholdAsset string + swapFeeParams types.SwapFeeParams nativeBalance sdk.Int externalBalance sdk.Int nativeAssetAmount sdk.Uint @@ -422,7 +423,7 @@ func TestMsgServer_Swap(t *testing.T) { nativeAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, currentRowanLiquidityThreshold: sdk.NewUint(1000), maxRowanLiquidityThresholdAsset: "eth", - externalBalanceEnd: sdk.NewInt(9749), + externalBalanceEnd: sdk.NewInt(10500), nativeBalanceEnd: sdk.NewInt(9749), expectedRunningThresholdEnd: sdk.NewUint(498), msg: &types.MsgSwap{ @@ -449,7 +450,7 @@ func TestMsgServer_Swap(t *testing.T) { nativeAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, currentRowanLiquidityThreshold: sdk.NewUint(4000), expectedRunningThresholdEnd: sdk.NewUint(3500), - externalBalanceEnd: sdk.NewInt(9750), + externalBalanceEnd: sdk.NewInt(10498), nativeBalanceEnd: sdk.NewInt(9750), maxRowanLiquidityThresholdAsset: "eth", msg: &types.MsgSwap{ @@ -602,6 +603,98 @@ func TestMsgServer_Swap(t *testing.T) { MinReceivingAmount: sdk.NewUint(0), }, }, + { + name: "eth:rowan - swap fee 5%", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "eth", + decimals: 18, + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + nativeBalance: sdk.NewInt(10000), + externalBalance: sdk.NewInt(10000), + nativeAssetAmount: sdk.NewUint(1000), + externalAssetAmount: sdk.NewUint(1000), + poolUnits: sdk.NewUint(1000), + nativeBalanceEnd: sdk.NewInt(10086), + externalBalanceEnd: sdk.NewInt(9900), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + nativeAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + currentRowanLiquidityThreshold: sdk.NewUint(1000), + expectedRunningThresholdEnd: sdk.NewUint(1086), + maxRowanLiquidityThresholdAsset: "rowan", + maxRowanLiquidityThreshold: sdk.NewUint(2000), + swapFeeParams: types.SwapFeeParams{ + DefaultSwapFeeRate: sdk.NewDecWithPrec(3, 3), + TokenParams: []*types.SwapFeeTokenParams{ + { + Asset: "rowan", + SwapFeeRate: sdk.NewDecWithPrec(1, 1), //10% + }, + { + Asset: "eth", + SwapFeeRate: sdk.NewDecWithPrec(5, 2), //5% + }, + { + Asset: "usdc", + SwapFeeRate: sdk.NewDecWithPrec(2, 2), //2% + }, + }, + }, + msg: &types.MsgSwap{ + Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + SentAsset: &types.Asset{Symbol: "eth"}, + ReceivedAsset: &types.Asset{Symbol: "rowan"}, + SentAmount: sdk.NewUint(100), + MinReceivingAmount: sdk.NewUint(1), + }, + }, + { + name: "rowan:eth - swap fee 10%", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "eth", + decimals: 18, + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + nativeBalance: sdk.NewInt(10000), + externalBalance: sdk.NewInt(10000), + nativeAssetAmount: sdk.NewUint(1000), + externalAssetAmount: sdk.NewUint(1000), + poolUnits: sdk.NewUint(1000), + nativeBalanceEnd: sdk.NewInt(9900), + externalBalanceEnd: sdk.NewInt(10081), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + nativeAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + currentRowanLiquidityThreshold: sdk.NewUint(1000), + expectedRunningThresholdEnd: sdk.NewUint(910), + maxRowanLiquidityThresholdAsset: "rowan", + maxRowanLiquidityThreshold: sdk.NewUint(2000), + swapFeeParams: types.SwapFeeParams{ + DefaultSwapFeeRate: sdk.NewDecWithPrec(3, 3), + TokenParams: []*types.SwapFeeTokenParams{ + { + Asset: "rowan", + SwapFeeRate: sdk.NewDecWithPrec(1, 1), //10% + }, + { + Asset: "eth", + SwapFeeRate: sdk.NewDecWithPrec(5, 2), //5% + }, + { + Asset: "usdc", + SwapFeeRate: sdk.NewDecWithPrec(2, 2), //2% + }, + }, + }, + msg: &types.MsgSwap{ + Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + SentAsset: &types.Asset{Symbol: "rowan"}, + ReceivedAsset: &types.Asset{Symbol: "eth"}, + SentAmount: sdk.NewUint(100), + MinReceivingAmount: sdk.NewUint(1), + }, + }, } for _, tc := range testcases { @@ -681,6 +774,7 @@ func TestMsgServer_Swap(t *testing.T) { }) app.ClpKeeper.SetPmtpCurrentRunningRate(ctx, sdk.NewDec(0)) + app.ClpKeeper.SetSwapFeeParams(ctx, &tc.swapFeeParams) liquidityProtectionParam := app.ClpKeeper.GetLiquidityProtectionParams(ctx) liquidityProtectionParam.MaxRowanLiquidityThresholdAsset = tc.maxRowanLiquidityThresholdAsset @@ -703,19 +797,19 @@ func TestMsgServer_Swap(t *testing.T) { } require.NoError(t, err) - sentAssetBalanceRequest := banktypes.QueryBalanceRequest{ + externalAssetBalanceRequest := banktypes.QueryBalanceRequest{ Address: tc.address, - Denom: tc.msg.SentAsset.Symbol, + Denom: tc.poolAsset, } - sentAssetBalanceResponse, err := app.BankKeeper.Balance(sdk.WrapSDKContext(ctx), &sentAssetBalanceRequest) + externalAssetBalanceResponse, err := app.BankKeeper.Balance(sdk.WrapSDKContext(ctx), &externalAssetBalanceRequest) if err != nil { t.Fatalf("err: %v", err) } - sentAssetBalance := sentAssetBalanceResponse.Balance.Amount + externalAssetBalance := externalAssetBalanceResponse.Balance.Amount - require.Equal(t, tc.externalBalanceEnd.String(), sentAssetBalance.String()) + require.Equal(t, tc.externalBalanceEnd.String(), externalAssetBalance.String()) nativeAssetBalanceRequest := banktypes.QueryBalanceRequest{ Address: tc.address, @@ -1177,25 +1271,30 @@ func TestMsgServer_CreatePool(t *testing.T) { func TestMsgServer_AddLiquidity(t *testing.T) { testcases := []struct { - name string - createBalance bool - createPool bool - createLPs bool - poolAsset string - externalDecimals int64 - address string - nativeBalance sdk.Int - externalBalance sdk.Int - nativeAssetAmount sdk.Uint - externalAssetAmount sdk.Uint - poolUnits sdk.Uint - poolAssetPermissions []tokenregistrytypes.Permission - nativeAssetPermissions []tokenregistrytypes.Permission - msg *types.MsgAddLiquidity - expectedPoolUnits sdk.Uint - expectedLPUnits sdk.Uint - err error - errString error + name string + createBalance bool + createPool bool + createLPs bool + poolAsset string + address string + userNativeAssetBalance sdk.Int + userExternalAssetBalance sdk.Int + poolNativeAssetBalance sdk.Uint + poolExternalAssetBalance sdk.Uint + poolNativeLiabilities sdk.Uint + poolExternalLiabilities sdk.Uint + poolUnits sdk.Uint + poolAssetPermissions []tokenregistrytypes.Permission + nativeAssetPermissions []tokenregistrytypes.Permission + msg *types.MsgAddLiquidity + liquidityProtectionActive bool + maxRowanLiquidityThreshold sdk.Uint + currentRowanLiquidityThreshold sdk.Uint + expectedPoolUnits sdk.Uint + expectedLPUnits sdk.Uint + err error + errString error + expectedUpdatedRowanLiquidityThreshold sdk.Uint }{ { name: "external asset token not supported", @@ -1225,18 +1324,18 @@ func TestMsgServer_AddLiquidity(t *testing.T) { errString: errors.New("permission denied for denom"), }, { - name: "pool does not exist", - createBalance: true, - createPool: false, - createLPs: false, - poolAsset: "eth", - address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", - nativeBalance: sdk.NewInt(10000), - externalBalance: sdk.NewInt(10000), - nativeAssetAmount: sdk.NewUint(1000), - externalAssetAmount: sdk.NewUint(1000), - poolUnits: sdk.NewUint(1000), - poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + name: "pool does not exist", + createBalance: true, + createPool: false, + createLPs: false, + poolAsset: "eth", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.NewInt(10000), + userExternalAssetBalance: sdk.NewInt(10000), + poolNativeAssetBalance: sdk.NewUint(1000), + poolExternalAssetBalance: sdk.NewUint(1000), + poolUnits: sdk.NewUint(1000), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, msg: &types.MsgAddLiquidity{ Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", ExternalAsset: &types.Asset{Symbol: "eth"}, @@ -1246,18 +1345,18 @@ func TestMsgServer_AddLiquidity(t *testing.T) { errString: errors.New("pool does not exist"), }, { - name: "user does have enough balance of required coin", - createBalance: true, - createPool: true, - createLPs: true, - poolAsset: "eth", - address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", - nativeBalance: sdk.NewInt(10000), - externalBalance: sdk.NewInt(10000), - nativeAssetAmount: sdk.NewUint(1000), - externalAssetAmount: sdk.NewUint(1000), - poolUnits: sdk.NewUint(1000), - poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + name: "user does have enough balance of required coin", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "eth", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.NewInt(10000), + userExternalAssetBalance: sdk.NewInt(10000), + poolNativeAssetBalance: sdk.NewUint(1000), + poolExternalAssetBalance: sdk.NewUint(1000), + poolUnits: sdk.NewUint(1000), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, msg: &types.MsgAddLiquidity{ Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", ExternalAsset: &types.Asset{Symbol: "eth"}, @@ -1267,206 +1366,363 @@ func TestMsgServer_AddLiquidity(t *testing.T) { errString: errors.New("user does not have enough balance of the required coin: Unable to add liquidity"), }, { - name: "successful", - createBalance: true, - createPool: true, - createLPs: true, - poolAsset: "eth", - address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", - nativeBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), - externalBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), - nativeAssetAmount: sdk.NewUint(1000), - externalAssetAmount: sdk.NewUint(1000), - poolUnits: sdk.NewUint(1000), - poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + name: "one side of pool empty - zero amount of external asset added", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "eth", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), + userExternalAssetBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), + poolNativeAssetBalance: sdk.ZeroUint(), + poolExternalAssetBalance: sdk.NewUint(123), + poolUnits: sdk.NewUint(1000), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, msg: &types.MsgAddLiquidity{ Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetAmount: sdk.NewUintFromString(types.PoolThrehold), - ExternalAssetAmount: sdk.NewUintFromString(types.PoolThrehold), + NativeAssetAmount: sdk.NewUint(100), + ExternalAssetAmount: sdk.ZeroUint(), }, - expectedPoolUnits: sdk.NewUintFromString("1000000000000001000"), - expectedLPUnits: sdk.NewUintFromString("1000000000000000000"), + errString: errors.New("amount is invalid"), }, { - name: "native asset is 0", - createBalance: true, - createPool: true, - createLPs: true, - poolAsset: "eth", - address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", - nativeBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), - externalBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), - nativeAssetAmount: sdk.ZeroUint(), - externalAssetAmount: sdk.NewUint(1000), - poolUnits: sdk.NewUint(1000), - poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + name: "one side of pool empty - external and native asset added", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "eth", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), + userExternalAssetBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), + poolNativeAssetBalance: sdk.ZeroUint(), + poolExternalAssetBalance: sdk.NewUint(123), + poolUnits: sdk.NewUint(1000), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, msg: &types.MsgAddLiquidity{ Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetAmount: sdk.ZeroUint(), - ExternalAssetAmount: sdk.NewUintFromString(types.PoolThrehold), + NativeAssetAmount: sdk.NewUint(178), + ExternalAssetAmount: sdk.NewUint(156), }, - errString: errors.New("0: insufficient funds"), + liquidityProtectionActive: false, + expectedUpdatedRowanLiquidityThreshold: sdk.ZeroUint(), + expectedPoolUnits: sdk.NewUint(178), + expectedLPUnits: sdk.NewUint(178), }, { - name: "external asset is 0", - createBalance: true, - createPool: true, - createLPs: true, - poolAsset: "eth", - address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", - nativeBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), - externalBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), - nativeAssetAmount: sdk.NewUint(1000), - externalAssetAmount: sdk.ZeroUint(), - poolUnits: sdk.NewUint(1000), - poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + name: "success - symmetric", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "eth", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), + userExternalAssetBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), + poolNativeAssetBalance: sdk.NewUint(1000), + poolExternalAssetBalance: sdk.NewUint(1000), + poolUnits: sdk.NewUint(1000), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, msg: &types.MsgAddLiquidity{ Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", ExternalAsset: &types.Asset{Symbol: "eth"}, NativeAssetAmount: sdk.NewUintFromString(types.PoolThrehold), + ExternalAssetAmount: sdk.NewUintFromString(types.PoolThrehold), + }, + liquidityProtectionActive: false, + expectedUpdatedRowanLiquidityThreshold: sdk.ZeroUint(), + expectedPoolUnits: sdk.NewUintFromString("1000000000000001000"), + expectedLPUnits: sdk.NewUintFromString("1000000000000000000"), + }, + { + name: "success - nearly symmetric", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "cusdc", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.Int(sdk.NewUint(4000000000000000000)), + userExternalAssetBalance: sdk.Int(sdk.NewUint(68140)), + poolNativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), + poolExternalAssetBalance: sdk.NewUint(2674623482959), + poolUnits: sdk.NewUintFromString("23662660550457383692937954"), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + msg: &types.MsgAddLiquidity{ + Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + ExternalAsset: &types.Asset{Symbol: "cusdc"}, + NativeAssetAmount: sdk.NewUint(4000000000000000000), + ExternalAssetAmount: sdk.NewUint(68140), + }, + liquidityProtectionActive: false, + expectedUpdatedRowanLiquidityThreshold: sdk.ZeroUint(), + expectedPoolUnits: sdk.NewUintFromString("23662661153298862513590992"), + expectedLPUnits: sdk.NewUintFromString("602841478820653038"), + }, + { + name: "success - swap external", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "cusdc", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.Int(sdk.NewUint(4000000000000000000)), + userExternalAssetBalance: sdk.Int(sdk.NewUint(68140)), + poolNativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), + poolExternalAssetBalance: sdk.NewUint(2674623482959), + poolUnits: sdk.NewUintFromString("23662660550457383692937954"), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + msg: &types.MsgAddLiquidity{ + Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + ExternalAsset: &types.Asset{Symbol: "cusdc"}, + NativeAssetAmount: sdk.NewUint(0), + ExternalAssetAmount: sdk.NewUint(68140), + }, + liquidityProtectionActive: false, + expectedUpdatedRowanLiquidityThreshold: sdk.ZeroUint(), + expectedPoolUnits: sdk.NewUintFromString("23662660751003435747009552"), + expectedLPUnits: sdk.NewUintFromString("200546052054071598"), + }, + { + name: "success - swap native", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "cusdc", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.Int(sdk.NewUint(4000000000000000000)), + userExternalAssetBalance: sdk.Int(sdk.NewUint(68140)), + poolNativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), + poolExternalAssetBalance: sdk.NewUint(2674623482959), + poolUnits: sdk.NewUintFromString("23662660550457383692937954"), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + msg: &types.MsgAddLiquidity{ + Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + ExternalAsset: &types.Asset{Symbol: "cusdc"}, + NativeAssetAmount: sdk.NewUint(4000000000000000000), ExternalAssetAmount: sdk.ZeroUint(), }, - errString: errors.New("0: insufficient funds"), + liquidityProtectionActive: false, + expectedUpdatedRowanLiquidityThreshold: sdk.ZeroUint(), + expectedPoolUnits: sdk.NewUintFromString("23662660951949037742990437"), + expectedLPUnits: sdk.NewUintFromString("401491654050052483"), }, { - name: "external and native asset is 0", - createBalance: true, - createPool: true, - createLPs: true, - poolAsset: "eth", - address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", - nativeBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), - externalBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), - nativeAssetAmount: sdk.ZeroUint(), - externalAssetAmount: sdk.ZeroUint(), - poolUnits: sdk.NewUint(1000), - poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + name: "success - swap native - with liabilities", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "cusdc", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.Int(sdk.NewUint(4000000000000000000)), + userExternalAssetBalance: sdk.Int(sdk.NewUint(68140)), + poolNativeAssetBalance: sdk.ZeroUint(), + poolExternalAssetBalance: sdk.ZeroUint(), + poolNativeLiabilities: sdk.NewUintFromString("157007500498726220240179086"), + poolExternalLiabilities: sdk.NewUint(2674623482959), + poolUnits: sdk.NewUintFromString("23662660550457383692937954"), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + msg: &types.MsgAddLiquidity{ + Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + ExternalAsset: &types.Asset{Symbol: "cusdc"}, + NativeAssetAmount: sdk.NewUint(4000000000000000000), + ExternalAssetAmount: sdk.ZeroUint(), + }, + liquidityProtectionActive: false, + expectedUpdatedRowanLiquidityThreshold: sdk.ZeroUint(), + expectedPoolUnits: sdk.NewUintFromString("23662660951949037742990437"), + expectedLPUnits: sdk.NewUintFromString("401491654050052483"), + }, + { + name: "success - symmetric - liquidity protection enabled", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "eth", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), + userExternalAssetBalance: sdk.Int(sdk.NewUintFromString(types.PoolThrehold)), + poolNativeAssetBalance: sdk.NewUint(1000), + poolExternalAssetBalance: sdk.NewUint(1000), + poolUnits: sdk.NewUint(1000), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, msg: &types.MsgAddLiquidity{ Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", ExternalAsset: &types.Asset{Symbol: "eth"}, - NativeAssetAmount: sdk.ZeroUint(), + NativeAssetAmount: sdk.NewUintFromString(types.PoolThrehold), + ExternalAssetAmount: sdk.NewUintFromString(types.PoolThrehold), + }, + liquidityProtectionActive: true, + maxRowanLiquidityThreshold: sdk.NewUint(1336005328924242545), + currentRowanLiquidityThreshold: sdk.NewUint(10), + expectedUpdatedRowanLiquidityThreshold: sdk.NewUint(10), + expectedPoolUnits: sdk.NewUintFromString("1000000000000001000"), + expectedLPUnits: sdk.NewUintFromString("1000000000000000000"), + }, + { + name: "success - swap external - liquidity protection enabled", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "cusdc", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.Int(sdk.NewUint(4000000000000000000)), + userExternalAssetBalance: sdk.Int(sdk.NewUint(68140)), + poolNativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), + poolExternalAssetBalance: sdk.NewUint(2674623482959), + poolUnits: sdk.NewUintFromString("23662660550457383692937954"), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + msg: &types.MsgAddLiquidity{ + Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + ExternalAsset: &types.Asset{Symbol: "cusdc"}, + NativeAssetAmount: sdk.NewUint(0), + ExternalAssetAmount: sdk.NewUint(68140), + }, + liquidityProtectionActive: true, + maxRowanLiquidityThreshold: sdk.NewUint(13360053289242425450), + currentRowanLiquidityThreshold: sdk.NewUint(10), + expectedUpdatedRowanLiquidityThreshold: sdk.NewUint(1330659558593215210), + expectedPoolUnits: sdk.NewUintFromString("23662660751003435747009552"), + expectedLPUnits: sdk.NewUintFromString("200546052054071598"), + }, + { + name: "success - swap native- liquidity protection enabled", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "cusdc", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.Int(sdk.NewUint(4000000000000000000)), + userExternalAssetBalance: sdk.Int(sdk.NewUint(68140)), + poolNativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), + poolExternalAssetBalance: sdk.NewUint(2674623482959), + poolUnits: sdk.NewUintFromString("23662660550457383692937954"), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + msg: &types.MsgAddLiquidity{ + Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + ExternalAsset: &types.Asset{Symbol: "cusdc"}, + NativeAssetAmount: sdk.NewUint(4000000000000000000), ExternalAssetAmount: sdk.ZeroUint(), }, - errString: errors.New("Tx amount is too low"), + liquidityProtectionActive: true, + maxRowanLiquidityThreshold: sdk.NewUint(1336005328924242545), + currentRowanLiquidityThreshold: sdk.NewUint(1336005328924242544), + expectedUpdatedRowanLiquidityThreshold: sdk.NewUint(4008015986772728), + expectedPoolUnits: sdk.NewUintFromString("23662660951949037742990437"), + expectedLPUnits: sdk.NewUintFromString("401491654050052483"), }, { - name: "success", - createBalance: true, - createPool: true, - createLPs: true, - poolAsset: "cusdc", - externalDecimals: 6, - address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", - nativeBalance: sdk.Int(sdk.NewUintFromString("4000000000000000000")), - externalBalance: sdk.Int(sdk.NewUint(68140)), - nativeAssetAmount: sdk.NewUintFromString("157007500498726220240179086"), - externalAssetAmount: sdk.NewUint(2674623482959), - poolUnits: sdk.NewUintFromString("23662660550457383692937954"), - poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + name: "failure - swap native - liquidity protection enabled", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "cusdc", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.Int(sdk.NewUint(4000000000000000000)), + userExternalAssetBalance: sdk.Int(sdk.NewUint(68140)), + poolNativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), + poolExternalAssetBalance: sdk.NewUint(2674623482959), + poolUnits: sdk.NewUintFromString("23662660550457383692937954"), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + msg: &types.MsgAddLiquidity{ + Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + ExternalAsset: &types.Asset{Symbol: "cusdc"}, + NativeAssetAmount: sdk.NewUint(4000000000000000000), + ExternalAssetAmount: sdk.ZeroUint(), + }, + liquidityProtectionActive: true, + maxRowanLiquidityThreshold: sdk.NewUint(1336005328924242545), + currentRowanLiquidityThreshold: sdk.NewUint(1336005328924242543), + errString: types.ErrReachedMaxRowanLiquidityThreshold, + }, + { + name: "failure - swap external - sell external disabled", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "cusdc", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.Int(sdk.NewUint(4000000000000000000)), + userExternalAssetBalance: sdk.Int(sdk.NewUint(68140)), + poolNativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), + poolExternalAssetBalance: sdk.NewUint(2674623482959), + poolUnits: sdk.NewUintFromString("23662660550457383692937954"), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP, tokenregistrytypes.Permission_DISABLE_SELL}, msg: &types.MsgAddLiquidity{ Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", ExternalAsset: &types.Asset{Symbol: "cusdc"}, - NativeAssetAmount: sdk.NewUintFromString("4000000000000000000"), + NativeAssetAmount: sdk.NewUint(0), ExternalAssetAmount: sdk.NewUint(68140), }, - expectedPoolUnits: sdk.NewUintFromString("23662661153298835875523384"), - expectedLPUnits: sdk.NewUintFromString("602841452182585430"), + liquidityProtectionActive: false, + errString: tokenregistrytypes.ErrNotAllowedToSellAsset, }, - // { - // // Same test as above but with external asset amount just below top limit - // name: "success (normalized) ratios diff = 0.000000000000000499", - // createBalance: true, - // createPool: true, - // createLPs: true, - // poolAsset: "cusdc", - // externalDecimals: 6, - // address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", - // nativeBalance: sdk.Int(sdk.NewUintFromString("4000000000000000000")), - // externalBalance: sdk.Int(sdk.NewUint(70140)), - // nativeAssetAmount: sdk.NewUintFromString("157007500498726220240179086"), - // externalAssetAmount: sdk.NewUint(2674623482959), - // poolUnits: sdk.NewUintFromString("23662660550457383692937954"), - // poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, - // msg: &types.MsgAddLiquidity{ - // Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", - // ExternalAsset: &types.Asset{Symbol: "cusdc"}, - // NativeAssetAmount: sdk.NewUintFromString("4000000000000000000"), - // ExternalAssetAmount: sdk.NewUint(70140), - // }, - // expectedPoolUnits: sdk.NewUintFromString("23662661162145935094484778"), - // expectedLPUnits: sdk.NewUintFromString("611688551401546824"), - // }, - // { - // // Same test as above but with external asset amount just above top limit - // name: "failure (normalized) ratios diff = 0.000000000000000500", - // createBalance: true, - // createPool: true, - // createLPs: true, - // poolAsset: "cusdc", - // externalDecimals: 6, - // address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", - // nativeBalance: sdk.Int(sdk.NewUintFromString("4000000000000000000")), - // externalBalance: sdk.Int(sdk.NewUint(70141)), - // nativeAssetAmount: sdk.NewUintFromString("157007500498726220240179086"), - // externalAssetAmount: sdk.NewUint(2674623482959), - // poolUnits: sdk.NewUintFromString("23662660550457383692937954"), - // poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, - // msg: &types.MsgAddLiquidity{ - // Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", - // ExternalAsset: &types.Asset{Symbol: "cusdc"}, - // NativeAssetAmount: sdk.NewUintFromString("4000000000000000000"), - // ExternalAssetAmount: sdk.NewUint(70141), - // }, - // errString: errors.New("Cannot add liquidity with asymmetric ratio"), - // }, { - // Same test as above but with external asset amount just above bottom limit - name: "success (normalized) ratios diff = 0.000000000000000499", - createBalance: true, - createPool: true, - createLPs: true, - poolAsset: "cusdc", - externalDecimals: 6, - address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", - nativeBalance: sdk.Int(sdk.NewUintFromString("4000000000000000000")), - externalBalance: sdk.Int(sdk.NewUint(68480)), - nativeAssetAmount: sdk.NewUintFromString("157007500498726220240179086"), - externalAssetAmount: sdk.NewUint(2674623482959), - poolUnits: sdk.NewUintFromString("23662660550457383692937954"), - poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + name: "failure - swap external - buy native disabled", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "cusdc", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.Int(sdk.NewUint(4000000000000000000)), + userExternalAssetBalance: sdk.Int(sdk.NewUint(68140)), + poolNativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), + poolExternalAssetBalance: sdk.NewUint(2674623482959), + poolUnits: sdk.NewUintFromString("23662660550457383692937954"), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + nativeAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_DISABLE_BUY}, msg: &types.MsgAddLiquidity{ Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", ExternalAsset: &types.Asset{Symbol: "cusdc"}, - NativeAssetAmount: sdk.NewUintFromString("4000000000000000000"), - ExternalAssetAmount: sdk.NewUint(68480), + NativeAssetAmount: sdk.NewUint(0), + ExternalAssetAmount: sdk.NewUint(68140), }, - expectedPoolUnits: sdk.NewUintFromString("23662661154802842743687067"), - expectedLPUnits: sdk.NewUintFromString("604345459050749113"), + liquidityProtectionActive: false, + errString: tokenregistrytypes.ErrNotAllowedToBuyAsset, }, { - // Same test as above but with external asset amount just below bottom limit - name: "failure (normalized) ratios diff = 0.000000000000000500", - createBalance: true, - createPool: true, - createLPs: true, - poolAsset: "cusdc", - externalDecimals: 6, - address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", - nativeBalance: sdk.Int(sdk.NewUintFromString("4000000000000000000")), - externalBalance: sdk.Int(sdk.NewUint(68489)), - nativeAssetAmount: sdk.NewUintFromString("157007500498726220240179086"), - externalAssetAmount: sdk.NewUint(2674623482959), - poolUnits: sdk.NewUintFromString("23662660550457383692937954"), - poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + name: "failure - swap native - sell native disabled", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "cusdc", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.Int(sdk.NewUint(4000000000000000000)), + userExternalAssetBalance: sdk.Int(sdk.NewUint(68140)), + poolNativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), + poolExternalAssetBalance: sdk.NewUint(2674623482959), + poolUnits: sdk.NewUintFromString("23662660550457383692937954"), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}, + nativeAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_DISABLE_SELL}, + msg: &types.MsgAddLiquidity{ + Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + ExternalAsset: &types.Asset{Symbol: "cusdc"}, + NativeAssetAmount: sdk.NewUint(4000000000000000000), + ExternalAssetAmount: sdk.ZeroUint(), + }, + liquidityProtectionActive: false, + errString: tokenregistrytypes.ErrNotAllowedToSellAsset, + }, + { + name: "failure - swap native - buy external disabled", + createBalance: true, + createPool: true, + createLPs: true, + poolAsset: "cusdc", + address: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", + userNativeAssetBalance: sdk.Int(sdk.NewUint(4000000000000000000)), + userExternalAssetBalance: sdk.Int(sdk.NewUint(68140)), + poolNativeAssetBalance: sdk.NewUintFromString("157007500498726220240179086"), + poolExternalAssetBalance: sdk.NewUint(2674623482959), + poolUnits: sdk.NewUintFromString("23662660550457383692937954"), + poolAssetPermissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP, tokenregistrytypes.Permission_DISABLE_BUY}, msg: &types.MsgAddLiquidity{ Signer: "sif1syavy2npfyt9tcncdtsdzf7kny9lh777yqc2nd", ExternalAsset: &types.Asset{Symbol: "cusdc"}, - NativeAssetAmount: sdk.NewUintFromString("4000000000000000000"), - ExternalAssetAmount: sdk.NewUint(68489), + NativeAssetAmount: sdk.NewUint(4000000000000000000), + ExternalAssetAmount: sdk.ZeroUint(), }, - errString: errors.New("Cannot add liquidity with asymmetric ratio"), + liquidityProtectionActive: false, + errString: tokenregistrytypes.ErrNotAllowedToBuyAsset, }, } @@ -1477,7 +1733,7 @@ func TestMsgServer_AddLiquidity(t *testing.T) { trGs := &tokenregistrytypes.GenesisState{ Registry: &tokenregistrytypes.Registry{ Entries: []*tokenregistrytypes.RegistryEntry{ - {Denom: tc.poolAsset, BaseDenom: tc.poolAsset, Decimals: tc.externalDecimals, Permissions: tc.poolAssetPermissions}, + {Denom: tc.poolAsset, BaseDenom: tc.poolAsset, Decimals: 18, Permissions: tc.poolAssetPermissions}, {Denom: "rowan", BaseDenom: "rowan", Decimals: 18, Permissions: tc.nativeAssetPermissions}, }, }, @@ -1490,8 +1746,8 @@ func TestMsgServer_AddLiquidity(t *testing.T) { { Address: tc.address, Coins: sdk.Coins{ - sdk.NewCoin(tc.poolAsset, tc.externalBalance), - sdk.NewCoin("rowan", tc.nativeBalance), + sdk.NewCoin(tc.poolAsset, tc.userExternalAssetBalance), + sdk.NewCoin("rowan", tc.userNativeAssetBalance), }, }, } @@ -1506,9 +1762,11 @@ func TestMsgServer_AddLiquidity(t *testing.T) { pools := []*types.Pool{ { ExternalAsset: &types.Asset{Symbol: tc.poolAsset}, - NativeAssetBalance: tc.nativeAssetAmount, - ExternalAssetBalance: tc.externalAssetAmount, + NativeAssetBalance: tc.poolNativeAssetBalance, + ExternalAssetBalance: tc.poolExternalAssetBalance, PoolUnits: tc.poolUnits, + NativeLiabilities: tc.poolNativeLiabilities, + ExternalLiabilities: tc.poolExternalLiabilities, }, } clpGs := types.DefaultGenesisState() @@ -1536,6 +1794,14 @@ func TestMsgServer_AddLiquidity(t *testing.T) { app.ClpKeeper.SetPmtpCurrentRunningRate(ctx, sdk.NewDec(1)) + liqProParams := app.ClpKeeper.GetLiquidityProtectionParams(ctx) + liqProParams.IsActive = tc.liquidityProtectionActive + liqProParams.MaxRowanLiquidityThreshold = tc.maxRowanLiquidityThreshold + liqProParams.MaxRowanLiquidityThresholdAsset = types.NativeSymbol + app.ClpKeeper.SetLiquidityProtectionParams(ctx, liqProParams) + + app.ClpKeeper.SetLiquidityProtectionCurrentRowanLiquidityThreshold(ctx, tc.currentRowanLiquidityThreshold) + msgServer := clpkeeper.NewMsgServerImpl(app.ClpKeeper) _, err := msgServer.AddLiquidity(sdk.WrapSDKContext(ctx), tc.msg) @@ -1555,6 +1821,9 @@ func TestMsgServer_AddLiquidity(t *testing.T) { require.Equal(t, tc.expectedPoolUnits.String(), pool.PoolUnits.String()) // compare strings so that the expected amounts can be read from the failure message require.Equal(t, tc.expectedLPUnits.String(), lp.LiquidityProviderUnits.String()) + + updatedThreshold := app.ClpKeeper.GetLiquidityProtectionRateParams(ctx).CurrentRowanLiquidityThreshold + require.Equal(t, tc.expectedUpdatedRowanLiquidityThreshold.String(), updatedThreshold.String()) }) } } diff --git a/x/clp/keeper/pmtp.go b/x/clp/keeper/pmtp.go index 6d907086d7..55021358ed 100644 --- a/x/clp/keeper/pmtp.go +++ b/x/clp/keeper/pmtp.go @@ -82,7 +82,6 @@ func (k Keeper) PolicyRun(ctx sdk.Context, pmtpCurrentRunningRate sdk.Dec) error spotPriceExternal = sdk.ZeroDec() } - // Note: the pool field should be named SpotPrice* pool.SwapPriceNative = &spotPriceNative pool.SwapPriceExternal = &spotPriceExternal diff --git a/x/clp/keeper/pureCalculation.go b/x/clp/keeper/pureCalculation.go index 919fc03f01..a2ef36ef1f 100644 --- a/x/clp/keeper/pureCalculation.go +++ b/x/clp/keeper/pureCalculation.go @@ -51,6 +51,11 @@ func RatIntQuo(r *big.Rat) *big.Int { return i.Quo(num, denom) } +func ApproxRatSquareRoot(x *big.Rat) *big.Int { + var i big.Int + return i.Sqrt(RatIntQuo(x)) +} + func IsAnyZero(inputs []sdk.Uint) bool { for _, val := range inputs { if val.IsZero() { diff --git a/x/clp/keeper/pureCalculation_test.go b/x/clp/keeper/pureCalculation_test.go index 6477c497f0..468855356c 100644 --- a/x/clp/keeper/pureCalculation_test.go +++ b/x/clp/keeper/pureCalculation_test.go @@ -301,3 +301,36 @@ func TestKeeper_RatIntQuo(t *testing.T) { }) } } + +func TestKeeper_ApproxRatSquareRoot(t *testing.T) { + testcases := []struct { + name string + x big.Rat + expected big.Int + }{ + { + name: "square number", + x: *big.NewRat(50, 2), + expected: *big.NewInt(5), + }, + { + name: "non square integer number", + x: *big.NewRat(100, 2), + expected: *big.NewInt(7), + }, + { + name: "non square non number", + x: *big.NewRat(101, 2), + expected: *big.NewInt(7), + }, + } + + for _, tc := range testcases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + y := clpkeeper.ApproxRatSquareRoot(&tc.x) + + require.Equal(t, tc.expected.String(), y.String()) + }) + } +} diff --git a/x/clp/keeper/swap.go b/x/clp/keeper/swap.go index 5479dd5512..7ba7d6981d 100644 --- a/x/clp/keeper/swap.go +++ b/x/clp/keeper/swap.go @@ -7,17 +7,15 @@ import ( func (k Keeper) CLPCalcSwap(ctx sdk.Context, sentAmount sdk.Uint, to types.Asset, pool types.Pool, marginEnabled bool) (sdk.Uint, error) { - X, Y, toRowan := pool.ExtractValues(to) + X, Y, toRowan, from := pool.ExtractValues(to) Xincl, Yincl := pool.ExtractDebt(X, Y, toRowan) pmtpCurrentRunningRate := k.GetPmtpRateParams(ctx).PmtpCurrentRunningRate - swapFeeParams := k.GetSwapFeeParams(ctx) - swapFeeRate := swapFeeParams.SwapFeeRate - minSwapFee := GetMinSwapFee(to, swapFeeParams.TokenParams) + swapFeeRate := k.GetSwapFeeRate(ctx, from, marginEnabled) - swapResult, _ := CalcSwapResult(toRowan, Xincl, sentAmount, Yincl, pmtpCurrentRunningRate, swapFeeRate, minSwapFee) + swapResult, _ := CalcSwapResult(toRowan, Xincl, sentAmount, Yincl, pmtpCurrentRunningRate, swapFeeRate) if swapResult.GTE(Y) { return sdk.ZeroUint(), types.ErrNotEnoughAssetTokens diff --git a/x/clp/keeper/swap_fee_params.go b/x/clp/keeper/swap_fee_params.go index 84699bd28d..41747615e0 100644 --- a/x/clp/keeper/swap_fee_params.go +++ b/x/clp/keeper/swap_fee_params.go @@ -15,18 +15,24 @@ func (k Keeper) GetSwapFeeParams(ctx sdk.Context) types.SwapFeeParams { store := ctx.KVStore(k.storeKey) bz := store.Get(types.SwapFeeParamsPrefix) if bz == nil { - return types.SwapFeeParams{SwapFeeRate: sdk.NewDecWithPrec(3, 3)} //0.003 + return types.SwapFeeParams{DefaultSwapFeeRate: sdk.NewDecWithPrec(3, 3)} //0.003 } k.cdc.MustUnmarshal(bz, ¶ms) return params } -func GetMinSwapFee(asset types.Asset, tokenParams []*types.SwapFeeTokenParams) sdk.Uint { - for _, p := range tokenParams { - if types.StringCompare(asset.Symbol, p.Asset) { - return p.MinSwapFee +func (k Keeper) GetSwapFeeRate(ctx sdk.Context, asset types.Asset, marginEnabled bool) sdk.Dec { + + params := k.GetSwapFeeParams(ctx) + + tokenParams := params.TokenParams + if !marginEnabled { + for _, p := range tokenParams { + if types.StringCompare(asset.Symbol, p.Asset) { + return p.SwapFeeRate + } } } - return sdk.ZeroUint() + return params.DefaultSwapFeeRate } diff --git a/x/clp/keeper/swap_fee_params_test.go b/x/clp/keeper/swap_fee_params_test.go index 01b8069b86..574b9a2c64 100644 --- a/x/clp/keeper/swap_fee_params_test.go +++ b/x/clp/keeper/swap_fee_params_test.go @@ -3,36 +3,84 @@ package keeper_test import ( "testing" - "github.com/Sifchain/sifnode/x/clp/keeper" + "github.com/Sifchain/sifnode/x/clp/test" "github.com/Sifchain/sifnode/x/clp/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" ) -func TestKeeper_GetMinSwapFee(t *testing.T) { +func TestKeeper_GetSwapFeeRate(t *testing.T) { testcases := []struct { - name string - asset types.Asset - tokenParams []*types.SwapFeeTokenParams - expectedMinSwapFee sdk.Uint + name string + asset types.Asset + swapFeeParams types.SwapFeeParams + marginEnabled bool + expectedSwapFeeRate sdk.Dec }{ { - name: "empty token params", - asset: types.NewAsset("ceth"), - expectedMinSwapFee: sdk.ZeroUint(), + name: "empty token params", + asset: types.NewAsset("ceth"), + swapFeeParams: types.SwapFeeParams{DefaultSwapFeeRate: sdk.NewDecWithPrec(3, 3)}, + marginEnabled: false, + expectedSwapFeeRate: sdk.NewDecWithPrec(3, 3), }, { - name: "match", - asset: types.NewAsset("ceth"), - tokenParams: []*types.SwapFeeTokenParams{{Asset: "ceth", MinSwapFee: sdk.NewUint(100)}, {Asset: "cusdc", MinSwapFee: sdk.NewUint(300)}}, - expectedMinSwapFee: sdk.NewUint(100), + name: "match", + asset: types.NewAsset("ceth"), + swapFeeParams: types.SwapFeeParams{ + DefaultSwapFeeRate: sdk.NewDecWithPrec(3, 3), + TokenParams: []*types.SwapFeeTokenParams{ + { + Asset: "ceth", + SwapFeeRate: sdk.NewDecWithPrec(1, 3), + }, + { + Asset: "cusdc", + SwapFeeRate: sdk.NewDecWithPrec(2, 3), + }, + }, + }, + marginEnabled: false, + expectedSwapFeeRate: sdk.NewDecWithPrec(1, 3), }, { - name: "no match", - asset: types.NewAsset("rowan"), - tokenParams: []*types.SwapFeeTokenParams{{Asset: "ceth", MinSwapFee: sdk.NewUint(100)}, {Asset: "cusdc", MinSwapFee: sdk.NewUint(300)}}, - expectedMinSwapFee: sdk.ZeroUint(), + name: "no match", + asset: types.NewAsset("rowan"), + swapFeeParams: types.SwapFeeParams{ + DefaultSwapFeeRate: sdk.NewDecWithPrec(3, 3), + TokenParams: []*types.SwapFeeTokenParams{ + { + Asset: "ceth", + SwapFeeRate: sdk.NewDecWithPrec(1, 3), + }, + { + Asset: "cusdc", + SwapFeeRate: sdk.NewDecWithPrec(2, 3), + }, + }, + }, + marginEnabled: false, + expectedSwapFeeRate: sdk.NewDecWithPrec(3, 3), + }, + { + name: "match but fallback to default rate as margin enabled", + asset: types.NewAsset("ceth"), + swapFeeParams: types.SwapFeeParams{ + DefaultSwapFeeRate: sdk.NewDecWithPrec(3, 3), + TokenParams: []*types.SwapFeeTokenParams{ + { + Asset: "ceth", + SwapFeeRate: sdk.NewDecWithPrec(1, 3), + }, + { + Asset: "cusdc", + SwapFeeRate: sdk.NewDecWithPrec(2, 3), + }, + }, + }, + marginEnabled: true, + expectedSwapFeeRate: sdk.NewDecWithPrec(3, 3), }, } @@ -40,9 +88,13 @@ func TestKeeper_GetMinSwapFee(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { - minSwapFee := keeper.GetMinSwapFee(tc.asset, tc.tokenParams) + ctx, app := test.CreateTestAppClp(false) + + app.ClpKeeper.SetSwapFeeParams(ctx, &tc.swapFeeParams) + + swapFeeRate := app.ClpKeeper.GetSwapFeeRate(ctx, tc.asset, tc.marginEnabled) - require.Equal(t, tc.expectedMinSwapFee.String(), minSwapFee.String()) + require.Equal(t, tc.expectedSwapFeeRate.String(), swapFeeRate.String()) }) } } diff --git a/x/clp/test/test_common.go b/x/clp/test/test_common.go index 475150bb56..9217a34f09 100644 --- a/x/clp/test/test_common.go +++ b/x/clp/test/test_common.go @@ -55,6 +55,7 @@ func CreateTestAppClpWithBlacklist(isCheckTx bool, blacklist []sdk.AccAddress) ( {Denom: "dash", Decimals: 18, Permissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}}, {Denom: "atom", Decimals: 18, Permissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}}, {Denom: "cusdc", Decimals: 18, Permissions: []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}}, + {Denom: "rowan"}, }, }) app.ClpKeeper.SetPmtpRateParams(ctx, types.PmtpRateParams{ diff --git a/x/clp/types/asset.go b/x/clp/types/asset.go index b6d1217ff6..6dfc6043e8 100644 --- a/x/clp/types/asset.go +++ b/x/clp/types/asset.go @@ -37,7 +37,7 @@ func (a Asset) IsEmpty() bool { } func (a *Asset) IsSettlementAsset() bool { - return *a == GetSettlementAsset() + return *a == GetSettlementAsset() } func GetSettlementAsset() Asset { diff --git a/x/clp/types/keys.go b/x/clp/types/keys.go index 512e1ca6ad..b842bd818f 100644 --- a/x/clp/types/keys.go +++ b/x/clp/types/keys.go @@ -75,7 +75,7 @@ func ParsePoolKey(key string) (string, string, error) { func GetDefaultRewardParams() *RewardParams { return &RewardParams{ - LiquidityRemovalLockPeriod: 12 * 60 * 24 * 7, + LiquidityRemovalLockPeriod: 0, LiquidityRemovalCancelPeriod: 12 * 60 * 24 * 30, RewardPeriods: nil, RewardPeriodStartTime: "", diff --git a/x/clp/types/msgs.go b/x/clp/types/msgs.go index 2f422a8301..35b8842f41 100644 --- a/x/clp/types/msgs.go +++ b/x/clp/types/msgs.go @@ -662,14 +662,24 @@ func (m MsgUpdateSwapFeeParamsRequest) ValidateBasic() error { return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, m.Signer) } - if m.SwapFeeRate.LT(sdk.ZeroDec()) { + if m.DefaultSwapFeeRate.LT(sdk.ZeroDec()) { return fmt.Errorf("swap rate fee must be greater than or equal to zero") } - if m.SwapFeeRate.GT(sdk.OneDec()) { + if m.DefaultSwapFeeRate.GT(sdk.OneDec()) { return fmt.Errorf("swap rate fee must be less than or equal to one") } + for _, p := range m.TokenParams { + if p.SwapFeeRate.LT(sdk.ZeroDec()) { + return fmt.Errorf("swap rate fee must be greater than or equal to zero") + } + + if p.SwapFeeRate.GT(sdk.OneDec()) { + return fmt.Errorf("swap rate fee must be less than or equal to one") + } + } + return nil } diff --git a/x/clp/types/params.pb.go b/x/clp/types/params.pb.go index 907f936486..2a7caa607d 100644 --- a/x/clp/types/params.pb.go +++ b/x/clp/types/params.pb.go @@ -581,8 +581,8 @@ func (m *ProviderDistributionParams) GetDistributionPeriods() []*ProviderDistrib } type SwapFeeParams struct { - SwapFeeRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=swap_fee_rate,json=swapFeeRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"swap_fee_rate"` - TokenParams []*SwapFeeTokenParams `protobuf:"bytes,2,rep,name=token_params,json=tokenParams,proto3" json:"token_params,omitempty"` + DefaultSwapFeeRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=default_swap_fee_rate,json=defaultSwapFeeRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"default_swap_fee_rate"` + TokenParams []*SwapFeeTokenParams `protobuf:"bytes,2,rep,name=token_params,json=tokenParams,proto3" json:"token_params,omitempty"` } func (m *SwapFeeParams) Reset() { *m = SwapFeeParams{} } @@ -626,8 +626,8 @@ func (m *SwapFeeParams) GetTokenParams() []*SwapFeeTokenParams { } type SwapFeeTokenParams struct { - Asset string `protobuf:"bytes,1,opt,name=asset,proto3" json:"asset,omitempty"` - MinSwapFee github_com_cosmos_cosmos_sdk_types.Uint `protobuf:"bytes,2,opt,name=min_swap_fee,json=minSwapFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Uint" json:"min_swap_fee"` + Asset string `protobuf:"bytes,1,opt,name=asset,proto3" json:"asset,omitempty"` + SwapFeeRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=swap_fee_rate,json=swapFeeRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"swap_fee_rate"` } func (m *SwapFeeTokenParams) Reset() { *m = SwapFeeTokenParams{} } @@ -688,78 +688,77 @@ func init() { func init() { proto.RegisterFile("sifnode/clp/v1/params.proto", fileDescriptor_61de66e331088d04) } var fileDescriptor_61de66e331088d04 = []byte{ - // 1121 bytes of a gzipped FileDescriptorProto + // 1119 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0xdd, 0x6e, 0xe3, 0x44, - 0x14, 0xae, 0x93, 0xb6, 0xb4, 0xa7, 0x3f, 0x80, 0x9b, 0xb6, 0xee, 0x5f, 0xd2, 0x35, 0x12, 0x54, - 0x45, 0x24, 0x74, 0xd1, 0xb2, 0x70, 0x99, 0xfe, 0x80, 0x16, 0x75, 0xa5, 0xac, 0x5b, 0x6e, 0x90, - 0x90, 0xe5, 0xda, 0xd3, 0x64, 0x54, 0xdb, 0xe3, 0x9d, 0x99, 0xa4, 0x2d, 0x88, 0x77, 0xd8, 0x2b, - 0x6e, 0x10, 0x3c, 0x00, 0xef, 0x81, 0xb4, 0x97, 0x8b, 0xb8, 0x41, 0x7b, 0x51, 0xa1, 0xf6, 0x45, - 0xd0, 0xcc, 0x38, 0xce, 0x38, 0x4e, 0xd1, 0xb6, 0xec, 0x55, 0xeb, 0x9c, 0x39, 0xdf, 0x77, 0xe6, - 0x3b, 0x67, 0xbe, 0xb1, 0x61, 0x8d, 0xe1, 0xd3, 0x98, 0x04, 0xa8, 0xe1, 0x87, 0x49, 0xa3, 0xb7, - 0xd3, 0x48, 0x3c, 0xea, 0x45, 0xac, 0x9e, 0x50, 0xc2, 0x89, 0x39, 0x9f, 0x06, 0xeb, 0x7e, 0x98, - 0xd4, 0x7b, 0x3b, 0xab, 0x95, 0x36, 0x69, 0x13, 0x19, 0x6a, 0x88, 0xff, 0xd4, 0x2a, 0xbb, 0x0b, - 0x93, 0x2d, 0x99, 0x65, 0x7e, 0x09, 0x2b, 0x11, 0x8e, 0x5d, 0x9f, 0x22, 0x8f, 0x23, 0x37, 0x21, - 0x24, 0x74, 0x79, 0x87, 0x22, 0xd6, 0x21, 0x61, 0x60, 0x19, 0x9b, 0xc6, 0xd6, 0xb8, 0xb3, 0x14, - 0xe1, 0x78, 0x4f, 0xc6, 0x5b, 0x84, 0x84, 0xc7, 0xfd, 0xa8, 0xf9, 0x29, 0x54, 0x50, 0xec, 0x9d, - 0x84, 0xc8, 0xa5, 0x28, 0x22, 0x3d, 0x2f, 0x74, 0x9f, 0x77, 0x51, 0x17, 0x59, 0xa5, 0x4d, 0x63, - 0x6b, 0xca, 0x31, 0x55, 0xcc, 0x51, 0xa1, 0x67, 0x22, 0x62, 0xff, 0x5c, 0x82, 0x59, 0x07, 0x9d, - 0x7b, 0x34, 0x48, 0xd9, 0x9b, 0xb0, 0x11, 0xe2, 0xe7, 0x5d, 0x1c, 0x60, 0x7e, 0x99, 0xa1, 0x84, - 0xc4, 0x3f, 0x73, 0x13, 0x44, 0x31, 0xe9, 0x57, 0xb0, 0x9a, 0x2d, 0x4a, 0xe1, 0x0e, 0x89, 0x7f, - 0xd6, 0x92, 0x2b, 0xcc, 0x03, 0xa8, 0x15, 0x21, 0x7c, 0x2f, 0xf6, 0x51, 0xd8, 0x07, 0x29, 0x49, - 0x90, 0xf5, 0x61, 0x90, 0x3d, 0xb9, 0x28, 0x85, 0xd9, 0x83, 0x79, 0x2a, 0x2b, 0x4b, 0x93, 0x98, - 0x35, 0xbe, 0x59, 0xde, 0x9a, 0x79, 0xb8, 0x5e, 0xcf, 0x0b, 0x5a, 0x4f, 0xeb, 0x97, 0x8b, 0x9c, - 0x39, 0xaa, 0x3d, 0x31, 0xf3, 0x31, 0x58, 0x39, 0x10, 0x97, 0x71, 0x8f, 0x72, 0x97, 0xe3, 0x08, - 0x59, 0x13, 0x9b, 0xc6, 0xd6, 0xb4, 0xb3, 0xa8, 0x27, 0x1c, 0x89, 0xe8, 0x31, 0x8e, 0x90, 0xfd, - 0x47, 0x09, 0xe6, 0x5b, 0x11, 0x4f, 0x1c, 0x21, 0xb2, 0x92, 0xc6, 0x87, 0xa5, 0x24, 0xe2, 0x49, - 0x1f, 0xe9, 0x44, 0xaa, 0x42, 0x3d, 0xae, 0xf4, 0x9d, 0xde, 0xad, 0xbf, 0xbc, 0xaa, 0x8d, 0xbd, - 0xbe, 0xaa, 0x7d, 0xd8, 0xc6, 0xbc, 0xd3, 0x3d, 0xa9, 0xfb, 0x24, 0x6a, 0xf8, 0x84, 0x45, 0x84, - 0xa5, 0x7f, 0x3e, 0x61, 0xc1, 0x59, 0x83, 0x5f, 0x26, 0x88, 0xd5, 0xf7, 0x91, 0xef, 0x2c, 0x08, - 0x34, 0xc5, 0xbb, 0x2b, 0xb0, 0x04, 0x95, 0x89, 0x61, 0x45, 0x92, 0xf8, 0x5d, 0x4a, 0x51, 0xcc, - 0x5d, 0xda, 0x8d, 0x63, 0x1c, 0xb7, 0x15, 0x4f, 0xf9, 0x5e, 0x3c, 0xb2, 0xea, 0x3d, 0x85, 0xe7, - 0x28, 0x38, 0x49, 0xd5, 0xdf, 0x0f, 0x8e, 0x39, 0xa2, 0x6e, 0x42, 0x42, 0xec, 0x5f, 0x2a, 0x9e, - 0xf1, 0xfb, 0xef, 0xe7, 0x89, 0x00, 0x6b, 0x49, 0x2c, 0x41, 0x62, 0xff, 0x56, 0x02, 0x10, 0x3a, - 0xa6, 0x1a, 0x46, 0xb0, 0xa6, 0x6b, 0xd8, 0x26, 0x3d, 0x44, 0x63, 0xd1, 0x75, 0x45, 0x6c, 0xdc, - 0x8b, 0xd8, 0x1a, 0x08, 0xf9, 0x75, 0x06, 0x28, 0xb7, 0xf8, 0x18, 0x2c, 0x9d, 0x0e, 0x25, 0xc4, - 0xef, 0xb8, 0x21, 0x8a, 0xdb, 0xbc, 0x23, 0x9b, 0x56, 0x76, 0x16, 0x07, 0xb9, 0x07, 0x22, 0x7a, - 0x28, 0x83, 0xe6, 0x23, 0x58, 0xd6, 0x13, 0xd5, 0xd4, 0xc8, 0x8e, 0xcb, 0x26, 0x94, 0x9d, 0xca, - 0x20, 0x4f, 0x0e, 0x8d, 0xec, 0xa0, 0xb9, 0x03, 0x8b, 0x39, 0xbe, 0x38, 0x1d, 0x13, 0xa9, 0x68, - 0xd9, 0x31, 0x35, 0xb2, 0x58, 0x35, 0xdd, 0xfe, 0x73, 0x3c, 0x3b, 0x81, 0x6a, 0xee, 0xb7, 0xe0, - 0xbd, 0xfc, 0xc8, 0x62, 0x75, 0xe8, 0xa6, 0x9d, 0x79, 0x7d, 0x54, 0x9f, 0x04, 0xc2, 0x29, 0x46, - 0x0d, 0xb7, 0x62, 0x54, 0x47, 0x6c, 0xa9, 0x30, 0xdd, 0xaa, 0xd0, 0x47, 0xb0, 0x9c, 0x4f, 0x1d, - 0x94, 0x5a, 0x96, 0x89, 0x15, 0x3d, 0xb1, 0x5f, 0xac, 0x89, 0x86, 0x8f, 0x93, 0x17, 0x86, 0xc4, - 0xf7, 0x38, 0x26, 0x71, 0x3a, 0x34, 0x1f, 0xbf, 0xbe, 0xaa, 0x7d, 0xf4, 0x06, 0x7d, 0xfb, 0x16, - 0xc7, 0x3c, 0x5f, 0x5d, 0x33, 0x83, 0x32, 0x7d, 0xa8, 0xe6, 0x69, 0xa4, 0x0b, 0x46, 0xdd, 0x90, - 0xe3, 0x24, 0xc4, 0x88, 0x32, 0x6b, 0x42, 0x5a, 0x41, 0x75, 0xd8, 0x0a, 0x84, 0x1d, 0x3e, 0xcd, - 0x96, 0x39, 0x6b, 0x3a, 0x7e, 0x3e, 0xc6, 0x4c, 0x06, 0x9b, 0x79, 0x92, 0x00, 0x9d, 0x7a, 0xdd, - 0x90, 0x6b, 0x3c, 0xd6, 0xa4, 0xdc, 0xd3, 0xf6, 0x1d, 0x66, 0x71, 0x43, 0xa7, 0xdc, 0x57, 0x88, - 0x03, 0x56, 0xf3, 0x8b, 0x61, 0x01, 0x03, 0xcc, 0x38, 0xc5, 0x27, 0x5d, 0x8e, 0xac, 0x77, 0xa4, - 0x4b, 0xe7, 0x34, 0xd9, 0xcf, 0xa2, 0xe6, 0x36, 0xbc, 0x9f, 0xcf, 0x8c, 0x48, 0x60, 0x4d, 0xc9, - 0x5e, 0xbd, 0xab, 0xa7, 0x3c, 0x25, 0x81, 0xfd, 0xc2, 0x80, 0xf9, 0xfc, 0x76, 0xcd, 0x87, 0xb0, - 0x38, 0x24, 0xa2, 0xeb, 0x31, 0x86, 0x78, 0x3a, 0x5a, 0x0b, 0x49, 0x6e, 0x79, 0x53, 0x84, 0xcc, - 0x6f, 0x00, 0x34, 0x2d, 0x4a, 0x77, 0xd6, 0x42, 0xcb, 0xb6, 0x7f, 0x2d, 0xc1, 0xca, 0x61, 0xdf, - 0xee, 0x5b, 0x94, 0x70, 0xe4, 0x8b, 0x56, 0xa7, 0xb6, 0x40, 0x61, 0x23, 0xf2, 0x2e, 0x5c, 0x4a, - 0xce, 0xbd, 0xd8, 0x1d, 0x5c, 0x1e, 0xf9, 0x7b, 0x6f, 0x7a, 0xb7, 0x91, 0x1a, 0xc3, 0x1b, 0x0f, - 0xd8, 0x6a, 0xe4, 0x5d, 0x38, 0x02, 0x34, 0xa3, 0x1e, 0x5c, 0x96, 0x87, 0xf0, 0xc1, 0x7f, 0x72, - 0xa6, 0xfa, 0xc8, 0x6d, 0x3b, 0xb5, 0xdb, 0x81, 0x94, 0x56, 0x0f, 0x60, 0x36, 0xe7, 0x2e, 0xea, - 0x14, 0xcd, 0x20, 0xcd, 0x53, 0xd6, 0x60, 0x1a, 0x33, 0xd7, 0xf3, 0x39, 0xee, 0x29, 0x8b, 0x9d, - 0x72, 0xa6, 0x30, 0x6b, 0xca, 0x67, 0xfb, 0x17, 0x03, 0x36, 0x46, 0xe8, 0xa3, 0x5d, 0x3f, 0x3f, - 0xc0, 0x83, 0xec, 0x52, 0x78, 0xdb, 0x3a, 0x55, 0x53, 0xe4, 0x5b, 0xb6, 0x68, 0xff, 0x55, 0x82, - 0xd5, 0x16, 0x25, 0x3d, 0x1c, 0x20, 0x9a, 0xcd, 0xa4, 0x68, 0x9f, 0xb2, 0x2c, 0x06, 0xd5, 0x40, - 0xfb, 0x75, 0xc4, 0x0d, 0x79, 0x3f, 0x63, 0x5f, 0x0b, 0x0a, 0x5c, 0x83, 0x9b, 0xf2, 0x00, 0x6a, - 0xa3, 0x48, 0x8b, 0x1e, 0xb8, 0x5e, 0x44, 0xd1, 0x9c, 0xb0, 0x09, 0x1b, 0xa3, 0x60, 0x86, 0xfd, - 0x70, 0xb5, 0x08, 0x92, 0xb9, 0xe2, 0xe7, 0xb0, 0x3c, 0x0a, 0x42, 0x1c, 0xd0, 0x71, 0x99, 0xbc, - 0x58, 0x4c, 0x16, 0xc7, 0xf4, 0xc7, 0x5b, 0x44, 0x55, 0xfd, 0xfe, 0x1e, 0x2a, 0x23, 0x50, 0x99, - 0x65, 0x48, 0xeb, 0xdb, 0x2e, 0x58, 0xdf, 0xad, 0xed, 0x71, 0x16, 0x8a, 0xf4, 0xcc, 0xfe, 0xdd, - 0x80, 0xb9, 0xa3, 0x73, 0x2f, 0xf9, 0x0a, 0xf5, 0x07, 0xcc, 0x81, 0x39, 0x76, 0xee, 0x25, 0xee, - 0x29, 0xfa, 0x5f, 0xb7, 0xf1, 0x0c, 0x53, 0xa8, 0x69, 0x93, 0x66, 0x39, 0x39, 0x43, 0xb1, 0xab, - 0x5e, 0x89, 0xad, 0x92, 0x2c, 0xde, 0x1e, 0x2e, 0x3e, 0x2d, 0xe4, 0x58, 0x2c, 0x55, 0xd5, 0x38, - 0x33, 0x7c, 0xf0, 0x60, 0xff, 0x04, 0x66, 0x71, 0x89, 0x59, 0x81, 0x09, 0xdd, 0xc3, 0xd4, 0x83, - 0xf9, 0x0c, 0x66, 0xc5, 0xfb, 0x73, 0x7f, 0x2b, 0xa9, 0x6f, 0xdd, 0xf9, 0x48, 0x40, 0x84, 0xe3, - 0x94, 0x73, 0xb7, 0xf9, 0xf2, 0xba, 0x6a, 0xbc, 0xba, 0xae, 0x1a, 0xff, 0x5c, 0x57, 0x8d, 0x17, - 0x37, 0xd5, 0xb1, 0x57, 0x37, 0xd5, 0xb1, 0xbf, 0x6f, 0xaa, 0x63, 0xdf, 0xe9, 0x70, 0x47, 0xf8, - 0xd4, 0xef, 0x78, 0x38, 0x6e, 0xf4, 0xbf, 0x06, 0x2e, 0xe4, 0xf7, 0x80, 0xc4, 0x3c, 0x99, 0x94, - 0xaf, 0xf9, 0x9f, 0xfd, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x80, 0x27, 0xe7, 0x26, 0x2b, 0x0c, 0x00, - 0x00, + 0x14, 0xae, 0x93, 0xb6, 0xb4, 0xa7, 0x3f, 0xc0, 0x34, 0x69, 0xdd, 0xbf, 0xa4, 0x6b, 0x24, 0xa8, + 0x8a, 0x48, 0xe8, 0xa2, 0x65, 0xe1, 0x32, 0xfd, 0x01, 0x2d, 0xea, 0x4a, 0xc1, 0x2d, 0x37, 0x48, + 0xc8, 0x9a, 0xda, 0xd3, 0x64, 0x54, 0xdb, 0xe3, 0x9d, 0x99, 0xa4, 0x2d, 0x48, 0x3c, 0xc3, 0x5e, + 0x71, 0x83, 0xe0, 0x3d, 0x78, 0x00, 0xa4, 0xbd, 0x5c, 0xc4, 0x0d, 0xda, 0x8b, 0x0a, 0xb5, 0x2f, + 0x82, 0x66, 0xc6, 0x49, 0xec, 0x24, 0x45, 0xbb, 0x85, 0xab, 0xd6, 0x39, 0x73, 0xbe, 0x6f, 0xce, + 0x77, 0xce, 0x7c, 0x63, 0xc3, 0xba, 0xa0, 0x67, 0x31, 0x0b, 0x48, 0xdd, 0x0f, 0x93, 0x7a, 0x77, + 0xb7, 0x9e, 0x60, 0x8e, 0x23, 0x51, 0x4b, 0x38, 0x93, 0x0c, 0x2d, 0xa6, 0xc1, 0x9a, 0x1f, 0x26, + 0xb5, 0xee, 0xee, 0x5a, 0xa9, 0xc5, 0x5a, 0x4c, 0x87, 0xea, 0xea, 0x3f, 0xb3, 0xca, 0xe9, 0xc0, + 0x74, 0x53, 0x67, 0xa1, 0xcf, 0x61, 0x35, 0xa2, 0xb1, 0xe7, 0x73, 0x82, 0x25, 0xf1, 0x12, 0xc6, + 0x42, 0x4f, 0xb6, 0x39, 0x11, 0x6d, 0x16, 0x06, 0xb6, 0xb5, 0x65, 0x6d, 0x4f, 0xba, 0xcb, 0x11, + 0x8d, 0xf7, 0x75, 0xbc, 0xc9, 0x58, 0x78, 0xd2, 0x8b, 0xa2, 0x8f, 0xa1, 0x44, 0x62, 0x7c, 0x1a, + 0x12, 0x8f, 0x93, 0x88, 0x75, 0x71, 0xe8, 0x3d, 0xeb, 0x90, 0x0e, 0xb1, 0x0b, 0x5b, 0xd6, 0xf6, + 0x8c, 0x8b, 0x4c, 0xcc, 0x35, 0xa1, 0xaf, 0x55, 0xc4, 0xf9, 0xa9, 0x00, 0xf3, 0x2e, 0xb9, 0xc0, + 0x3c, 0x48, 0xd9, 0x1b, 0xb0, 0x19, 0xd2, 0x67, 0x1d, 0x1a, 0x50, 0x79, 0xd5, 0x47, 0x09, 0x99, + 0x7f, 0xee, 0x25, 0x84, 0x53, 0xd6, 0xdb, 0xc1, 0x5a, 0x7f, 0x51, 0x0a, 0x77, 0xc4, 0xfc, 0xf3, + 0xa6, 0x5e, 0x81, 0x0e, 0xa1, 0x3a, 0x0a, 0xe1, 0xe3, 0xd8, 0x27, 0x61, 0x0f, 0xa4, 0xa0, 0x41, + 0x36, 0x86, 0x41, 0xf6, 0xf5, 0xa2, 0x14, 0x66, 0x1f, 0x16, 0xb9, 0xde, 0x59, 0x9a, 0x24, 0xec, + 0xc9, 0xad, 0xe2, 0xf6, 0xdc, 0xc3, 0x8d, 0x5a, 0x5e, 0xd0, 0x5a, 0xba, 0x7f, 0xbd, 0xc8, 0x5d, + 0xe0, 0x99, 0x27, 0x81, 0x1e, 0x83, 0x9d, 0x03, 0xf1, 0x84, 0xc4, 0x5c, 0x7a, 0x92, 0x46, 0xc4, + 0x9e, 0xda, 0xb2, 0xb6, 0x67, 0xdd, 0x72, 0x36, 0xe1, 0x58, 0x45, 0x4f, 0x68, 0x44, 0x9c, 0xdf, + 0x0b, 0xb0, 0xd8, 0x8c, 0x64, 0xe2, 0x2a, 0x91, 0x8d, 0x34, 0x3e, 0x2c, 0x27, 0x91, 0x4c, 0x7a, + 0x48, 0xa7, 0x5a, 0x15, 0x8e, 0xa5, 0xd1, 0x77, 0x76, 0xaf, 0xf6, 0xe2, 0xba, 0x3a, 0xf1, 0xea, + 0xba, 0xfa, 0x7e, 0x8b, 0xca, 0x76, 0xe7, 0xb4, 0xe6, 0xb3, 0xa8, 0xee, 0x33, 0x11, 0x31, 0x91, + 0xfe, 0xf9, 0x48, 0x04, 0xe7, 0x75, 0x79, 0x95, 0x10, 0x51, 0x3b, 0x20, 0xbe, 0xbb, 0xa4, 0xd0, + 0x0c, 0xef, 0x9e, 0xc2, 0x52, 0x54, 0x88, 0xc2, 0xaa, 0x26, 0xf1, 0x3b, 0x9c, 0x93, 0x58, 0x7a, + 0xbc, 0x13, 0xc7, 0x34, 0x6e, 0x19, 0x9e, 0xe2, 0xbd, 0x78, 0xf4, 0xae, 0xf7, 0x0d, 0x9e, 0x6b, + 0xe0, 0x34, 0x55, 0xaf, 0x1e, 0x1a, 0x4b, 0xc2, 0xbd, 0x84, 0x85, 0xd4, 0xbf, 0x32, 0x3c, 0x93, + 0xf7, 0xaf, 0xe7, 0x89, 0x02, 0x6b, 0x6a, 0x2c, 0x45, 0xe2, 0xfc, 0x5a, 0x00, 0x50, 0x3a, 0xa6, + 0x1a, 0x46, 0xb0, 0x9e, 0xd5, 0xb0, 0xc5, 0xba, 0x84, 0xc7, 0xaa, 0xeb, 0x86, 0xd8, 0xba, 0x17, + 0xb1, 0x3d, 0x10, 0xf2, 0xcb, 0x3e, 0xa0, 0x2e, 0xf1, 0x31, 0xd8, 0x59, 0x3a, 0x92, 0x30, 0xbf, + 0xed, 0x85, 0x24, 0x6e, 0xc9, 0xb6, 0x6e, 0x5a, 0xd1, 0x2d, 0x0f, 0x72, 0x0f, 0x55, 0xf4, 0x48, + 0x07, 0xd1, 0x23, 0x58, 0xc9, 0x26, 0x9a, 0xa9, 0xd1, 0x1d, 0xd7, 0x4d, 0x28, 0xba, 0xa5, 0x41, + 0x9e, 0x1e, 0x1a, 0xdd, 0x41, 0xb4, 0x0b, 0xe5, 0x1c, 0x5f, 0x9c, 0x8e, 0x89, 0x56, 0xb4, 0xe8, + 0xa2, 0x0c, 0x59, 0x6c, 0x9a, 0xee, 0xfc, 0x31, 0xd9, 0x3f, 0x81, 0x66, 0xee, 0xb7, 0xe1, 0x9d, + 0xfc, 0xc8, 0x52, 0x73, 0xe8, 0x66, 0xdd, 0xc5, 0xec, 0xa8, 0x3e, 0x09, 0x94, 0x53, 0x8c, 0x1b, + 0x6e, 0xc3, 0x68, 0x8e, 0xd8, 0xf2, 0xc8, 0x74, 0x9b, 0x8d, 0x3e, 0x82, 0x95, 0x7c, 0xea, 0x60, + 0xab, 0x45, 0x9d, 0x58, 0xca, 0x26, 0xf6, 0x36, 0x8b, 0xc8, 0xf0, 0x71, 0xc2, 0x61, 0xc8, 0x7c, + 0x2c, 0x29, 0x8b, 0xd3, 0xa1, 0xf9, 0xf0, 0xd5, 0x75, 0xf5, 0x83, 0xd7, 0xe8, 0xdb, 0x37, 0x34, + 0x96, 0xf9, 0xdd, 0x35, 0xfa, 0x50, 0xc8, 0x87, 0x4a, 0x9e, 0x46, 0xbb, 0x60, 0xd4, 0x09, 0x25, + 0x4d, 0x42, 0x4a, 0xb8, 0xb0, 0xa7, 0xb4, 0x15, 0x54, 0x86, 0xad, 0x40, 0xd9, 0xe1, 0xd3, 0xfe, + 0x32, 0x77, 0x3d, 0x8b, 0x9f, 0x8f, 0x09, 0x24, 0x60, 0x2b, 0x4f, 0x12, 0x90, 0x33, 0xdc, 0x09, + 0x65, 0x86, 0xc7, 0x9e, 0xd6, 0x35, 0xed, 0xbc, 0xc1, 0x2c, 0x6e, 0x66, 0x29, 0x0f, 0x0c, 0xe2, + 0x80, 0x15, 0x7d, 0x36, 0x2c, 0x60, 0x40, 0x85, 0xe4, 0xf4, 0xb4, 0x23, 0x89, 0xfd, 0x96, 0x76, + 0xe9, 0x9c, 0x26, 0x07, 0xfd, 0x28, 0xda, 0x81, 0x77, 0xf3, 0x99, 0x11, 0x0b, 0xec, 0x19, 0xdd, + 0xab, 0xb7, 0xb3, 0x29, 0x4f, 0x59, 0xe0, 0x3c, 0xb7, 0x60, 0x31, 0x5f, 0x2e, 0x7a, 0x08, 0xe5, + 0x21, 0x11, 0x3d, 0x2c, 0x04, 0x91, 0xe9, 0x68, 0x2d, 0x25, 0xb9, 0xe5, 0x0d, 0x15, 0x42, 0x5f, + 0x01, 0x64, 0xb4, 0x28, 0xbc, 0xb1, 0x16, 0x99, 0x6c, 0xe7, 0x97, 0x02, 0xac, 0x1e, 0xf5, 0xec, + 0xbe, 0xc9, 0x99, 0x24, 0xbe, 0x6a, 0x75, 0x6a, 0x0b, 0x1c, 0x36, 0x23, 0x7c, 0xe9, 0x71, 0x76, + 0x81, 0x63, 0x6f, 0x70, 0x79, 0xe4, 0xef, 0xbd, 0xd9, 0xbd, 0x7a, 0x6a, 0x0c, 0xaf, 0x3d, 0x60, + 0x6b, 0x11, 0xbe, 0x74, 0x15, 0x68, 0x9f, 0x7a, 0x70, 0x59, 0x1e, 0xc1, 0x7b, 0xff, 0xca, 0x99, + 0xea, 0xa3, 0xcb, 0x76, 0xab, 0x77, 0x03, 0x19, 0xad, 0x1e, 0xc0, 0x7c, 0xce, 0x5d, 0xcc, 0x29, + 0x9a, 0x23, 0x19, 0x4f, 0x59, 0x87, 0x59, 0x2a, 0x3c, 0xec, 0x4b, 0xda, 0x35, 0x16, 0x3b, 0xe3, + 0xce, 0x50, 0xd1, 0xd0, 0xcf, 0xce, 0xcf, 0x16, 0x6c, 0x8e, 0xd1, 0x27, 0x73, 0xfd, 0x7c, 0x0f, + 0x0f, 0xfa, 0x97, 0xc2, 0xff, 0xad, 0x53, 0x25, 0x45, 0xbe, 0xa3, 0x44, 0xe7, 0xcf, 0x02, 0xac, + 0x35, 0x39, 0xeb, 0xd2, 0x80, 0xf0, 0xfe, 0x4c, 0xaa, 0xf6, 0x19, 0xcb, 0x12, 0x50, 0x09, 0x32, + 0xbf, 0x8e, 0xb9, 0x21, 0xef, 0x67, 0xec, 0xeb, 0xc1, 0x08, 0xd7, 0xe0, 0xa6, 0x3c, 0x84, 0xea, + 0x38, 0xd2, 0x51, 0x0f, 0xdc, 0x18, 0x45, 0xc9, 0x38, 0x61, 0x03, 0x36, 0xc7, 0xc1, 0x0c, 0xfb, + 0xe1, 0xda, 0x28, 0x48, 0xdf, 0x15, 0x3f, 0x85, 0x95, 0x71, 0x10, 0xea, 0x80, 0x4e, 0xea, 0xe4, + 0xf2, 0x68, 0xb2, 0x3a, 0xa6, 0x3f, 0xdc, 0x21, 0xaa, 0xe9, 0xf7, 0x77, 0x50, 0x1a, 0x83, 0x2a, + 0x6c, 0x4b, 0x5b, 0xdf, 0xce, 0x88, 0xf5, 0xdd, 0xd9, 0x1e, 0x77, 0x69, 0x94, 0x5e, 0x38, 0xbf, + 0x59, 0xb0, 0x70, 0x7c, 0x81, 0x93, 0x2f, 0x48, 0x6f, 0xc0, 0x30, 0x94, 0x7b, 0x16, 0x28, 0x2e, + 0x70, 0xe2, 0x9d, 0x91, 0xff, 0x74, 0x2b, 0xa3, 0x14, 0x2c, 0x25, 0x49, 0x7b, 0x36, 0x2f, 0xd9, + 0x39, 0x89, 0x3d, 0xf3, 0x86, 0x6c, 0x17, 0x74, 0x2d, 0xce, 0x70, 0x2d, 0x69, 0xca, 0x89, 0x5a, + 0x6a, 0x36, 0xe7, 0xce, 0xc9, 0xc1, 0x83, 0xf3, 0x23, 0xa0, 0xd1, 0x25, 0xa8, 0x04, 0x53, 0x59, + 0x4b, 0x33, 0x0f, 0xc8, 0x85, 0x85, 0x7c, 0x35, 0xf7, 0x7b, 0x59, 0x9b, 0x13, 0x83, 0x32, 0xf6, + 0x1a, 0x2f, 0x6e, 0x2a, 0xd6, 0xcb, 0x9b, 0x8a, 0xf5, 0xf7, 0x4d, 0xc5, 0x7a, 0x7e, 0x5b, 0x99, + 0x78, 0x79, 0x5b, 0x99, 0xf8, 0xeb, 0xb6, 0x32, 0xf1, 0x6d, 0xf6, 0xc4, 0x1d, 0xd3, 0x33, 0xbf, + 0x8d, 0x69, 0x5c, 0xef, 0x7d, 0x1d, 0x5c, 0xea, 0xef, 0x03, 0x8d, 0x79, 0x3a, 0xad, 0x5f, 0xfb, + 0x3f, 0xf9, 0x27, 0x00, 0x00, 0xff, 0xff, 0x29, 0x02, 0xcc, 0x33, 0x3b, 0x0c, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -1298,9 +1297,9 @@ func (m *SwapFeeParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } { - size := m.SwapFeeRate.Size() + size := m.DefaultSwapFeeRate.Size() i -= size - if _, err := m.SwapFeeRate.MarshalTo(dAtA[i:]); err != nil { + if _, err := m.DefaultSwapFeeRate.MarshalTo(dAtA[i:]); err != nil { return 0, err } i = encodeVarintParams(dAtA, i, uint64(size)) @@ -1331,9 +1330,9 @@ func (m *SwapFeeTokenParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { var l int _ = l { - size := m.MinSwapFee.Size() + size := m.SwapFeeRate.Size() i -= size - if _, err := m.MinSwapFee.MarshalTo(dAtA[i:]); err != nil { + if _, err := m.SwapFeeRate.MarshalTo(dAtA[i:]); err != nil { return 0, err } i = encodeVarintParams(dAtA, i, uint64(size)) @@ -1565,7 +1564,7 @@ func (m *SwapFeeParams) Size() (n int) { } var l int _ = l - l = m.SwapFeeRate.Size() + l = m.DefaultSwapFeeRate.Size() n += 1 + l + sovParams(uint64(l)) if len(m.TokenParams) > 0 { for _, e := range m.TokenParams { @@ -1586,7 +1585,7 @@ func (m *SwapFeeTokenParams) Size() (n int) { if l > 0 { n += 1 + l + sovParams(uint64(l)) } - l = m.MinSwapFee.Size() + l = m.SwapFeeRate.Size() n += 1 + l + sovParams(uint64(l)) return n } @@ -3011,7 +3010,7 @@ func (m *SwapFeeParams) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SwapFeeRate", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field DefaultSwapFeeRate", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -3039,7 +3038,7 @@ func (m *SwapFeeParams) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.SwapFeeRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.DefaultSwapFeeRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -3161,7 +3160,7 @@ func (m *SwapFeeTokenParams) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field MinSwapFee", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SwapFeeRate", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -3189,7 +3188,7 @@ func (m *SwapFeeTokenParams) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.MinSwapFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.SwapFeeRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/x/clp/types/querier.pb.go b/x/clp/types/querier.pb.go index 800edbb962..61d5550ebb 100644 --- a/x/clp/types/querier.pb.go +++ b/x/clp/types/querier.pb.go @@ -1218,8 +1218,8 @@ func (m *SwapFeeParamsReq) XXX_DiscardUnknown() { var xxx_messageInfo_SwapFeeParamsReq proto.InternalMessageInfo type SwapFeeParamsRes struct { - SwapFeeRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=swap_fee_rate,json=swapFeeRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"swap_fee_rate"` - TokenParams []*SwapFeeTokenParams `protobuf:"bytes,2,rep,name=token_params,json=tokenParams,proto3" json:"token_params,omitempty"` + DefaultSwapFeeRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=default_swap_fee_rate,json=defaultSwapFeeRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"default_swap_fee_rate"` + TokenParams []*SwapFeeTokenParams `protobuf:"bytes,2,rep,name=token_params,json=tokenParams,proto3" json:"token_params,omitempty"` } func (m *SwapFeeParamsRes) Reset() { *m = SwapFeeParamsRes{} } @@ -1294,97 +1294,97 @@ func init() { func init() { proto.RegisterFile("sifnode/clp/v1/querier.proto", fileDescriptor_5f4edede314ca3fd) } var fileDescriptor_5f4edede314ca3fd = []byte{ - // 1428 bytes of a gzipped FileDescriptorProto + // 1435 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xcd, 0x6f, 0x1b, 0x45, 0x14, 0xcf, 0x26, 0x6d, 0xda, 0x4c, 0xfa, 0x91, 0x3e, 0x92, 0xd4, 0x5d, 0x1c, 0x3b, 0xac, 0x4a, - 0x1a, 0x42, 0xb2, 0x6e, 0xfa, 0x21, 0x28, 0x88, 0x43, 0xa2, 0xb6, 0xe6, 0x50, 0x50, 0xd8, 0x56, - 0x42, 0x42, 0x2a, 0xd6, 0xda, 0x9e, 0xda, 0xab, 0xae, 0xbd, 0xeb, 0x7d, 0x93, 0xb4, 0x56, 0x29, - 0x48, 0xa8, 0x07, 0x24, 0x2e, 0x48, 0xbd, 0xa3, 0x1e, 0xe0, 0xc0, 0xa1, 0x12, 0x07, 0xfe, 0x05, - 0xa4, 0x22, 0x21, 0x51, 0x89, 0x0b, 0x70, 0x28, 0xa8, 0xe5, 0xd0, 0x3f, 0x03, 0xcd, 0xec, 0xac, - 0xd7, 0xfb, 0x65, 0x3b, 0x51, 0x05, 0xe2, 0x64, 0xef, 0xbc, 0xdf, 0xbc, 0xf7, 0x7b, 0xbf, 0x79, - 0x3b, 0xef, 0xd9, 0x24, 0x8f, 0xd6, 0x8d, 0xb6, 0x53, 0xa7, 0xa5, 0x9a, 0xed, 0x96, 0x76, 0xd6, - 0x4b, 0x9d, 0x6d, 0xea, 0x59, 0xd4, 0xd3, 0x5d, 0xcf, 0x61, 0x0e, 0x1c, 0x91, 0x56, 0xbd, 0x66, - 0xbb, 0xfa, 0xce, 0xba, 0x3a, 0xdb, 0x70, 0x1a, 0x8e, 0x30, 0x95, 0xf8, 0x37, 0x1f, 0xa5, 0xaa, - 0x31, 0x1f, 0xac, 0xeb, 0x52, 0x94, 0xb6, 0x97, 0x63, 0x36, 0xd7, 0xf4, 0xcc, 0x56, 0x60, 0x5c, - 0xa9, 0x39, 0xd8, 0x72, 0xb0, 0x54, 0x35, 0x91, 0x8a, 0xc8, 0xdd, 0xd2, 0xce, 0x7a, 0x95, 0x32, - 0x93, 0xe3, 0x1a, 0x56, 0xdb, 0x64, 0x96, 0xd3, 0x96, 0xd8, 0x7c, 0xc3, 0x71, 0x1a, 0x36, 0x2d, - 0x99, 0xae, 0x55, 0x32, 0xdb, 0x6d, 0x87, 0x09, 0xa3, 0xf4, 0xa4, 0xbd, 0x4e, 0x0e, 0x6c, 0x39, - 0x8e, 0x6d, 0xd0, 0x0e, 0xcc, 0x93, 0x49, 0xec, 0xb6, 0xaa, 0x8e, 0x9d, 0x53, 0x16, 0x95, 0xe5, - 0x29, 0x43, 0x3e, 0xbd, 0x75, 0xf0, 0x8b, 0x07, 0xc5, 0xb1, 0xe7, 0x0f, 0x8a, 0x63, 0x5a, 0x37, - 0x00, 0x23, 0x2c, 0x93, 0x7d, 0xae, 0x23, 0xa1, 0xd3, 0x67, 0x66, 0xf5, 0x68, 0xbe, 0xba, 0x80, - 0x09, 0x04, 0xac, 0x12, 0xa8, 0xd9, 0x6e, 0xa5, 0xe5, 0xd4, 0xb7, 0x6d, 0x5a, 0x31, 0xeb, 0x75, - 0x8f, 0x22, 0xe6, 0xc6, 0x45, 0x88, 0x99, 0x9a, 0xed, 0xbe, 0x27, 0x0c, 0x1b, 0xfe, 0x3a, 0x27, - 0xd1, 0xa4, 0x56, 0xa3, 0xc9, 0x72, 0x13, 0x8b, 0xca, 0xf2, 0x84, 0x21, 0x9f, 0x34, 0x83, 0x1c, - 0xe4, 0x3e, 0x91, 0x13, 0xbd, 0x4c, 0x48, 0x98, 0xa5, 0x64, 0xb0, 0xa4, 0xfb, 0x92, 0xe8, 0x5c, - 0x12, 0x5d, 0x48, 0xa2, 0x4b, 0x49, 0xf4, 0x2d, 0xb3, 0x41, 0x0d, 0xda, 0xd9, 0xa6, 0xc8, 0x8c, - 0xbe, 0x9d, 0xda, 0x8f, 0x4a, 0xcf, 0x29, 0xc2, 0x0a, 0xd9, 0xcf, 0xe9, 0x62, 0x4e, 0x59, 0x9c, - 0xc8, 0xcc, 0xc8, 0x87, 0xbc, 0x98, 0x94, 0xa0, 0x1c, 0x49, 0x63, 0x9f, 0x48, 0xe3, 0xd4, 0xd0, - 0x34, 0xd0, 0x75, 0xda, 0x48, 0x23, 0x79, 0x7c, 0x48, 0x66, 0xaf, 0x58, 0x9d, 0x6d, 0xab, 0x6e, - 0xb1, 0xee, 0x96, 0xe7, 0xec, 0x58, 0x75, 0xea, 0x0d, 0x38, 0x50, 0x58, 0x20, 0xc4, 0x76, 0x63, - 0xb4, 0xa7, 0x6c, 0x57, 0xf2, 0xed, 0x3b, 0xef, 0xe7, 0x4a, 0xaa, 0x67, 0x84, 0x2d, 0x02, 0x76, - 0xb0, 0x5e, 0x71, 0xa5, 0x41, 0x9e, 0xc4, 0x2b, 0x71, 0xe5, 0x92, 0x1e, 0x8e, 0xd9, 0xf1, 0x25, - 0x38, 0x4d, 0x66, 0x79, 0x36, 0x3b, 0xb4, 0x62, 0x22, 0x52, 0x56, 0xa9, 0x9a, 0xb6, 0xd9, 0xae, - 0x51, 0xc9, 0x0e, 0x7c, 0xdb, 0x06, 0x37, 0x6d, 0xfa, 0x16, 0x38, 0x47, 0xe6, 0xe9, 0x6d, 0x46, - 0xbd, 0xb6, 0x69, 0xc7, 0xf6, 0x4c, 0x88, 0x3d, 0xb3, 0x81, 0x35, 0xb2, 0x2b, 0x3c, 0x8c, 0x7d, - 0x91, 0xfa, 0xfa, 0x8c, 0x1c, 0x12, 0xb8, 0x2b, 0x16, 0x32, 0xae, 0x5d, 0x54, 0x23, 0x25, 0xa6, - 0x51, 0xac, 0x04, 0xc7, 0xf7, 0x5a, 0x82, 0x7d, 0x5a, 0x7f, 0xad, 0x44, 0x18, 0x20, 0xac, 0x91, - 0x49, 0x91, 0x56, 0x50, 0x91, 0x73, 0x71, 0x5d, 0x05, 0xda, 0x90, 0xa0, 0xbe, 0xc4, 0xc6, 0x07, - 0x54, 0xd9, 0xc4, 0xde, 0xab, 0xec, 0x4b, 0x85, 0xe4, 0x12, 0x47, 0x79, 0xd1, 0x64, 0xe6, 0x7f, - 0x22, 0xd7, 0xef, 0xd9, 0x6c, 0x10, 0xae, 0x93, 0xe3, 0xc9, 0xf2, 0xac, 0xd4, 0x4d, 0x66, 0x4a, - 0x2d, 0x5f, 0x1d, 0x5a, 0xa3, 0xc2, 0xd5, 0x9c, 0x9d, 0xb6, 0x9c, 0x29, 0xf5, 0xe5, 0x14, 0xa9, - 0xf7, 0x72, 0x2f, 0xdd, 0x4b, 0xcb, 0x2d, 0x28, 0xcc, 0xac, 0x97, 0xfa, 0xc5, 0x4b, 0xfc, 0x4b, - 0x36, 0x0d, 0x04, 0x83, 0xbc, 0x94, 0x94, 0x38, 0x28, 0xd5, 0x11, 0xae, 0x00, 0x48, 0x48, 0xfb, - 0x2f, 0x94, 0xb0, 0x45, 0xe6, 0x12, 0x4c, 0x52, 0x3a, 0xca, 0x8b, 0x10, 0xef, 0x67, 0x25, 0x3d, - 0xd6, 0xff, 0x54, 0xb9, 0x69, 0x32, 0xb5, 0x25, 0x06, 0x10, 0x83, 0x76, 0xb4, 0x7b, 0xe3, 0xe1, - 0x13, 0x82, 0x4e, 0x26, 0xfd, 0xd9, 0x44, 0xde, 0xff, 0xf3, 0x89, 0xce, 0xe9, 0x43, 0x25, 0x0a, - 0xae, 0x13, 0xc0, 0x6e, 0xab, 0x45, 0x99, 0xd7, 0xad, 0xb0, 0xa6, 0x47, 0xb1, 0xe9, 0xd8, 0x75, - 0xff, 0x9e, 0xdf, 0xd4, 0x1f, 0x3d, 0x29, 0x8e, 0xfd, 0xf1, 0xa4, 0xb8, 0xd4, 0xb0, 0x58, 0x73, - 0xbb, 0xaa, 0xd7, 0x9c, 0x56, 0x49, 0x8e, 0x3a, 0xfe, 0xc7, 0x1a, 0xd6, 0x6f, 0xca, 0x31, 0xe9, - 0x22, 0xad, 0x19, 0xc7, 0x02, 0x4f, 0xd7, 0x02, 0x47, 0xd0, 0x24, 0xb9, 0x9e, 0x7b, 0x8f, 0x93, - 0xef, 0x0b, 0x32, 0xb1, 0xa7, 0x20, 0xf3, 0x81, 0x3f, 0x83, 0xbb, 0xeb, 0x45, 0xd2, 0x8e, 0x91, - 0xa3, 0x06, 0xbd, 0x65, 0x7a, 0xf5, 0x50, 0x99, 0x72, 0x7c, 0x09, 0xe1, 0x5c, 0x4c, 0x9e, 0x7c, - 0x5c, 0x9e, 0xc8, 0x06, 0x89, 0xd5, 0x8e, 0x92, 0xc3, 0x5b, 0x2d, 0xe6, 0x86, 0x9e, 0xff, 0x54, - 0xa2, 0x2b, 0x08, 0x67, 0x62, 0x8e, 0xd5, 0x84, 0xee, 0x21, 0x3c, 0xd0, 0xfe, 0x5d, 0x32, 0xe3, - 0xb6, 0x98, 0xcb, 0x85, 0xa1, 0x15, 0xb9, 0xdb, 0xaf, 0xf6, 0x42, 0xda, 0x6e, 0xc3, 0x64, 0x54, - 0x7a, 0x38, 0xe2, 0x46, 0x9e, 0xe1, 0x4d, 0x42, 0x84, 0x27, 0xea, 0x3a, 0xb5, 0xa6, 0xac, 0xac, - 0x13, 0x69, 0x3e, 0x2e, 0x71, 0x80, 0x31, 0xe5, 0x06, 0x5f, 0x33, 0x3b, 0x70, 0x81, 0xe4, 0xfb, - 0x8b, 0x9d, 0xd1, 0x1a, 0xaf, 0xbc, 0x50, 0x81, 0x9f, 0x94, 0x81, 0x00, 0x84, 0x8d, 0x98, 0x20, - 0xaf, 0x0d, 0x7a, 0x97, 0xa2, 0xbb, 0x03, 0x7d, 0xde, 0x27, 0xd3, 0x49, 0x69, 0xd6, 0x46, 0xf0, - 0xd3, 0xa7, 0x14, 0xf1, 0x42, 0x95, 0xb2, 0xa6, 0xd9, 0x22, 0x59, 0xe8, 0x75, 0x14, 0x0b, 0x99, - 0x67, 0x55, 0xb7, 0xa3, 0xc9, 0xd6, 0x06, 0x03, 0x10, 0x36, 0x63, 0xc9, 0xae, 0x24, 0xb4, 0xcf, - 0xde, 0x1e, 0x14, 0x19, 0x90, 0x99, 0xab, 0xb7, 0x4c, 0xf7, 0x32, 0xa5, 0x61, 0xe0, 0x87, 0x4a, - 0x62, 0x91, 0x5f, 0x59, 0x87, 0xf1, 0x96, 0xe9, 0x56, 0x6e, 0x50, 0x2a, 0x4a, 0xc7, 0x6f, 0x3d, - 0xbb, 0x7e, 0x91, 0xa6, 0xd1, 0x77, 0xcc, 0xc5, 0x82, 0x4b, 0xe4, 0x10, 0x73, 0x6e, 0xd2, 0x76, - 0xa8, 0x35, 0xbf, 0xff, 0xb4, 0x78, 0x1a, 0x92, 0xcb, 0x35, 0x0e, 0x95, 0x84, 0xa6, 0x59, 0xf8, - 0x70, 0xe6, 0x9b, 0x23, 0x64, 0xff, 0x07, 0xfc, 0x0e, 0x83, 0x1a, 0x39, 0x50, 0xa6, 0x8c, 0x8f, - 0xe9, 0x70, 0x3c, 0x75, 0x78, 0xa7, 0x1d, 0x35, 0xc3, 0x80, 0xda, 0xd2, 0xe7, 0xbf, 0xfe, 0x7d, - 0x7f, 0x7c, 0x11, 0x0a, 0x25, 0xb4, 0x6e, 0xd4, 0x9a, 0xa6, 0xd5, 0xee, 0xfd, 0xee, 0x72, 0x1c, - 0xbb, 0x74, 0xc7, 0x6f, 0xb2, 0x77, 0xe1, 0x63, 0x72, 0x50, 0x06, 0x41, 0xc8, 0xa5, 0x39, 0xe3, - 0x22, 0xaa, 0x59, 0x16, 0xd4, 0x0a, 0x22, 0x4e, 0x0e, 0xe6, 0x53, 0xe3, 0x20, 0x7c, 0xab, 0x90, - 0xd9, 0x32, 0x9f, 0x01, 0xe3, 0xf3, 0xf1, 0xc9, 0xe1, 0x8d, 0x81, 0x76, 0xd4, 0x51, 0x50, 0xa8, - 0x6d, 0x08, 0x12, 0x6f, 0xc3, 0x85, 0x04, 0x89, 0x64, 0x63, 0xea, 0xa5, 0x5e, 0xba, 0x13, 0x0e, - 0x78, 0x77, 0xe1, 0xa1, 0x42, 0x72, 0x69, 0x3c, 0xc5, 0x7c, 0xb4, 0x3c, 0xda, 0x74, 0x45, 0x3b, - 0xea, 0xa8, 0x48, 0xd4, 0xde, 0x11, 0x9c, 0xdf, 0x80, 0xf3, 0x23, 0x70, 0x16, 0x93, 0x5e, 0x94, - 0xef, 0x27, 0xe4, 0x50, 0x99, 0xb2, 0xde, 0x7c, 0x0d, 0xf9, 0xd4, 0x61, 0x5a, 0xce, 0x58, 0xea, - 0x20, 0x2b, 0x6a, 0xa7, 0x05, 0x95, 0x15, 0x58, 0x4e, 0x50, 0xf1, 0x7f, 0x86, 0xd8, 0x16, 0xb2, - 0x68, 0xf4, 0xfb, 0x0a, 0x99, 0x4b, 0x53, 0x0b, 0x61, 0xf8, 0x20, 0x2a, 0x0a, 0x6a, 0x24, 0x18, - 0x6a, 0xab, 0x82, 0xd9, 0x12, 0x9c, 0x1c, 0x41, 0x24, 0x84, 0xef, 0x32, 0xce, 0x50, 0x08, 0x34, - 0xfc, 0x64, 0x02, 0xb1, 0x46, 0x45, 0xa2, 0x76, 0x41, 0xd0, 0x3b, 0x0b, 0xeb, 0xa3, 0x9c, 0xa1, - 0xaf, 0x62, 0xf0, 0xde, 0x55, 0xc9, 0x14, 0x7f, 0xef, 0xfc, 0x5b, 0xf5, 0x44, 0xc6, 0x84, 0x41, - 0x3b, 0x6a, 0xa6, 0x09, 0xb5, 0xa2, 0x88, 0x7e, 0x02, 0x8e, 0x27, 0x5f, 0x3d, 0xdf, 0xed, 0x1d, - 0x72, 0xb4, 0x4c, 0x59, 0x7f, 0x3b, 0x86, 0xe2, 0xc0, 0x66, 0x4d, 0x3b, 0xea, 0x10, 0xc0, 0xa0, - 0x8b, 0xc5, 0x13, 0x48, 0x79, 0xfd, 0x01, 0x92, 0xc3, 0x3c, 0xc1, 0x5e, 0xcb, 0x86, 0x85, 0x01, - 0xed, 0x9c, 0x76, 0xd4, 0x81, 0x66, 0xd4, 0x4e, 0x8a, 0xb0, 0x05, 0xc8, 0x27, 0x93, 0xe5, 0x5d, - 0x5b, 0x06, 0xfd, 0x5e, 0x21, 0xf9, 0x58, 0x05, 0x44, 0xfa, 0x22, 0xac, 0x8e, 0xde, 0x42, 0x69, - 0x47, 0xdd, 0x0d, 0x1a, 0xb5, 0x73, 0x82, 0xa2, 0x0e, 0xab, 0x83, 0xab, 0x41, 0xee, 0x0b, 0x28, - 0xff, 0xa0, 0x90, 0x05, 0x2e, 0x54, 0x66, 0x77, 0x83, 0xb5, 0x5d, 0x74, 0x42, 0xda, 0x51, 0x77, - 0x05, 0x47, 0xed, 0xbc, 0x60, 0x5d, 0x82, 0xb5, 0xa4, 0xb0, 0xbd, 0xdb, 0xa7, 0x6f, 0x63, 0x40, - 0xfb, 0x53, 0x32, 0x53, 0xa6, 0x2c, 0xd2, 0x58, 0x61, 0x31, 0xa3, 0xd7, 0x85, 0xdc, 0x86, 0x21, - 0x06, 0x95, 0x57, 0xa4, 0x61, 0x6f, 0x6e, 0x3c, 0x7a, 0x5a, 0x50, 0x1e, 0x3f, 0x2d, 0x28, 0x7f, - 0x3d, 0x2d, 0x28, 0x5f, 0x3d, 0x2b, 0x8c, 0x3d, 0x7e, 0x56, 0x18, 0xfb, 0xed, 0x59, 0x61, 0xec, - 0xa3, 0x53, 0x7d, 0xcd, 0xfb, 0x6a, 0xe0, 0x23, 0xf8, 0xef, 0xf1, 0xb6, 0xf0, 0x26, 0x3a, 0x78, - 0x75, 0x52, 0xfc, 0x61, 0x78, 0xf6, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x5f, 0x5b, 0x92, 0xf0, - 0xf9, 0x14, 0x00, 0x00, + 0x1a, 0x42, 0xb2, 0xdb, 0xf4, 0x43, 0x50, 0x10, 0x87, 0x44, 0x6d, 0xcd, 0xa1, 0xa0, 0xb0, 0xad, + 0x84, 0x84, 0x54, 0xac, 0xf5, 0x7a, 0x62, 0xaf, 0xba, 0xf6, 0x7e, 0xcc, 0x38, 0xad, 0x55, 0x0a, + 0x12, 0xea, 0x01, 0x89, 0x0b, 0x52, 0xef, 0xa8, 0x07, 0x38, 0x70, 0x40, 0xe2, 0xc0, 0x91, 0x2b, + 0x52, 0x91, 0x90, 0xa8, 0xc4, 0x05, 0x38, 0x14, 0xd4, 0x72, 0xe8, 0x9f, 0x81, 0x66, 0x76, 0xd6, + 0xeb, 0xfd, 0xb2, 0x9d, 0x28, 0x02, 0x71, 0xb2, 0x77, 0xde, 0x6f, 0xde, 0xfb, 0xbd, 0xdf, 0xbc, + 0x9d, 0xf7, 0x6c, 0x54, 0x24, 0xd6, 0x76, 0xdb, 0xa9, 0x63, 0xcd, 0xb4, 0x5d, 0x6d, 0x67, 0x5d, + 0xf3, 0x3a, 0xd8, 0xb7, 0xb0, 0xaf, 0xba, 0xbe, 0x43, 0x1d, 0x38, 0x26, 0xac, 0xaa, 0x69, 0xbb, + 0xea, 0xce, 0xba, 0x3c, 0xdb, 0x70, 0x1a, 0x0e, 0x37, 0x69, 0xec, 0x5b, 0x80, 0x92, 0xe5, 0x84, + 0x0f, 0xda, 0x75, 0x31, 0x11, 0xb6, 0x17, 0x13, 0x36, 0xd7, 0xf0, 0x8d, 0x56, 0x68, 0x5c, 0x31, + 0x1d, 0xd2, 0x72, 0x88, 0x56, 0x33, 0x08, 0xe6, 0x91, 0xbb, 0xda, 0xce, 0x7a, 0x0d, 0x53, 0x83, + 0xe1, 0x1a, 0x56, 0xdb, 0xa0, 0x96, 0xd3, 0x16, 0xd8, 0x62, 0xc3, 0x71, 0x1a, 0x36, 0xd6, 0x0c, + 0xd7, 0xd2, 0x8c, 0x76, 0xdb, 0xa1, 0xdc, 0x28, 0x3c, 0x29, 0xaf, 0xa2, 0x43, 0x5b, 0x8e, 0x63, + 0xeb, 0xd8, 0x83, 0x79, 0x34, 0x49, 0xba, 0xad, 0x9a, 0x63, 0x17, 0xa4, 0x45, 0x69, 0x79, 0x4a, + 0x17, 0x4f, 0x6f, 0x1c, 0xfe, 0xec, 0x61, 0x79, 0xec, 0xf9, 0xc3, 0xf2, 0x98, 0xd2, 0x0d, 0xc1, + 0x04, 0x96, 0xd1, 0x01, 0xd7, 0x11, 0xd0, 0xe9, 0x73, 0xb3, 0x6a, 0x3c, 0x5f, 0x95, 0xc3, 0x38, + 0x02, 0x56, 0x11, 0x98, 0xb6, 0x5b, 0x6d, 0x39, 0xf5, 0x8e, 0x8d, 0xab, 0x46, 0xbd, 0xee, 0x63, + 0x42, 0x0a, 0xe3, 0x3c, 0xc4, 0x8c, 0x69, 0xbb, 0xef, 0x70, 0xc3, 0x46, 0xb0, 0xce, 0x48, 0x34, + 0xb1, 0xd5, 0x68, 0xd2, 0xc2, 0xc4, 0xa2, 0xb4, 0x3c, 0xa1, 0x8b, 0x27, 0x45, 0x47, 0x87, 0x99, + 0x4f, 0xc2, 0x88, 0x5e, 0x45, 0x28, 0xca, 0x52, 0x30, 0x58, 0x52, 0x03, 0x49, 0x54, 0x26, 0x89, + 0xca, 0x25, 0x51, 0x85, 0x24, 0xea, 0x96, 0xd1, 0xc0, 0x3a, 0xf6, 0x3a, 0x98, 0x50, 0xbd, 0x6f, + 0xa7, 0xf2, 0xa3, 0xd4, 0x73, 0x4a, 0x60, 0x05, 0x1d, 0x64, 0x74, 0x49, 0x41, 0x5a, 0x9c, 0xc8, + 0xcd, 0x28, 0x80, 0xec, 0x4f, 0x4a, 0x50, 0x89, 0xa5, 0x71, 0x80, 0xa7, 0x71, 0x66, 0x68, 0x1a, + 0xc4, 0x75, 0xda, 0x04, 0xc7, 0xf2, 0x78, 0x1f, 0xcd, 0x5e, 0xb3, 0xbc, 0x8e, 0x55, 0xb7, 0x68, + 0x77, 0xcb, 0x77, 0x76, 0xac, 0x3a, 0xf6, 0x07, 0x1c, 0x28, 0x2c, 0x20, 0x64, 0xbb, 0x09, 0xda, + 0x53, 0xb6, 0x2b, 0xf8, 0xf6, 0x9d, 0xf7, 0x73, 0x29, 0xd3, 0x33, 0x81, 0x2d, 0x04, 0x76, 0xb8, + 0x5e, 0x75, 0x85, 0x41, 0x9c, 0xc4, 0x4b, 0x49, 0xe5, 0xd2, 0x1e, 0x4e, 0xd8, 0xc9, 0x25, 0x38, + 0x8b, 0x66, 0x59, 0x36, 0x3b, 0xb8, 0x6a, 0x10, 0x82, 0x69, 0xb5, 0x66, 0xd8, 0x46, 0xdb, 0xc4, + 0x82, 0x1d, 0x04, 0xb6, 0x0d, 0x66, 0xda, 0x0c, 0x2c, 0x70, 0x01, 0xcd, 0xe3, 0x3b, 0x14, 0xfb, + 0x6d, 0xc3, 0x4e, 0xec, 0x99, 0xe0, 0x7b, 0x66, 0x43, 0x6b, 0x6c, 0x57, 0x74, 0x18, 0x07, 0x62, + 0xf5, 0xf5, 0x09, 0x3a, 0xc2, 0x71, 0xd7, 0x2c, 0x42, 0x99, 0x76, 0x71, 0x8d, 0xa4, 0x84, 0x46, + 0x89, 0x12, 0x1c, 0xdf, 0x6b, 0x09, 0xf6, 0x69, 0xfd, 0xa5, 0x14, 0x63, 0x40, 0x60, 0x0d, 0x4d, + 0xf2, 0xb4, 0xc2, 0x8a, 0x9c, 0x4b, 0xea, 0xca, 0xd1, 0xba, 0x00, 0xf5, 0x25, 0x36, 0x3e, 0xa0, + 0xca, 0x26, 0xf6, 0x5e, 0x65, 0x9f, 0x4b, 0xa8, 0x90, 0x3a, 0xca, 0xcb, 0x06, 0x35, 0xfe, 0x13, + 0xb9, 0x7e, 0xcf, 0x67, 0x43, 0xe0, 0x26, 0x3a, 0x99, 0x2e, 0xcf, 0x6a, 0xdd, 0xa0, 0x86, 0xd0, + 0xf2, 0xe5, 0xa1, 0x35, 0xca, 0x5d, 0xcd, 0xd9, 0x59, 0xcb, 0xb9, 0x52, 0x5f, 0xcd, 0x90, 0x7a, + 0x2f, 0xf7, 0xd2, 0xfd, 0xac, 0xdc, 0xc2, 0xc2, 0xcc, 0x7b, 0xa9, 0xf7, 0x5f, 0xe2, 0x5f, 0xf2, + 0x69, 0x10, 0xd0, 0xd1, 0x0b, 0x69, 0x89, 0xc3, 0x52, 0x1d, 0xe1, 0x0a, 0x80, 0x94, 0xb4, 0xff, + 0x42, 0x09, 0x5b, 0x68, 0x2e, 0xc5, 0x24, 0xa3, 0xa3, 0xec, 0x87, 0x78, 0x3f, 0x4b, 0xd9, 0xb1, + 0xfe, 0xa7, 0xca, 0x4d, 0xa3, 0xa9, 0x2d, 0x3e, 0x80, 0xe8, 0xd8, 0x53, 0xee, 0x8f, 0x47, 0x4f, + 0x04, 0x54, 0x34, 0x19, 0xcc, 0x26, 0xe2, 0xfe, 0x9f, 0x4f, 0x75, 0xce, 0x00, 0x2a, 0x50, 0x70, + 0x13, 0x01, 0xe9, 0xb6, 0x5a, 0x98, 0xfa, 0xdd, 0x2a, 0x6d, 0xfa, 0x98, 0x34, 0x1d, 0xbb, 0x1e, + 0xdc, 0xf3, 0x9b, 0xea, 0xa3, 0x27, 0xe5, 0xb1, 0x3f, 0x9e, 0x94, 0x97, 0x1a, 0x16, 0x6d, 0x76, + 0x6a, 0xaa, 0xe9, 0xb4, 0x34, 0x31, 0xea, 0x04, 0x1f, 0x6b, 0xa4, 0x7e, 0x4b, 0x8c, 0x49, 0x97, + 0xb1, 0xa9, 0x9f, 0x08, 0x3d, 0xdd, 0x08, 0x1d, 0x41, 0x13, 0x15, 0x7a, 0xee, 0x7d, 0x46, 0xbe, + 0x2f, 0xc8, 0xc4, 0x9e, 0x82, 0xcc, 0x87, 0xfe, 0x74, 0xe6, 0xae, 0x17, 0x49, 0x39, 0x81, 0x8e, + 0xeb, 0xf8, 0xb6, 0xe1, 0xd7, 0x23, 0x65, 0x2a, 0xc9, 0x25, 0x02, 0x17, 0x12, 0xf2, 0x14, 0x93, + 0xf2, 0xc4, 0x36, 0x08, 0xac, 0x72, 0x1c, 0x1d, 0xdd, 0x6a, 0x51, 0x37, 0xf2, 0xfc, 0xa7, 0x14, + 0x5f, 0x21, 0x70, 0x2e, 0xe1, 0x58, 0x4e, 0xe9, 0x1e, 0xc1, 0x43, 0xed, 0xdf, 0x46, 0x33, 0x6e, + 0x8b, 0xba, 0x4c, 0x18, 0x5c, 0x15, 0xbb, 0x83, 0x6a, 0x2f, 0x65, 0xed, 0xd6, 0x0d, 0x8a, 0x85, + 0x87, 0x63, 0x6e, 0xec, 0x19, 0x5e, 0x47, 0x88, 0x7b, 0xc2, 0xae, 0x63, 0x36, 0x45, 0x65, 0x9d, + 0xca, 0xf2, 0x71, 0x85, 0x01, 0xf4, 0x29, 0x37, 0xfc, 0x9a, 0xdb, 0x81, 0x4b, 0xa8, 0xd8, 0x5f, + 0xec, 0x14, 0x9b, 0xac, 0xf2, 0x22, 0x05, 0x7e, 0x92, 0x06, 0x02, 0x08, 0x6c, 0x24, 0x04, 0x79, + 0x65, 0xd0, 0xbb, 0x14, 0xdf, 0x1d, 0xea, 0xf3, 0x2e, 0x9a, 0x4e, 0x4b, 0xb3, 0x36, 0x82, 0x9f, + 0x3e, 0xa5, 0x90, 0x1f, 0xa9, 0x94, 0x37, 0xcd, 0x96, 0xd1, 0x42, 0xaf, 0xa3, 0x58, 0x84, 0xfa, + 0x56, 0xad, 0x13, 0x4f, 0xd6, 0x1c, 0x0c, 0x20, 0xb0, 0x99, 0x48, 0x76, 0x25, 0xa5, 0x7d, 0xfe, + 0xf6, 0xb0, 0xc8, 0x00, 0xcd, 0x5c, 0xbf, 0x6d, 0xb8, 0x57, 0x31, 0x8e, 0x02, 0xff, 0x20, 0xa5, + 0x16, 0x09, 0x18, 0x68, 0xae, 0x8e, 0xb7, 0x8d, 0x8e, 0x4d, 0xab, 0xe4, 0xb6, 0xe1, 0x56, 0xb7, + 0x31, 0xe6, 0x25, 0x14, 0xb4, 0xa0, 0x5d, 0xbf, 0x50, 0x20, 0x9c, 0x89, 0x38, 0x4c, 0x3b, 0xb8, + 0x82, 0x8e, 0x50, 0xe7, 0x16, 0x6e, 0x47, 0xd2, 0xb3, 0xeb, 0x50, 0x49, 0x66, 0x25, 0xb6, 0xdc, + 0x60, 0x50, 0xc1, 0x6f, 0x9a, 0x46, 0x0f, 0xe7, 0xbe, 0x3a, 0x86, 0x0e, 0xbe, 0xc7, 0xae, 0x34, + 0x30, 0xd1, 0xa1, 0x0a, 0xa6, 0x6c, 0x6a, 0x87, 0x93, 0x99, 0xb3, 0x3c, 0xf6, 0xe4, 0x1c, 0x03, + 0x51, 0x96, 0x3e, 0xfd, 0xf5, 0xef, 0x07, 0xe3, 0x8b, 0x50, 0xd2, 0x88, 0xb5, 0x6d, 0x36, 0x0d, + 0xab, 0xdd, 0xfb, 0x19, 0xe6, 0x38, 0xb6, 0x76, 0x37, 0xe8, 0xb9, 0xf7, 0xe0, 0x43, 0x74, 0x58, + 0x04, 0x21, 0x50, 0xc8, 0x72, 0xc6, 0x34, 0x95, 0xf3, 0x2c, 0x44, 0x29, 0xf1, 0x38, 0x05, 0x98, + 0xcf, 0x8c, 0x43, 0xe0, 0x6b, 0x09, 0xcd, 0x56, 0xd8, 0x48, 0x98, 0x1c, 0x97, 0x4f, 0x0f, 0xef, + 0x13, 0xd8, 0x93, 0x47, 0x41, 0x11, 0x65, 0x83, 0x93, 0x78, 0x13, 0x2e, 0xa5, 0x48, 0xa4, 0xfb, + 0x54, 0x2f, 0x75, 0xed, 0x6e, 0x34, 0xef, 0xdd, 0x83, 0x6f, 0x25, 0x54, 0xc8, 0xe2, 0xc9, 0xc7, + 0xa5, 0xe5, 0xd1, 0x86, 0x2d, 0xec, 0xc9, 0xa3, 0x22, 0x89, 0xf2, 0x16, 0xe7, 0xfc, 0x1a, 0x5c, + 0x1c, 0x81, 0x33, 0x1f, 0xfc, 0xe2, 0x7c, 0x3f, 0x42, 0x47, 0x2a, 0x98, 0xf6, 0xc6, 0x6d, 0x28, + 0x66, 0xce, 0xd6, 0x62, 0xe4, 0x92, 0x07, 0x59, 0x89, 0x72, 0x96, 0x53, 0x59, 0x81, 0xe5, 0x14, + 0x95, 0xe0, 0x57, 0x89, 0x6d, 0x11, 0x1a, 0x8f, 0xfe, 0x40, 0x42, 0x73, 0x59, 0x6a, 0x11, 0x18, + 0x3e, 0x97, 0xf2, 0x82, 0x1a, 0x09, 0x46, 0x94, 0x55, 0xce, 0x6c, 0x09, 0x4e, 0x8f, 0x20, 0x12, + 0x81, 0x6f, 0x72, 0xce, 0x90, 0x0b, 0x34, 0xfc, 0x64, 0x42, 0xb1, 0x46, 0x45, 0x12, 0xe5, 0x12, + 0xa7, 0x77, 0x1e, 0xd6, 0x47, 0x39, 0xc3, 0x40, 0xc5, 0xf0, 0xbd, 0xab, 0xa1, 0x29, 0xf6, 0xde, + 0x05, 0x97, 0xec, 0xa9, 0x9c, 0x81, 0x03, 0x7b, 0x72, 0xae, 0x89, 0x28, 0x65, 0x1e, 0xfd, 0x14, + 0x9c, 0x4c, 0xbf, 0x7a, 0x81, 0xdb, 0xbb, 0xe8, 0x78, 0x05, 0xd3, 0xfe, 0xee, 0x0c, 0xe5, 0x81, + 0xbd, 0x1b, 0x7b, 0xf2, 0x10, 0xc0, 0xa0, 0x8b, 0xc5, 0xe7, 0x48, 0x71, 0xfd, 0x01, 0x41, 0x47, + 0x59, 0x82, 0xbd, 0x0e, 0x0e, 0x0b, 0x03, 0xba, 0x3b, 0xf6, 0xe4, 0x81, 0x66, 0xa2, 0x9c, 0xe6, + 0x61, 0x4b, 0x50, 0x4c, 0x27, 0xcb, 0x9a, 0xb8, 0x08, 0xfa, 0x9d, 0x84, 0x8a, 0x89, 0x0a, 0x88, + 0xb5, 0x49, 0x58, 0x1d, 0xbd, 0xa3, 0x62, 0x4f, 0xde, 0x0d, 0x9a, 0x28, 0x17, 0x38, 0x45, 0x15, + 0x56, 0x07, 0x57, 0x83, 0xd8, 0x17, 0x52, 0xfe, 0x5e, 0x42, 0x0b, 0x4c, 0xa8, 0xdc, 0x66, 0x07, + 0x6b, 0xbb, 0x68, 0x8c, 0xd8, 0x93, 0x77, 0x05, 0x27, 0xca, 0x45, 0xce, 0x5a, 0x83, 0xb5, 0xb4, + 0xb0, 0xbd, 0xdb, 0xa7, 0x6f, 0x63, 0x48, 0xfb, 0x63, 0x34, 0x53, 0xc1, 0x34, 0xd6, 0x67, 0x61, + 0x31, 0xa7, 0xd7, 0x45, 0xdc, 0x86, 0x21, 0x06, 0x95, 0x57, 0xac, 0x6f, 0x6f, 0x6e, 0x3c, 0x7a, + 0x5a, 0x92, 0x1e, 0x3f, 0x2d, 0x49, 0x7f, 0x3d, 0x2d, 0x49, 0x5f, 0x3c, 0x2b, 0x8d, 0x3d, 0x7e, + 0x56, 0x1a, 0xfb, 0xed, 0x59, 0x69, 0xec, 0x83, 0x33, 0x7d, 0x3d, 0xfc, 0x7a, 0xe8, 0x23, 0xfc, + 0x2b, 0xf2, 0x0e, 0xf7, 0xc6, 0x1b, 0x79, 0x6d, 0x92, 0xff, 0x7f, 0x78, 0xfe, 0x9f, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x9e, 0xf8, 0x43, 0x25, 0x08, 0x15, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2954,9 +2954,9 @@ func (m *SwapFeeParamsRes) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } { - size := m.SwapFeeRate.Size() + size := m.DefaultSwapFeeRate.Size() i -= size - if _, err := m.SwapFeeRate.MarshalTo(dAtA[i:]); err != nil { + if _, err := m.DefaultSwapFeeRate.MarshalTo(dAtA[i:]); err != nil { return 0, err } i = encodeVarintQuerier(dAtA, i, uint64(size)) @@ -3389,7 +3389,7 @@ func (m *SwapFeeParamsRes) Size() (n int) { } var l int _ = l - l = m.SwapFeeRate.Size() + l = m.DefaultSwapFeeRate.Size() n += 1 + l + sovQuerier(uint64(l)) if len(m.TokenParams) > 0 { for _, e := range m.TokenParams { @@ -6136,7 +6136,7 @@ func (m *SwapFeeParamsRes) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SwapFeeRate", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field DefaultSwapFeeRate", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -6164,7 +6164,7 @@ func (m *SwapFeeParamsRes) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.SwapFeeRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.DefaultSwapFeeRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/x/clp/types/tx.pb.go b/x/clp/types/tx.pb.go index 2fabe73069..bc7aaa4da0 100644 --- a/x/clp/types/tx.pb.go +++ b/x/clp/types/tx.pb.go @@ -1657,9 +1657,9 @@ func (m *MsgAddProviderDistributionPeriodResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgAddProviderDistributionPeriodResponse proto.InternalMessageInfo type MsgUpdateSwapFeeParamsRequest struct { - Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty" yaml:"signer"` - SwapFeeRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=swap_fee_rate,json=swapFeeRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"swap_fee_rate"` - TokenParams []*SwapFeeTokenParams `protobuf:"bytes,3,rep,name=token_params,json=tokenParams,proto3" json:"token_params,omitempty"` + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty" yaml:"signer"` + DefaultSwapFeeRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=default_swap_fee_rate,json=defaultSwapFeeRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"default_swap_fee_rate"` + TokenParams []*SwapFeeTokenParams `protobuf:"bytes,3,rep,name=token_params,json=tokenParams,proto3" json:"token_params,omitempty"` } func (m *MsgUpdateSwapFeeParamsRequest) Reset() { *m = MsgUpdateSwapFeeParamsRequest{} } @@ -1788,121 +1788,122 @@ func init() { func init() { proto.RegisterFile("sifnode/clp/v1/tx.proto", fileDescriptor_a3bff5b30808c4f3) } var fileDescriptor_a3bff5b30808c4f3 = []byte{ - // 1822 bytes of a gzipped FileDescriptorProto + // 1830 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x59, 0xcd, 0x6f, 0xe4, 0x48, 0x15, 0x1f, 0xa7, 0x27, 0xc3, 0xe4, 0xe5, 0x63, 0x76, 0x9c, 0x84, 0x64, 0x9c, 0xa4, 0x7b, 0xe2, 0x81, 0x9d, 0x8f, 0xdd, 0x4d, 0x33, 0xc3, 0xa2, 0x5d, 0x56, 0x42, 0x22, 0x99, 0x09, 0x0b, 0x22, 0x0d, 0x2d, 0x67, 0x47, 0x8b, 0x90, 0x90, 0x71, 0xec, 0x4a, 0xa7, 0x94, 0xb6, 0xcb, 0xeb, 0xaa, 0xce, 0x07, 0x12, 0x02, 0x89, 0x23, 0xd2, 0x0a, 0x38, 0x21, 0x24, 0x24, 0xc4, 0xbf, 0xc0, 0xdf, - 0x80, 0xb4, 0xcb, 0x69, 0x91, 0x38, 0x20, 0x0e, 0x11, 0x9a, 0x11, 0x48, 0x1c, 0xb8, 0xcc, 0x5f, - 0x80, 0xea, 0xa3, 0xab, 0xdd, 0xee, 0x72, 0x27, 0x8e, 0x38, 0xe4, 0xb0, 0xa7, 0xc4, 0xf5, 0xbe, - 0xdf, 0xab, 0xf7, 0xde, 0xcf, 0x6e, 0x58, 0xa2, 0x78, 0x3f, 0x21, 0x11, 0x6a, 0x86, 0xdd, 0xb4, - 0x79, 0xf4, 0xb8, 0xc9, 0x4e, 0x36, 0xd2, 0x8c, 0x30, 0x62, 0xcf, 0x29, 0xc2, 0x46, 0xd8, 0x4d, - 0x37, 0x8e, 0x1e, 0x3b, 0x0b, 0x1d, 0xd2, 0x21, 0x82, 0xd4, 0xe4, 0xff, 0x49, 0x2e, 0xc7, 0x29, - 0x8a, 0x9f, 0xa6, 0x88, 0x2a, 0xda, 0x4a, 0x81, 0x96, 0x06, 0x59, 0x10, 0x2b, 0xa2, 0xfb, 0x5f, - 0x0b, 0x56, 0x5b, 0xb4, 0xf3, 0x3c, 0x8d, 0x02, 0x86, 0x76, 0x59, 0x70, 0x88, 0x93, 0x8e, 0x87, - 0x8e, 0x83, 0x2c, 0x6a, 0x0b, 0x36, 0xfb, 0x21, 0xdc, 0xa0, 0xb8, 0x93, 0xa0, 0x6c, 0xd9, 0xba, - 0x6b, 0x3d, 0x98, 0xda, 0xba, 0xfd, 0xea, 0xac, 0x31, 0x7b, 0x1a, 0xc4, 0xdd, 0xf7, 0x5c, 0x79, - 0xee, 0x7a, 0x8a, 0xc1, 0x6e, 0xc3, 0x8d, 0x18, 0x27, 0x0c, 0x65, 0xcb, 0x13, 0x82, 0xf5, 0xdd, - 0x4f, 0xce, 0x1a, 0xd7, 0xfe, 0x71, 0xd6, 0xf8, 0x4a, 0x07, 0xb3, 0x83, 0xde, 0xde, 0x46, 0x48, - 0xe2, 0x66, 0x48, 0x68, 0x4c, 0xa8, 0xfa, 0xf3, 0x16, 0x8d, 0x0e, 0x9b, 0x27, 0x4d, 0x2e, 0xa4, - 0x3c, 0x6e, 0x09, 0x79, 0x4f, 0xe9, 0xe1, 0x1a, 0xa5, 0xb7, 0xcb, 0xb5, 0xcb, 0x6a, 0x94, 0x61, - 0x78, 0x4a, 0x8f, 0xfb, 0x3a, 0x7c, 0x69, 0x5c, 0xb8, 0x1e, 0xa2, 0x29, 0x49, 0x28, 0x72, 0xff, - 0x33, 0x01, 0x76, 0x8b, 0x76, 0x3c, 0x14, 0x93, 0x23, 0xb4, 0x83, 0x3f, 0xea, 0xe1, 0x08, 0xb3, - 0xd3, 0x2a, 0xd9, 0xf8, 0x10, 0xe6, 0xd0, 0x09, 0x43, 0x59, 0x12, 0x74, 0xfd, 0x80, 0x52, 0xc4, - 0x44, 0x56, 0xa6, 0x9f, 0x2c, 0x6e, 0x0c, 0x57, 0x74, 0x63, 0x93, 0x13, 0xb7, 0xee, 0xbc, 0x3a, - 0x6b, 0x2c, 0x4a, 0x4d, 0xc3, 0x62, 0xae, 0x37, 0xdb, 0x3f, 0x10, 0x9c, 0x76, 0x0c, 0x73, 0xc7, - 0xfe, 0x5e, 0x40, 0x31, 0xf5, 0x53, 0x82, 0x13, 0xd6, 0x4f, 0xce, 0xfb, 0x2a, 0x39, 0xaf, 0x8f, - 0x4d, 0x8e, 0xcc, 0xca, 0x77, 0x12, 0x36, 0xb0, 0x37, 0xac, 0xcd, 0xf5, 0x66, 0x8e, 0xb7, 0xf8, - 0x73, 0x5b, 0x3c, 0xda, 0x3f, 0x86, 0xa9, 0x80, 0x9e, 0xc6, 0x31, 0x62, 0xd9, 0xe9, 0xf2, 0x75, - 0x61, 0x69, 0xab, 0xb2, 0xa5, 0xd7, 0xa4, 0x25, 0xad, 0xc8, 0xf5, 0x06, 0x4a, 0xdd, 0x55, 0x70, - 0x46, 0x53, 0xad, 0x2b, 0xf1, 0xf1, 0x04, 0x2c, 0x8d, 0x92, 0x9f, 0x27, 0x98, 0xd1, 0x2b, 0x51, - 0x0e, 0x02, 0x73, 0xc7, 0x98, 0x1d, 0x44, 0x59, 0x70, 0xec, 0xf7, 0xb8, 0x57, 0xaa, 0x1c, 0xdf, - 0x56, 0x49, 0xba, 0x7f, 0x81, 0x24, 0x3d, 0xc7, 0x43, 0xf5, 0x18, 0x52, 0xe7, 0x7a, 0xb3, 0xfd, - 0x03, 0x11, 0xb4, 0xbb, 0x0e, 0x8d, 0x92, 0x7c, 0xe8, 0x9c, 0xfd, 0xb6, 0x06, 0xb3, 0x2d, 0xda, - 0x79, 0x9a, 0xa1, 0x80, 0xa1, 0x36, 0x21, 0xdd, 0x2b, 0x91, 0xa9, 0x9f, 0xc2, 0x7c, 0x12, 0x30, - 0x7c, 0x84, 0x24, 0xdd, 0x0f, 0x62, 0xd2, 0x4b, 0x98, 0x4a, 0x57, 0xab, 0x7a, 0xba, 0x1c, 0x69, - 0xd5, 0xa0, 0xd3, 0xf5, 0x6e, 0xcb, 0x53, 0x61, 0x78, 0x53, 0x9c, 0xd9, 0xbf, 0xb0, 0x60, 0x71, - 0xd8, 0xc3, 0xbe, 0x07, 0xf2, 0x56, 0x7f, 0xbf, 0xba, 0x07, 0xab, 0xa6, 0xb8, 0xb5, 0x0f, 0xf3, - 0x43, 0xe1, 0x4b, 0x2f, 0xdc, 0x25, 0x58, 0x1c, 0xaa, 0x8c, 0xae, 0xd9, 0xef, 0x6a, 0x70, 0xab, - 0x45, 0x3b, 0x9b, 0x51, 0x74, 0xb5, 0xc6, 0xcd, 0xe7, 0x55, 0x4b, 0x98, 0x7b, 0x47, 0xcc, 0xa0, - 0x7c, 0x6d, 0x74, 0xdd, 0xfe, 0x60, 0x89, 0x4d, 0xd1, 0x22, 0x11, 0xde, 0x3f, 0x6d, 0xc7, 0x2c, - 0xf5, 0x02, 0x86, 0x2a, 0x8d, 0xa6, 0x35, 0x80, 0xbd, 0x2e, 0x09, 0x0f, 0xfd, 0x2c, 0x60, 0x48, - 0xee, 0x4e, 0x6f, 0x4a, 0x9c, 0x70, 0x55, 0xf6, 0x3a, 0xcc, 0x64, 0xbd, 0x24, 0xc1, 0x49, 0x47, - 0x32, 0x88, 0xcc, 0x7b, 0xd3, 0xea, 0x4c, 0xb0, 0xac, 0x01, 0xa0, 0x24, 0xf2, 0x53, 0xd2, 0xc5, - 0xa1, 0x1c, 0xd2, 0x37, 0xbd, 0x29, 0x94, 0x44, 0x6d, 0x71, 0xa0, 0x06, 0x6c, 0xc1, 0x43, 0x1d, - 0xc0, 0x1f, 0x27, 0x60, 0x5e, 0xef, 0x44, 0x4e, 0xae, 0xbe, 0xf9, 0xbf, 0x01, 0x2b, 0x69, 0xcc, - 0x52, 0x3f, 0x45, 0x19, 0x26, 0x91, 0xdf, 0x21, 0x47, 0x3c, 0x83, 0x49, 0x88, 0xf2, 0x21, 0x2d, - 0x73, 0x96, 0xb6, 0xe0, 0x78, 0x5f, 0x33, 0x08, 0xf7, 0xdf, 0x81, 0xe5, 0xbc, 0x38, 0x4a, 0x49, - 0x78, 0xe0, 0x77, 0x51, 0xd2, 0x61, 0x07, 0x22, 0xda, 0x9a, 0xb7, 0x38, 0x90, 0xdd, 0xe6, 0xd4, - 0x1d, 0x41, 0xb4, 0xbf, 0x06, 0x4b, 0x79, 0x41, 0xca, 0x82, 0x8c, 0xf9, 0x22, 0x73, 0x22, 0x09, - 0x35, 0x6f, 0x61, 0x20, 0xb7, 0xcb, 0x89, 0x5b, 0x9c, 0x66, 0x3f, 0x86, 0xc5, 0x21, 0x7b, 0x49, - 0xa4, 0x84, 0x26, 0x85, 0x90, 0x9d, 0x33, 0x96, 0x44, 0x42, 0xc4, 0x5d, 0x83, 0x15, 0x43, 0x8e, - 0x74, 0x0e, 0xff, 0x5c, 0x83, 0x2f, 0xb4, 0x68, 0x67, 0xf7, 0x38, 0x48, 0xab, 0xe4, 0xed, 0xbb, - 0x00, 0x14, 0x25, 0xec, 0x22, 0x0d, 0xbb, 0xf8, 0xea, 0xac, 0x71, 0x5b, 0x69, 0xd1, 0x22, 0xae, - 0x37, 0xc5, 0x1f, 0x64, 0xa3, 0x7e, 0x08, 0x73, 0x19, 0x0a, 0x11, 0x3e, 0x42, 0x91, 0x52, 0x58, - 0xbb, 0xe0, 0x04, 0x18, 0x16, 0x73, 0xbd, 0xd9, 0xfe, 0x81, 0x54, 0xbc, 0x0f, 0xd3, 0xd2, 0x64, - 0xbe, 0xef, 0xb6, 0xab, 0xf7, 0x9d, 0x9d, 0x77, 0x5f, 0x75, 0x9b, 0x88, 0x5f, 0xb5, 0xfa, 0xcf, - 0x2d, 0x58, 0x88, 0x71, 0xe2, 0x4b, 0xeb, 0xfc, 0xbe, 0x2b, 0x8b, 0x93, 0xc2, 0xe2, 0xf7, 0xaa, - 0x5b, 0x5c, 0x91, 0x16, 0x4d, 0x4a, 0x5d, 0xcf, 0x8e, 0x71, 0xe2, 0xf5, 0x4f, 0x55, 0x9f, 0xdf, - 0x16, 0x33, 0x98, 0x97, 0x51, 0x97, 0xf6, 0x50, 0x74, 0xc7, 0x33, 0x14, 0x92, 0x38, 0xc6, 0x94, - 0x62, 0x92, 0x54, 0x5d, 0xa8, 0x9c, 0xf5, 0x34, 0xde, 0x23, 0x5d, 0x85, 0x8b, 0xf3, 0xac, 0xe2, - 0x9c, 0xb3, 0xca, 0x7f, 0xe4, 0x35, 0x2b, 0x1a, 0xd3, 0xbe, 0xfc, 0xdb, 0x82, 0x3b, 0xfc, 0x1a, - 0x26, 0xfc, 0x4e, 0xe6, 0x46, 0xd1, 0x47, 0x3d, 0x44, 0xd9, 0x95, 0xd8, 0x16, 0xdb, 0x30, 0x99, - 0x07, 0x41, 0xcd, 0x8a, 0x35, 0xf3, 0xa4, 0xb4, 0x9a, 0x58, 0x23, 0x71, 0xaa, 0x34, 0xfc, 0xcd, - 0x82, 0x35, 0xdd, 0x8d, 0x12, 0xbe, 0xd3, 0x7e, 0x43, 0x56, 0x4e, 0xc5, 0x26, 0xac, 0x75, 0xfb, - 0x16, 0xfc, 0x8c, 0xa3, 0xaa, 0xa0, 0xeb, 0x8b, 0x71, 0x2c, 0xc7, 0x83, 0xc8, 0xcc, 0x75, 0xcf, - 0xe9, 0x0e, 0xdc, 0x10, 0x3c, 0x3b, 0x24, 0x3c, 0x94, 0x43, 0xc2, 0xde, 0x86, 0xc6, 0xa8, 0x8a, - 0x90, 0x8f, 0xb7, 0x6e, 0x5f, 0x49, 0x4d, 0x28, 0x59, 0x2d, 0x2a, 0x79, 0x2a, 0x98, 0xa4, 0x1a, - 0xf7, 0x2e, 0xd4, 0xcb, 0xa2, 0x52, 0x81, 0xff, 0x52, 0xd6, 0x7f, 0x33, 0x8a, 0xd4, 0x4b, 0x8b, - 0x10, 0xbc, 0x44, 0xd0, 0x4f, 0xf9, 0xac, 0xe0, 0x1a, 0x94, 0x7f, 0x74, 0x79, 0xe2, 0x6e, 0xed, - 0xc1, 0xf4, 0x93, 0xd5, 0x62, 0xfd, 0x87, 0xec, 0xcc, 0x66, 0xb9, 0xa7, 0x7e, 0x91, 0x46, 0x9c, - 0xe9, 0x8f, 0x44, 0x4b, 0xec, 0xcc, 0x5d, 0xc4, 0x76, 0x15, 0xd0, 0xff, 0xe0, 0x20, 0x43, 0xf4, - 0x80, 0x74, 0x23, 0xfb, 0x8b, 0xc3, 0x9e, 0x6a, 0xb7, 0x76, 0x60, 0x8a, 0xf5, 0x99, 0x54, 0xb3, - 0x6c, 0x54, 0x78, 0xd7, 0x78, 0x86, 0x42, 0x6f, 0xa0, 0xc0, 0x7e, 0x06, 0x93, 0x59, 0xc0, 0x30, - 0x51, 0x77, 0xb1, 0xaa, 0x26, 0x29, 0xac, 0xe0, 0xb6, 0x29, 0x0c, 0x1d, 0xea, 0xa7, 0x96, 0x18, - 0x1b, 0xb2, 0x98, 0xf2, 0xd2, 0x96, 0x86, 0x78, 0xd5, 0x3b, 0x4f, 0x22, 0x9d, 0x7c, 0x28, 0x3a, - 0xcc, 0xdf, 0x5b, 0x30, 0xa7, 0xee, 0x6d, 0xff, 0xca, 0xcd, 0xc1, 0x04, 0x8e, 0x44, 0x84, 0x35, - 0x6f, 0x02, 0xf3, 0x4e, 0x98, 0x3c, 0x0a, 0xba, 0x3d, 0xb5, 0xf2, 0x2f, 0xe1, 0x84, 0x90, 0xb6, - 0xdf, 0x86, 0x5a, 0x4c, 0x3b, 0x6a, 0x7f, 0xb9, 0xc5, 0xcc, 0x18, 0x5e, 0x16, 0x39, 0xbb, 0xfb, - 0x17, 0x0b, 0xd6, 0x35, 0xce, 0xd1, 0xb4, 0x76, 0x46, 0x18, 0x0a, 0x19, 0x26, 0x49, 0x65, 0x60, - 0xf6, 0x13, 0x58, 0x0f, 0x7b, 0x59, 0xc6, 0xf7, 0x55, 0x46, 0x8e, 0x83, 0xc4, 0x1f, 0x74, 0x79, - 0xf1, 0x9a, 0x56, 0x8e, 0xb4, 0xae, 0x34, 0x7b, 0x5c, 0xb1, 0x76, 0x56, 0xdf, 0x2d, 0xf7, 0x0d, - 0x78, 0x78, 0x6e, 0x2c, 0xba, 0x32, 0x7f, 0x9d, 0x00, 0x57, 0x8f, 0x0e, 0x03, 0x77, 0x75, 0x44, - 0x97, 0xc1, 0x5a, 0x1c, 0x9c, 0xfc, 0xff, 0xc3, 0x76, 0xe2, 0xe0, 0xa4, 0x24, 0x64, 0x7b, 0x07, - 0xee, 0x8d, 0xb5, 0xa9, 0xfa, 0x45, 0xe0, 0x0f, 0xaf, 0x51, 0xae, 0x48, 0xf6, 0xc3, 0x3a, 0xcc, - 0x8c, 0x00, 0xc9, 0xeb, 0xde, 0x34, 0xca, 0xc1, 0xc7, 0x15, 0x98, 0xc2, 0xd4, 0x0f, 0x42, 0xfe, - 0xce, 0x21, 0x40, 0xc6, 0x4d, 0xef, 0x26, 0xa6, 0x9b, 0xe2, 0xd9, 0x7d, 0x13, 0x1e, 0x9d, 0x9f, - 0x52, 0x5d, 0x81, 0x3f, 0x59, 0x70, 0x5f, 0x0e, 0xc3, 0x76, 0x46, 0x8e, 0x70, 0x84, 0xb2, 0x67, - 0x98, 0xb2, 0x0c, 0xef, 0xf5, 0x04, 0xf3, 0x65, 0xe7, 0xf4, 0x8f, 0x60, 0x21, 0xca, 0xe9, 0x29, - 0x4c, 0xeb, 0x47, 0xc5, 0xce, 0x18, 0x63, 0x7b, 0x3e, 0x1a, 0x39, 0xa3, 0xee, 0x23, 0x78, 0x70, - 0xbe, 0xd3, 0x2a, 0xc2, 0x7f, 0xe5, 0x97, 0x2e, 0x47, 0x48, 0xdf, 0x42, 0xe8, 0xd2, 0x4b, 0xd7, - 0x83, 0x59, 0x7a, 0x1c, 0xa4, 0xfe, 0x3e, 0xca, 0xbf, 0x22, 0x54, 0x1e, 0xd1, 0xd3, 0x54, 0xfa, - 0x21, 0xde, 0x22, 0xb6, 0x61, 0x86, 0x91, 0x43, 0x94, 0xf8, 0xfa, 0x93, 0x61, 0xcd, 0x34, 0x3d, - 0x94, 0xeb, 0x1f, 0x70, 0x56, 0xe5, 0xff, 0x34, 0x1b, 0x3c, 0x0c, 0x6d, 0xe1, 0x42, 0x98, 0x32, - 0x13, 0x4f, 0x3e, 0xbd, 0x05, 0xb5, 0x16, 0xed, 0xd8, 0x01, 0xdc, 0x2a, 0x7e, 0x1f, 0xbc, 0xc0, - 0xac, 0x72, 0x1e, 0x5d, 0x60, 0x9e, 0x29, 0x53, 0x76, 0x0a, 0x0b, 0xc6, 0x0f, 0x5f, 0xf7, 0xcf, - 0xd7, 0x21, 0x18, 0x9d, 0xe6, 0x05, 0x19, 0xb5, 0x45, 0x0f, 0x20, 0xf7, 0xd9, 0x68, 0xcd, 0x20, - 0x3e, 0x20, 0x3b, 0x5f, 0x1e, 0x4b, 0xd6, 0x3a, 0x7f, 0x00, 0x33, 0x43, 0x9f, 0x35, 0x1a, 0x06, - 0xb1, 0x3c, 0x83, 0x73, 0xff, 0x1c, 0x06, 0xad, 0xf9, 0x9b, 0x70, 0x5d, 0xbc, 0x73, 0x2d, 0x19, - 0x04, 0x38, 0xc1, 0x69, 0x94, 0x10, 0xb4, 0x86, 0x08, 0x5e, 0x1b, 0xc1, 0xf6, 0xf7, 0x0c, 0x42, - 0x45, 0x26, 0xe7, 0x8d, 0x0b, 0x30, 0x69, 0x2b, 0x07, 0x70, 0xab, 0x00, 0x66, 0xed, 0x87, 0x06, - 0x79, 0x33, 0xb0, 0x37, 0xde, 0x98, 0x12, 0x6c, 0x6c, 0x33, 0x98, 0x37, 0x20, 0x48, 0xfb, 0x2d, - 0x93, 0x8a, 0x52, 0xfc, 0xec, 0x6c, 0x5c, 0x94, 0x7d, 0x10, 0x5f, 0x01, 0x07, 0x1a, 0xe3, 0x33, - 0x03, 0x57, 0x63, 0x7c, 0x25, 0xb0, 0x92, 0x37, 0x5d, 0xf1, 0x53, 0x8b, 0xa9, 0xe9, 0x0a, 0x3c, - 0x46, 0x13, 0x25, 0x1f, 0x44, 0xf8, 0x95, 0x18, 0xf9, 0x18, 0x72, 0xaf, 0x34, 0x21, 0x03, 0x26, - 0xe3, 0x95, 0x28, 0xfb, 0x64, 0x60, 0xff, 0x0c, 0xee, 0x94, 0xff, 0xea, 0xf2, 0x66, 0xa9, 0x26, - 0x03, 0xb7, 0xf3, 0x76, 0x15, 0xee, 0xfc, 0x6c, 0x31, 0x82, 0x73, 0x53, 0xf3, 0x99, 0x18, 0x8d, - 0xb3, 0x65, 0x1c, 0x4e, 0xb6, 0x03, 0x58, 0xcc, 0x03, 0xcb, 0xf1, 0x03, 0x21, 0xcf, 0x69, 0x1c, - 0x08, 0x26, 0x8c, 0x6a, 0xff, 0xda, 0x82, 0xc6, 0x79, 0x30, 0xe8, 0x49, 0x69, 0xba, 0x4a, 0x65, - 0x9c, 0xf7, 0xaa, 0xcb, 0x68, 0x9f, 0x3e, 0xb6, 0xa0, 0x7e, 0x0e, 0x28, 0x7d, 0x5c, 0x7a, 0x3d, - 0xcb, 0x44, 0x9c, 0xaf, 0x57, 0x16, 0xd1, 0x0e, 0xfd, 0xc6, 0x82, 0xb5, 0xb1, 0x4b, 0xdf, 0x7e, - 0xc7, 0xdc, 0x91, 0xe7, 0x62, 0x1b, 0xe7, 0xdd, 0xea, 0x82, 0xc5, 0xc1, 0x35, 0xb4, 0x74, 0xc7, - 0x0c, 0x2e, 0x13, 0x06, 0x19, 0x33, 0xb8, 0x8c, 0xbb, 0x7c, 0x6b, 0xf3, 0x93, 0x17, 0x75, 0xeb, - 0xb3, 0x17, 0x75, 0xeb, 0x9f, 0x2f, 0xea, 0xd6, 0xaf, 0x5e, 0xd6, 0xaf, 0x7d, 0xf6, 0xb2, 0x7e, - 0xed, 0xef, 0x2f, 0xeb, 0xd7, 0x7e, 0x98, 0x87, 0xb4, 0xbb, 0x78, 0x3f, 0x3c, 0x08, 0x70, 0xd2, - 0xec, 0xff, 0x94, 0x7a, 0x22, 0x7e, 0x4c, 0x15, 0x40, 0x64, 0xef, 0x86, 0xf8, 0x25, 0xf5, 0xab, - 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x84, 0xf2, 0x48, 0xda, 0xc3, 0x1d, 0x00, 0x00, + 0x80, 0xb4, 0xcb, 0x69, 0x91, 0x38, 0x20, 0x0e, 0x11, 0x9a, 0x91, 0x90, 0x38, 0x70, 0x19, 0xf1, + 0x07, 0xa0, 0xfa, 0xe8, 0x6a, 0xb7, 0xbb, 0xdc, 0x89, 0x23, 0x0e, 0x39, 0xec, 0x29, 0x71, 0xd5, + 0x7b, 0xbf, 0xf7, 0x55, 0xef, 0xd5, 0xcf, 0x6e, 0x58, 0xa2, 0x78, 0x3f, 0x21, 0x11, 0x6a, 0x86, + 0xdd, 0xb4, 0x79, 0xf4, 0xb8, 0xc9, 0x4e, 0x36, 0xd2, 0x8c, 0x30, 0x62, 0xcf, 0xa9, 0x8d, 0x8d, + 0xb0, 0x9b, 0x6e, 0x1c, 0x3d, 0x76, 0x16, 0x3a, 0xa4, 0x43, 0xc4, 0x56, 0x93, 0xff, 0x27, 0xa5, + 0x1c, 0xa7, 0xa8, 0x7e, 0x9a, 0x22, 0xaa, 0xf6, 0x56, 0x0a, 0x7b, 0x69, 0x90, 0x05, 0xb1, 0xda, + 0x74, 0xff, 0x63, 0xc1, 0x6a, 0x8b, 0x76, 0x9e, 0xa7, 0x51, 0xc0, 0xd0, 0x2e, 0x0b, 0x0e, 0x71, + 0xd2, 0xf1, 0xd0, 0x71, 0x90, 0x45, 0x6d, 0x21, 0x66, 0x3f, 0x84, 0x1b, 0x14, 0x77, 0x12, 0x94, + 0x2d, 0x5b, 0x77, 0xad, 0x07, 0x53, 0x5b, 0xb7, 0x5f, 0x9d, 0x35, 0x66, 0x4f, 0x83, 0xb8, 0xfb, + 0x9e, 0x2b, 0xd7, 0x5d, 0x4f, 0x09, 0xd8, 0x6d, 0xb8, 0x11, 0xe3, 0x84, 0xa1, 0x6c, 0x79, 0x42, + 0x88, 0xbe, 0xfb, 0xc9, 0x59, 0xe3, 0xda, 0x3f, 0xce, 0x1a, 0x5f, 0xe9, 0x60, 0x76, 0xd0, 0xdb, + 0xdb, 0x08, 0x49, 0xdc, 0x0c, 0x09, 0x8d, 0x09, 0x55, 0x7f, 0xde, 0xa2, 0xd1, 0x61, 0xf3, 0xa4, + 0xc9, 0x95, 0x94, 0xc7, 0x2d, 0xa1, 0xef, 0x29, 0x1c, 0x8e, 0x28, 0xbd, 0x5d, 0xae, 0x5d, 0x16, + 0x51, 0x86, 0xe1, 0x29, 0x1c, 0xf7, 0x75, 0xf8, 0xd2, 0xb8, 0x70, 0x3d, 0x44, 0x53, 0x92, 0x50, + 0xe4, 0xfe, 0x7b, 0x02, 0xec, 0x16, 0xed, 0x78, 0x28, 0x26, 0x47, 0x68, 0x07, 0x7f, 0xd4, 0xc3, + 0x11, 0x66, 0xa7, 0x55, 0xb2, 0xf1, 0x21, 0xcc, 0xa1, 0x13, 0x86, 0xb2, 0x24, 0xe8, 0xfa, 0x01, + 0xa5, 0x88, 0x89, 0xac, 0x4c, 0x3f, 0x59, 0xdc, 0x18, 0xae, 0xe8, 0xc6, 0x26, 0xdf, 0xdc, 0xba, + 0xf3, 0xea, 0xac, 0xb1, 0x28, 0x91, 0x86, 0xd5, 0x5c, 0x6f, 0xb6, 0xbf, 0x20, 0x24, 0xed, 0x18, + 0xe6, 0x8e, 0xfd, 0xbd, 0x80, 0x62, 0xea, 0xa7, 0x04, 0x27, 0xac, 0x9f, 0x9c, 0xf7, 0x55, 0x72, + 0x5e, 0x1f, 0x9b, 0x1c, 0x99, 0x95, 0xef, 0x24, 0x6c, 0x60, 0x6f, 0x18, 0xcd, 0xf5, 0x66, 0x8e, + 0xb7, 0xf8, 0x73, 0x5b, 0x3c, 0xda, 0x3f, 0x86, 0xa9, 0x80, 0x9e, 0xc6, 0x31, 0x62, 0xd9, 0xe9, + 0xf2, 0x75, 0x61, 0x69, 0xab, 0xb2, 0xa5, 0xd7, 0xa4, 0x25, 0x0d, 0xe4, 0x7a, 0x03, 0x50, 0x77, + 0x15, 0x9c, 0xd1, 0x54, 0xeb, 0x4a, 0x7c, 0x3c, 0x01, 0x4b, 0xa3, 0xdb, 0xcf, 0x13, 0xcc, 0xe8, + 0x95, 0x28, 0x07, 0x81, 0xb9, 0x63, 0xcc, 0x0e, 0xa2, 0x2c, 0x38, 0xf6, 0x7b, 0xdc, 0x2b, 0x55, + 0x8e, 0x6f, 0xab, 0x24, 0xdd, 0xbf, 0x40, 0x92, 0x9e, 0xe3, 0xa1, 0x7a, 0x0c, 0xc1, 0xb9, 0xde, + 0x6c, 0x7f, 0x41, 0x04, 0xed, 0xae, 0x43, 0xa3, 0x24, 0x1f, 0x3a, 0x67, 0xbf, 0xad, 0xc1, 0x6c, + 0x8b, 0x76, 0x9e, 0x66, 0x28, 0x60, 0xa8, 0x4d, 0x48, 0xf7, 0x4a, 0x64, 0xea, 0xa7, 0x30, 0x9f, + 0x04, 0x0c, 0x1f, 0x21, 0xb9, 0xef, 0x07, 0x31, 0xe9, 0x25, 0x4c, 0xa5, 0xab, 0x55, 0x3d, 0x5d, + 0x8e, 0xb4, 0x6a, 0xc0, 0x74, 0xbd, 0xdb, 0x72, 0x55, 0x18, 0xde, 0x14, 0x6b, 0xf6, 0x2f, 0x2c, + 0x58, 0x1c, 0xf6, 0xb0, 0xef, 0x81, 0x3c, 0xd5, 0xdf, 0xaf, 0xee, 0xc1, 0xaa, 0x29, 0x6e, 0xed, + 0xc3, 0xfc, 0x50, 0xf8, 0xd2, 0x0b, 0x77, 0x09, 0x16, 0x87, 0x2a, 0xa3, 0x6b, 0xf6, 0xbb, 0x1a, + 0xdc, 0x6a, 0xd1, 0xce, 0x66, 0x14, 0x5d, 0xad, 0x71, 0xf3, 0x79, 0xd5, 0x12, 0xe6, 0xde, 0x11, + 0x33, 0x28, 0x5f, 0x1b, 0x5d, 0xb7, 0x3f, 0x58, 0xe2, 0xa6, 0x68, 0x91, 0x08, 0xef, 0x9f, 0xb6, + 0x63, 0x96, 0x7a, 0x01, 0x43, 0x95, 0x46, 0xd3, 0x1a, 0xc0, 0x5e, 0x97, 0x84, 0x87, 0x7e, 0x16, + 0x30, 0x24, 0xef, 0x4e, 0x6f, 0x4a, 0xac, 0x70, 0x28, 0x7b, 0x1d, 0x66, 0xb2, 0x5e, 0x92, 0xe0, + 0xa4, 0x23, 0x05, 0x44, 0xe6, 0xbd, 0x69, 0xb5, 0x26, 0x44, 0xd6, 0x00, 0x50, 0x12, 0xf9, 0x29, + 0xe9, 0xe2, 0x50, 0x0e, 0xe9, 0x9b, 0xde, 0x14, 0x4a, 0xa2, 0xb6, 0x58, 0x50, 0x03, 0xb6, 0xe0, + 0xa1, 0x0e, 0xe0, 0x8f, 0x13, 0x30, 0xaf, 0xef, 0x44, 0xbe, 0x5d, 0xfd, 0xe6, 0xff, 0x06, 0xac, + 0xa4, 0x31, 0x4b, 0xfd, 0x14, 0x65, 0x98, 0x44, 0x7e, 0x87, 0x1c, 0xf1, 0x0c, 0x26, 0x21, 0xca, + 0x87, 0xb4, 0xcc, 0x45, 0xda, 0x42, 0xe2, 0x7d, 0x2d, 0x20, 0xdc, 0x7f, 0x07, 0x96, 0xf3, 0xea, + 0x28, 0x25, 0xe1, 0x81, 0xdf, 0x45, 0x49, 0x87, 0x1d, 0x88, 0x68, 0x6b, 0xde, 0xe2, 0x40, 0x77, + 0x9b, 0xef, 0xee, 0x88, 0x4d, 0xfb, 0x6b, 0xb0, 0x94, 0x57, 0xa4, 0x2c, 0xc8, 0x98, 0x2f, 0x32, + 0x27, 0x92, 0x50, 0xf3, 0x16, 0x06, 0x7a, 0xbb, 0x7c, 0x73, 0x8b, 0xef, 0xd9, 0x8f, 0x61, 0x71, + 0xc8, 0x5e, 0x12, 0x29, 0xa5, 0x49, 0xa1, 0x64, 0xe7, 0x8c, 0x25, 0x91, 0x50, 0x71, 0xd7, 0x60, + 0xc5, 0x90, 0x23, 0x9d, 0xc3, 0x3f, 0xd7, 0xe0, 0x0b, 0x2d, 0xda, 0xd9, 0x3d, 0x0e, 0xd2, 0x2a, + 0x79, 0xfb, 0x2e, 0x00, 0x45, 0x09, 0xbb, 0x48, 0xc3, 0x2e, 0xbe, 0x3a, 0x6b, 0xdc, 0x56, 0x28, + 0x5a, 0xc5, 0xf5, 0xa6, 0xf8, 0x83, 0x6c, 0xd4, 0x0f, 0x61, 0x2e, 0x43, 0x21, 0xc2, 0x47, 0x28, + 0x52, 0x80, 0xb5, 0x0b, 0x4e, 0x80, 0x61, 0x35, 0xd7, 0x9b, 0xed, 0x2f, 0x48, 0xe0, 0x7d, 0x98, + 0x96, 0x26, 0xf3, 0x7d, 0xb7, 0x5d, 0xbd, 0xef, 0xec, 0xbc, 0xfb, 0xaa, 0xdb, 0x44, 0xfc, 0xaa, + 0xd5, 0x7f, 0x6e, 0xc1, 0x42, 0x8c, 0x13, 0x5f, 0x5a, 0xe7, 0xe7, 0x5d, 0x59, 0x9c, 0x14, 0x16, + 0xbf, 0x57, 0xdd, 0xe2, 0x8a, 0xb4, 0x68, 0x02, 0x75, 0x3d, 0x3b, 0xc6, 0x89, 0xd7, 0x5f, 0x55, + 0x7d, 0x7e, 0x5b, 0xcc, 0x60, 0x5e, 0x46, 0x5d, 0xda, 0x43, 0xd1, 0x1d, 0xcf, 0x50, 0x48, 0xe2, + 0x18, 0x53, 0x8a, 0x49, 0x52, 0xf5, 0x42, 0xe5, 0xa2, 0xa7, 0xf1, 0x1e, 0xe9, 0x2a, 0x5e, 0x9c, + 0x17, 0x15, 0xeb, 0x5c, 0x54, 0xfe, 0x23, 0x8f, 0x59, 0xd1, 0x98, 0xf6, 0xe5, 0x5f, 0x16, 0xdc, + 0xe1, 0xc7, 0x30, 0xe1, 0x67, 0x32, 0x37, 0x8a, 0x3e, 0xea, 0x21, 0xca, 0xae, 0xc4, 0x6d, 0xb1, + 0x0d, 0x93, 0x79, 0x12, 0xd4, 0xac, 0x58, 0x33, 0x4f, 0x6a, 0xab, 0x89, 0x35, 0x12, 0xa7, 0x4a, + 0xc3, 0xdf, 0x2c, 0x58, 0xd3, 0xdd, 0x28, 0xe9, 0x3b, 0xed, 0x37, 0x64, 0xe5, 0x54, 0x6c, 0xc2, + 0x5a, 0xb7, 0x6f, 0xc1, 0xcf, 0x38, 0xab, 0x0a, 0xba, 0xbe, 0x18, 0xc7, 0x72, 0x3c, 0x88, 0xcc, + 0x5c, 0xf7, 0x9c, 0xee, 0xc0, 0x0d, 0x21, 0xb3, 0x43, 0xc2, 0x43, 0x39, 0x24, 0xec, 0x6d, 0x68, + 0x8c, 0x42, 0x84, 0x7c, 0xbc, 0x75, 0xfb, 0x20, 0x35, 0x01, 0xb2, 0x5a, 0x04, 0x79, 0x2a, 0x84, + 0x24, 0x8c, 0x7b, 0x17, 0xea, 0x65, 0x51, 0xa9, 0xc0, 0x7f, 0x29, 0xeb, 0xbf, 0x19, 0x45, 0xea, + 0xa5, 0x45, 0x28, 0x5e, 0x22, 0xe8, 0xa7, 0x7c, 0x56, 0x70, 0x04, 0xe5, 0x1f, 0x5d, 0x9e, 0xb8, + 0x5b, 0x7b, 0x30, 0xfd, 0x64, 0xb5, 0x58, 0xff, 0x21, 0x3b, 0xb3, 0x59, 0xee, 0xa9, 0x5f, 0xa4, + 0x11, 0x67, 0xfa, 0x23, 0xd1, 0x12, 0x77, 0xe6, 0x2e, 0x62, 0xbb, 0x8a, 0xe8, 0x7f, 0x70, 0x90, + 0x21, 0x7a, 0x40, 0xba, 0x91, 0xfd, 0xc5, 0x61, 0x4f, 0xb5, 0x5b, 0x3b, 0x30, 0xc5, 0xfa, 0x42, + 0xaa, 0x59, 0x36, 0x2a, 0xbc, 0x6b, 0x3c, 0x43, 0xa1, 0x37, 0x00, 0xb0, 0x9f, 0xc1, 0x64, 0x16, + 0x30, 0x4c, 0xd4, 0x59, 0xac, 0x8a, 0x24, 0x95, 0x15, 0xdd, 0x36, 0x85, 0xa1, 0x43, 0xfd, 0xd4, + 0x12, 0x63, 0x43, 0x16, 0x53, 0x1e, 0xda, 0xd2, 0x10, 0xaf, 0x7a, 0xe7, 0x49, 0xa6, 0x93, 0x0f, + 0x45, 0x87, 0xf9, 0x7b, 0x0b, 0xe6, 0xd4, 0xb9, 0xed, 0x1f, 0xb9, 0x39, 0x98, 0xc0, 0x91, 0x88, + 0xb0, 0xe6, 0x4d, 0x60, 0xde, 0x09, 0x93, 0x47, 0x41, 0xb7, 0xa7, 0xae, 0xfc, 0x4b, 0x38, 0x21, + 0xb4, 0xed, 0xb7, 0xa1, 0x16, 0xd3, 0x8e, 0xba, 0xbf, 0xdc, 0x62, 0x66, 0x0c, 0x2f, 0x8b, 0x5c, + 0xdc, 0xfd, 0x8b, 0x05, 0xeb, 0x9a, 0xe7, 0xe8, 0xbd, 0x76, 0x46, 0x18, 0x0a, 0x19, 0x26, 0x49, + 0x65, 0x62, 0xf6, 0x13, 0x58, 0x0f, 0x7b, 0x59, 0xc6, 0xef, 0xab, 0x8c, 0x1c, 0x07, 0x89, 0x3f, + 0xe8, 0xf2, 0xe2, 0x31, 0xad, 0x1c, 0x69, 0x5d, 0x21, 0x7b, 0x1c, 0x58, 0x3b, 0xab, 0xcf, 0x96, + 0xfb, 0x06, 0x3c, 0x3c, 0x37, 0x16, 0x5d, 0x99, 0xbf, 0x4e, 0x80, 0xab, 0x47, 0x87, 0x41, 0xba, + 0x3a, 0xa3, 0xcb, 0x60, 0x2d, 0x0e, 0x4e, 0xfe, 0xff, 0x61, 0x3b, 0x71, 0x70, 0x52, 0x12, 0xb2, + 0xbd, 0x03, 0xf7, 0xc6, 0xda, 0x54, 0xfd, 0x22, 0xf8, 0x87, 0xd7, 0x28, 0x07, 0x92, 0xfd, 0xb0, + 0x0e, 0x33, 0x23, 0x44, 0xf2, 0xba, 0x37, 0x8d, 0x72, 0xf4, 0x71, 0x05, 0xa6, 0x30, 0xf5, 0x83, + 0x90, 0xbf, 0x73, 0x08, 0x92, 0x71, 0xd3, 0xbb, 0x89, 0xe9, 0xa6, 0x78, 0x76, 0xdf, 0x84, 0x47, + 0xe7, 0xa7, 0x54, 0x57, 0xe0, 0x4f, 0x16, 0xdc, 0x97, 0xc3, 0xb0, 0x9d, 0x91, 0x23, 0x1c, 0xa1, + 0xec, 0x19, 0xa6, 0x2c, 0xc3, 0x7b, 0x3d, 0x21, 0x7c, 0xd9, 0x39, 0xfd, 0x23, 0x58, 0x88, 0x72, + 0x38, 0x85, 0x69, 0xfd, 0xa8, 0xd8, 0x19, 0x63, 0x6c, 0xcf, 0x47, 0x23, 0x6b, 0xd4, 0x7d, 0x04, + 0x0f, 0xce, 0x77, 0x5a, 0x45, 0xf8, 0xdf, 0xfc, 0xa5, 0xcb, 0x19, 0xd2, 0xb7, 0x10, 0xba, 0xf4, + 0xa5, 0x1b, 0xc0, 0x62, 0x84, 0xf6, 0x83, 0x5e, 0x97, 0xf9, 0xf4, 0x38, 0x48, 0xfd, 0x7d, 0x94, + 0x7f, 0x55, 0xa8, 0x3c, 0xaa, 0x6d, 0x05, 0xa6, 0xdc, 0x12, 0x2f, 0x15, 0xdb, 0x30, 0xc3, 0xc8, + 0x21, 0x4a, 0x7c, 0xfd, 0x05, 0xb1, 0x66, 0x1a, 0x26, 0x4a, 0xe5, 0x03, 0x2e, 0xaa, 0xc2, 0x99, + 0x66, 0x83, 0x87, 0xa1, 0x4b, 0xb9, 0x10, 0xb5, 0x4c, 0xcc, 0x93, 0x4f, 0x6f, 0x41, 0xad, 0x45, + 0x3b, 0x76, 0x00, 0xb7, 0x8a, 0x9f, 0x0b, 0x2f, 0x30, 0xba, 0x9c, 0x47, 0x17, 0x18, 0x6f, 0xca, + 0x94, 0x9d, 0xc2, 0x82, 0xf1, 0x3b, 0xd8, 0xfd, 0xf3, 0x31, 0x84, 0xa0, 0xd3, 0xbc, 0xa0, 0xa0, + 0xb6, 0xe8, 0x01, 0xe4, 0xbe, 0x22, 0xad, 0x19, 0xd4, 0x07, 0xdb, 0xce, 0x97, 0xc7, 0x6e, 0x6b, + 0xcc, 0x1f, 0xc0, 0xcc, 0xd0, 0x57, 0x8e, 0x86, 0x41, 0x2d, 0x2f, 0xe0, 0xdc, 0x3f, 0x47, 0x40, + 0x23, 0x7f, 0x13, 0xae, 0x8b, 0x57, 0xb0, 0x25, 0x83, 0x02, 0xdf, 0x70, 0x1a, 0x25, 0x1b, 0x1a, + 0x21, 0x82, 0xd7, 0x46, 0xa8, 0xfe, 0x3d, 0x83, 0x52, 0x51, 0xc8, 0x79, 0xe3, 0x02, 0x42, 0xda, + 0xca, 0x01, 0xdc, 0x2a, 0x70, 0x5b, 0xfb, 0xa1, 0x41, 0xdf, 0xcc, 0xf3, 0x8d, 0x27, 0xa6, 0x84, + 0x2a, 0xdb, 0x0c, 0xe6, 0x0d, 0x84, 0xd2, 0x7e, 0xcb, 0x04, 0x51, 0x4a, 0xa7, 0x9d, 0x8d, 0x8b, + 0x8a, 0x0f, 0xe2, 0x2b, 0xd0, 0x42, 0x63, 0x7c, 0x66, 0x1e, 0x6b, 0x8c, 0xaf, 0x84, 0x65, 0xf2, + 0xa6, 0x2b, 0x7e, 0x79, 0x31, 0x35, 0x5d, 0x41, 0xc6, 0x68, 0xa2, 0xe4, 0xfb, 0x08, 0x3f, 0x12, + 0x23, 0xdf, 0x46, 0xee, 0x95, 0x26, 0x64, 0x20, 0x64, 0x3c, 0x12, 0x65, 0x5f, 0x10, 0xec, 0x9f, + 0xc1, 0x9d, 0xf2, 0x1f, 0x61, 0xde, 0x2c, 0x45, 0x32, 0x48, 0x3b, 0x6f, 0x57, 0x91, 0xce, 0xcf, + 0x16, 0x23, 0x57, 0x37, 0x35, 0x9f, 0x49, 0xd0, 0x38, 0x5b, 0xc6, 0xd1, 0x66, 0x7e, 0x09, 0xe4, + 0x79, 0xe6, 0xf8, 0x81, 0x90, 0x97, 0x34, 0x0e, 0x04, 0x13, 0x65, 0xb5, 0x7f, 0x6d, 0x41, 0xe3, + 0x3c, 0x56, 0xf4, 0xa4, 0x34, 0x5d, 0xa5, 0x3a, 0xce, 0x7b, 0xd5, 0x75, 0xb4, 0x4f, 0x1f, 0x5b, + 0x50, 0x3f, 0x87, 0xa3, 0x3e, 0x2e, 0x3d, 0x9e, 0x65, 0x2a, 0xce, 0xd7, 0x2b, 0xab, 0x68, 0x87, + 0x7e, 0x63, 0xc1, 0xda, 0x58, 0x0e, 0x60, 0xbf, 0x63, 0xee, 0xc8, 0x73, 0xa9, 0x8e, 0xf3, 0x6e, + 0x75, 0xc5, 0xe2, 0xe0, 0x1a, 0xba, 0x74, 0xc7, 0x0c, 0x2e, 0x13, 0x25, 0x19, 0x33, 0xb8, 0x8c, + 0x77, 0xf9, 0xd6, 0xe6, 0x27, 0x2f, 0xea, 0xd6, 0x67, 0x2f, 0xea, 0xd6, 0x3f, 0x5f, 0xd4, 0xad, + 0x5f, 0xbd, 0xac, 0x5f, 0xfb, 0xec, 0x65, 0xfd, 0xda, 0xdf, 0x5f, 0xd6, 0xaf, 0xfd, 0x30, 0xcf, + 0x70, 0x77, 0xf1, 0x7e, 0x78, 0x10, 0xe0, 0xa4, 0xd9, 0xff, 0x65, 0xf5, 0x44, 0xfc, 0xb6, 0x2a, + 0xf8, 0xc8, 0xde, 0x0d, 0xf1, 0xc3, 0xea, 0x57, 0xff, 0x17, 0x00, 0x00, 0xff, 0xff, 0x02, 0xb9, + 0xb5, 0x24, 0xd2, 0x1d, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -3962,9 +3963,9 @@ func (m *MsgUpdateSwapFeeParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, } } { - size := m.SwapFeeRate.Size() + size := m.DefaultSwapFeeRate.Size() i -= size - if _, err := m.SwapFeeRate.MarshalTo(dAtA[i:]); err != nil { + if _, err := m.DefaultSwapFeeRate.MarshalTo(dAtA[i:]); err != nil { return 0, err } i = encodeVarintTx(dAtA, i, uint64(size)) @@ -4539,7 +4540,7 @@ func (m *MsgUpdateSwapFeeParamsRequest) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } - l = m.SwapFeeRate.Size() + l = m.DefaultSwapFeeRate.Size() n += 1 + l + sovTx(uint64(l)) if len(m.TokenParams) > 0 { for _, e := range m.TokenParams { @@ -8259,7 +8260,7 @@ func (m *MsgUpdateSwapFeeParamsRequest) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SwapFeeRate", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field DefaultSwapFeeRate", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -8287,7 +8288,7 @@ func (m *MsgUpdateSwapFeeParamsRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.SwapFeeRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.DefaultSwapFeeRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/x/clp/types/types.go b/x/clp/types/types.go index 234a101b5f..48663dee92 100755 --- a/x/clp/types/types.go +++ b/x/clp/types/types.go @@ -18,21 +18,24 @@ func NewPool(externalAsset *Asset, nativeAssetBalance, externalAssetBalance, poo return pool } -func (p *Pool) ExtractValues(to Asset) (sdk.Uint, sdk.Uint, bool) { +func (p *Pool) ExtractValues(to Asset) (sdk.Uint, sdk.Uint, bool, Asset) { var X, Y sdk.Uint + var from Asset var toRowan bool if to.IsSettlementAsset() { Y = p.NativeAssetBalance X = p.ExternalAssetBalance toRowan = true + from = *p.ExternalAsset } else { X = p.NativeAssetBalance Y = p.ExternalAssetBalance toRowan = false + from = GetSettlementAsset() } - return X, Y, toRowan + return X, Y, toRowan, from } func (p *Pool) UpdateBalances(toRowan bool, X, x, Y, swapResult sdk.Uint) { diff --git a/x/ethbridge/client/cli/query.go b/x/ethbridge/client/cli/query.go index f7daf01125..153c1b04c3 100644 --- a/x/ethbridge/client/cli/query.go +++ b/x/ethbridge/client/cli/query.go @@ -39,6 +39,28 @@ func GetCmdGetEthBridgeProphecy() *cobra.Command { }, } } +func GetPauseStatus() *cobra.Command { + cmd := &cobra.Command{ + Use: "pause", + Short: "Query pause status", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + req := &types.QueryPauseRequest{} + res, err := queryClient.GetPauseStatus(context.Background(), req) + if err != nil { + return err + } + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} // GetCmdGetCrosschainFeeConfig queries crosschain fee config for a network func GetCmdGetCrosschainFeeConfig() *cobra.Command { diff --git a/x/ethbridge/client/cli/tx.go b/x/ethbridge/client/cli/tx.go index 32e07118ec..4d79efb3c2 100644 --- a/x/ethbridge/client/cli/tx.go +++ b/x/ethbridge/client/cli/tx.go @@ -2,6 +2,7 @@ package cli import ( "encoding/json" + "github.com/Sifchain/sifnode/x/ethbridge/utils" "os" "path/filepath" "regexp" @@ -21,6 +22,7 @@ import ( ) // GetCmdBurn is the CLI command for burning some of your eth and triggering an event +// //nolint:lll func GetCmdBurn() *cobra.Command { cmd := &cobra.Command{ @@ -431,3 +433,35 @@ func GetCmdSetBlacklist() *cobra.Command { return cmd } + +func GetCmdPause() *cobra.Command { + cmd := &cobra.Command{ + Use: "set-pause [pause]", + Short: "pause or unpause Lock and Burn transactions", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + isPaused, err := utils.ParseStringToBool(args[0]) + if err != nil { + return err + } + signer := clientCtx.GetFromAddress() + msg := types.MsgPause{ + Signer: signer.String(), + IsPaused: isPaused, + } + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/ethbridge/client/module_client.go b/x/ethbridge/client/module_client.go index ec69d68c02..100301c89c 100644 --- a/x/ethbridge/client/module_client.go +++ b/x/ethbridge/client/module_client.go @@ -22,16 +22,14 @@ func GetQueryCmd() *cobra.Command { ethBridgeQueryCmd.PersistentFlags().String(types.FlagEthereumChainID, "", "Ethereum chain ID") ethBridgeQueryCmd.PersistentFlags().String(types.FlagTokenContractAddr, "", "Token address representing a unique asset type") - flags.AddQueryFlagsToCmd(ethBridgeQueryCmd) - ethBridgeQueryCmd.AddCommand( cli.GetCmdGetEthBridgeProphecy(), cli.GetCmdGetCrosschainFeeConfig(), cli.GetEthereumLockBurnNonce(), cli.GetWitnessLockBurnSequence(), cli.GetCmdGetBlacklist(), - ) + cli.GetPauseStatus()) return ethBridgeQueryCmd } @@ -58,6 +56,7 @@ func GetTxCmd() *cobra.Command { cli.GetCmdSignProphecy(), cli.GetCmdUpdateConsensusNeeded(), cli.GetCmdSetBlacklist(), + cli.GetCmdPause(), ) return ethBridgeTxCmd diff --git a/x/ethbridge/handler.go b/x/ethbridge/handler.go index 0c2d91e344..628b31481d 100644 --- a/x/ethbridge/handler.go +++ b/x/ethbridge/handler.go @@ -48,6 +48,10 @@ func NewHandler(k Keeper) sdk.Handler { case *types.MsgSetBlacklist: res, err := msgServer.SetBlacklist(sdk.WrapSDKContext(ctx), msg) return sdk.WrapServiceResult(ctx, res, err) + case *types.MsgPause: + res, err := msgServer.SetPause(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + default: errMsg := fmt.Sprintf("unrecognized ethbridge message type: %v", sdk.MsgTypeURL(msg)) return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, errMsg) diff --git a/x/ethbridge/keeper/grpc_query.go b/x/ethbridge/keeper/grpc_query.go index d7c58986f1..7929b57037 100644 --- a/x/ethbridge/keeper/grpc_query.go +++ b/x/ethbridge/keeper/grpc_query.go @@ -15,6 +15,10 @@ type queryServer struct { Keeper } +func (srv queryServer) GetPauseStatus(ctx context.Context, _ *types.QueryPauseRequest) (*types.QueryPauseResponse, error) { + return &types.QueryPauseResponse{IsPaused: srv.Keeper.IsPaused(sdk.UnwrapSDKContext(ctx))}, nil +} + func (srv queryServer) GetBlacklist(ctx context.Context, _ *types.QueryBlacklistRequest) (*types.QueryBlacklistResponse, error) { sdkCtx := sdk.UnwrapSDKContext(ctx) diff --git a/x/ethbridge/keeper/msg_server.go b/x/ethbridge/keeper/msg_server.go index 15009f12e5..edf6ebb95b 100644 --- a/x/ethbridge/keeper/msg_server.go +++ b/x/ethbridge/keeper/msg_server.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + admintypes "github.com/Sifchain/sifnode/x/admin/types" "strconv" "github.com/Sifchain/sifnode/x/instrumentation" @@ -29,8 +30,26 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer { var _ types.MsgServer = msgServer{} +func (srv msgServer) SetPause(goCtx context.Context, msg *types.MsgPause) (*types.MsgPauseResponse, error) { + response := &types.MsgPauseResponse{} + ctx := sdk.UnwrapSDKContext(goCtx) + signer, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return response, err + } + if !srv.adminKeeper.IsAdminAccount(ctx, admintypes.AdminType_ETHBRIDGE, signer) { + return response, types.ErrNotEnoughPermissions + } + + srv.Keeper.SetPause(ctx, &types.Pause{IsPaused: msg.IsPaused}) + return response, nil +} + func (srv msgServer) Lock(goCtx context.Context, msg *types.MsgLock) (*types.MsgLockResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + if srv.Keeper.IsPaused(ctx) { + return nil, types.ErrPaused + } logger := srv.Keeper.Logger(ctx) instrumentation.PeggyCheckpoint(logger, instrumentation.Lock, "msg", zap.Reflect("message", msg)) @@ -106,8 +125,12 @@ func (srv msgServer) Lock(goCtx context.Context, msg *types.MsgLock) (*types.Msg } func (srv msgServer) Burn(goCtx context.Context, msg *types.MsgBurn) (*types.MsgBurnResponse, error) { + response := &types.MsgBurnResponse{} ctx := sdk.UnwrapSDKContext(goCtx) logger := srv.Keeper.Logger(ctx) + if srv.Keeper.IsPaused(ctx) { + return response, types.ErrPaused + } instrumentation.PeggyCheckpoint(logger, instrumentation.Burn, "msg", zap.Reflect("message", msg)) @@ -120,7 +143,7 @@ func (srv msgServer) Burn(goCtx context.Context, msg *types.MsgBurn) (*types.Msg account := srv.Keeper.accountKeeper.GetAccount(ctx, cosmosSender) if account == nil { logger.Error("account is nil.", "CosmosSender", msg.CosmosSender) - return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.CosmosSender) + return response, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.CosmosSender) } tokenMetadata, ok := srv.Keeper.GetTokenMetadata(ctx, msg.DenomHash) @@ -137,7 +160,7 @@ func (srv msgServer) Burn(goCtx context.Context, msg *types.MsgBurn) (*types.Msg if err != nil { logger.Error("bridge keeper failed to process burn.", errorMessageKey, err.Error()) - return nil, err + return response, err } srv.Keeper.UpdateGlobalSequence(ctx, msg.NetworkDescriptor, uint64(ctx.BlockHeight())) @@ -197,7 +220,8 @@ func (srv msgServer) Burn(goCtx context.Context, msg *types.MsgBurn) (*types.Msg ), }) - return &types.MsgBurnResponse{}, nil + return response, nil + } func (srv msgServer) CreateEthBridgeClaim(goCtx context.Context, msg *types.MsgCreateEthBridgeClaim) (*types.MsgCreateEthBridgeClaimResponse, error) { diff --git a/x/ethbridge/keeper/msg_server_test.go b/x/ethbridge/keeper/msg_server_test.go new file mode 100644 index 0000000000..d5c6cd52b8 --- /dev/null +++ b/x/ethbridge/keeper/msg_server_test.go @@ -0,0 +1,267 @@ +package keeper_test + +import ( + "testing" + + adminTypes "github.com/Sifchain/sifnode/x/admin/types" + ethbriddgeKeeper "github.com/Sifchain/sifnode/x/ethbridge/keeper" + "github.com/Sifchain/sifnode/x/ethbridge/test" + "github.com/Sifchain/sifnode/x/ethbridge/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +const TestToken = "ceth" + +func TestMsgServer_Lock_No_Pause_Set(t *testing.T) { + t.Skip("pausing tests not implemented for peggy2") + ctx, app := test.CreateSimulatorApp(false) + addresses, _ := test.CreateTestAddrs(2) + admin := addresses[0] + // nonAdmin := addresses[1] + msg := types.NewMsgLock(1, admin, ethereumSender, amount, "stake", amount) + coins := sdk.NewCoins(sdk.NewCoin("stake", amount), sdk.NewCoin(TestToken, amount)) + _ = app.BankKeeper.MintCoins(ctx, types.ModuleName, coins) + _ = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, admin, coins) + app.AdminKeeper.SetAdminAccount(ctx, &adminTypes.AdminAccount{ + AdminType: adminTypes.AdminType_ETHBRIDGE, + AdminAddress: admin.String(), + }) + msgServer := ethbriddgeKeeper.NewMsgServerImpl(app.EthbridgeKeeper) + + _, err := msgServer.Lock(sdk.WrapSDKContext(ctx), &msg) + require.NoError(t, err) +} + +func TestMsgServer_Lock(t *testing.T) { + t.Skip("pausing tests not implemented for peggy2") + ctx, app := test.CreateSimulatorApp(false) + addresses, _ := test.CreateTestAddrs(2) + admin := addresses[0] + nonAdmin := addresses[1] + msg := types.NewMsgLock(1, admin, ethereumSender, amount, "stake", amount) + coins := sdk.NewCoins(sdk.NewCoin("stake", amount), sdk.NewCoin(TestToken, amount)) + _ = app.BankKeeper.MintCoins(ctx, types.ModuleName, coins) + _ = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, admin, coins) + app.AdminKeeper.SetAdminAccount(ctx, &adminTypes.AdminAccount{ + AdminType: adminTypes.AdminType_ETHBRIDGE, + AdminAddress: admin.String(), + }) + msgPauseNonAdmin := types.MsgPause{ + Signer: nonAdmin.String(), + IsPaused: true, + } + msgPause := types.MsgPause{ + Signer: admin.String(), + IsPaused: true, + } + msgUnPause := types.MsgPause{ + Signer: admin.String(), + IsPaused: false, + } + msgServer := ethbriddgeKeeper.NewMsgServerImpl(app.EthbridgeKeeper) + // Pause with Non Admin Account + _, err := msgServer.SetPause(sdk.WrapSDKContext(ctx), &msgPauseNonAdmin) + require.Error(t, err) + + // Pause Transactions + _, err = msgServer.SetPause(sdk.WrapSDKContext(ctx), &msgPause) + require.NoError(t, err) + + // Fail Lock + _, err = msgServer.Lock(sdk.WrapSDKContext(ctx), &msg) + require.Error(t, err) + + // Unpause Transactions + _, err = msgServer.SetPause(sdk.WrapSDKContext(ctx), &msgUnPause) + require.NoError(t, err) + + // Lock Success + _, err = msgServer.Lock(sdk.WrapSDKContext(ctx), &msg) + require.NoError(t, err) +} + +func TestMsgServer_Burn(t *testing.T) { + t.Skip("pausing tests not implemented for peggy2") + ctx, app := test.CreateSimulatorApp(false) + addresses, _ := test.CreateTestAddrs(1) + admin := addresses[0] + coins := sdk.NewCoins(sdk.NewCoin("stake", amount), sdk.NewCoin(TestToken, amount)) + _ = app.BankKeeper.MintCoins(ctx, types.ModuleName, coins) + _ = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, admin, coins) + app.AdminKeeper.SetAdminAccount(ctx, &adminTypes.AdminAccount{ + AdminType: adminTypes.AdminType_ETHBRIDGE, + AdminAddress: admin.String(), + }) + //app.EthbridgeKeeper.AddPeggyToken(ctx, "stake") + msg := types.NewMsgBurn(1, admin, ethereumSender, amount, "stake", amount) + msgPause := types.MsgPause{ + Signer: admin.String(), + IsPaused: true, + } + msgUnPause := types.MsgPause{ + Signer: admin.String(), + IsPaused: false, + } + msgServer := ethbriddgeKeeper.NewMsgServerImpl(app.EthbridgeKeeper) + + // Pause Transactions + _, err := msgServer.SetPause(sdk.WrapSDKContext(ctx), &msgPause) + require.NoError(t, err) + + // Fail Burn + _, err = msgServer.Burn(sdk.WrapSDKContext(ctx), &msg) + require.Error(t, err) + + // Unpause Transactions + _, err = msgServer.SetPause(sdk.WrapSDKContext(ctx), &msgUnPause) + require.NoError(t, err) + + // Burn Success + _, err = msgServer.Burn(sdk.WrapSDKContext(ctx), &msg) + require.NoError(t, err) +} + +func TestRedundantSetUnpauseValid(t *testing.T) { + t.Skip("pausing tests not implemented for peggy2") + ctx, app := test.CreateSimulatorApp(false) + addresses, _ := test.CreateTestAddrs(2) + admin := addresses[0] + testAccount := addresses[1] + coins := sdk.NewCoins( + sdk.NewCoin("stake", amount), + sdk.NewCoin(TestToken, amount)) + + _ = app.BankKeeper.MintCoins(ctx, types.ModuleName, coins) + _ = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, testAccount, coins) + + app.AdminKeeper.SetAdminAccount(ctx, &adminTypes.AdminAccount{ + AdminType: adminTypes.AdminType_ETHBRIDGE, + AdminAddress: admin.String(), + }) + + msgServer := ethbriddgeKeeper.NewMsgServerImpl(app.EthbridgeKeeper) + msgUnPause := types.MsgPause{ + Signer: admin.String(), + IsPaused: false, + } + + _, err := msgServer.SetPause(sdk.WrapSDKContext(ctx), &msgUnPause) + require.NoError(t, err) + + msgLock := types.NewMsgLock( + 1, + testAccount, ethereumSender, + amount, "stake", amount) + + _, err = msgServer.Lock(sdk.WrapSDKContext(ctx), &msgLock) + require.NoError(t, err) +} + +func TestSetPauseIdempotent(t *testing.T) { + ctx, app := test.CreateSimulatorApp(false) + addresses, _ := test.CreateTestAddrs(2) + admin := addresses[0] + testAccount := addresses[1] + coins := sdk.NewCoins( + sdk.NewCoin("stake", amount), + sdk.NewCoin(TestToken, amount)) + + _ = app.BankKeeper.MintCoins(ctx, types.ModuleName, coins) + _ = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, testAccount, coins) + + app.AdminKeeper.SetAdminAccount(ctx, &adminTypes.AdminAccount{ + AdminType: adminTypes.AdminType_ETHBRIDGE, + AdminAddress: admin.String(), + }) + + msgServer := ethbriddgeKeeper.NewMsgServerImpl(app.EthbridgeKeeper) + msgPause := types.MsgPause{ + Signer: admin.String(), + IsPaused: true, + } + + _, err := msgServer.SetPause(sdk.WrapSDKContext(ctx), &msgPause) + require.NoError(t, err) + + _, err = msgServer.SetPause(sdk.WrapSDKContext(ctx), &msgPause) + require.NoError(t, err) + + msgLock := types.NewMsgLock( + 1, + testAccount, ethereumSender, + amount, "stake", amount) + + _, err = msgServer.Lock(sdk.WrapSDKContext(ctx), &msgLock) + require.Error(t, err, types.ErrPaused) +} + +func TestSetUnpauseIdempotent(t *testing.T) { + t.Skip("pausing tests not implemented for peggy2") + ctx, app := test.CreateSimulatorApp(false) + addresses, _ := test.CreateTestAddrs(2) + admin := addresses[0] + testAccount := addresses[1] + coins := sdk.NewCoins( + sdk.NewCoin("stake", amount), + sdk.NewCoin(TestToken, amount)) + + _ = app.BankKeeper.MintCoins(ctx, types.ModuleName, coins) + _ = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, testAccount, coins) + + app.AdminKeeper.SetAdminAccount(ctx, &adminTypes.AdminAccount{ + AdminType: adminTypes.AdminType_ETHBRIDGE, + AdminAddress: admin.String(), + }) + + msgServer := ethbriddgeKeeper.NewMsgServerImpl(app.EthbridgeKeeper) + msgUnPause := types.MsgPause{ + Signer: admin.String(), + IsPaused: false, + } + + _, err := msgServer.SetPause(sdk.WrapSDKContext(ctx), &msgUnPause) + require.NoError(t, err) + + _, err = msgServer.SetPause(sdk.WrapSDKContext(ctx), &msgUnPause) + require.NoError(t, err) + + msgLock := types.NewMsgLock( + 1, + testAccount, ethereumSender, + amount, "stake", amount) + + _, err = msgServer.Lock(sdk.WrapSDKContext(ctx), &msgLock) + require.NoError(t, err) + +} + +func TestSetUnPauseWithNonAdminFails(t *testing.T) { + ctx, app := test.CreateSimulatorApp(false) + addresses, _ := test.CreateTestAddrs(1) + non_admin := addresses[0] + + msgServer := ethbriddgeKeeper.NewMsgServerImpl(app.EthbridgeKeeper) + msgUnPause := types.MsgPause{ + Signer: non_admin.String(), + IsPaused: false, + } + + _, err := msgServer.SetPause(sdk.WrapSDKContext(ctx), &msgUnPause) + require.Error(t, err, types.ErrNotEnoughPermissions) +} + +func TestSetPauseWithNonAdminFails(t *testing.T) { + ctx, app := test.CreateSimulatorApp(false) + addresses, _ := test.CreateTestAddrs(1) + non_admin := addresses[0] + + msgServer := ethbriddgeKeeper.NewMsgServerImpl(app.EthbridgeKeeper) + msgPause := types.MsgPause{ + Signer: non_admin.String(), + IsPaused: true, + } + + _, err := msgServer.SetPause(sdk.WrapSDKContext(ctx), &msgPause) + require.Error(t, err, types.ErrNotEnoughPermissions) +} diff --git a/x/ethbridge/keeper/pauser.go b/x/ethbridge/keeper/pauser.go new file mode 100644 index 0000000000..f0708bfeab --- /dev/null +++ b/x/ethbridge/keeper/pauser.go @@ -0,0 +1,23 @@ +package keeper + +import ( + "github.com/Sifchain/sifnode/x/ethbridge/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (k Keeper) SetPause(ctx sdk.Context, pause *types.Pause) { + store := ctx.KVStore(k.storeKey) + store.Set(types.PausePrefix, k.cdc.MustMarshal(pause)) +} + +func (k Keeper) getPause(ctx sdk.Context) *types.Pause { + pause := types.Pause{} + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.PausePrefix) + k.cdc.MustUnmarshal(bz, &pause) + return &pause +} + +func (k Keeper) IsPaused(ctx sdk.Context) bool { + return k.getPause(ctx).IsPaused +} diff --git a/x/ethbridge/keeper/pauser_test.go b/x/ethbridge/keeper/pauser_test.go new file mode 100644 index 0000000000..497d015fa6 --- /dev/null +++ b/x/ethbridge/keeper/pauser_test.go @@ -0,0 +1,33 @@ +package keeper_test + +import ( + "testing" + + "github.com/Sifchain/sifnode/x/ethbridge/test" + "github.com/Sifchain/sifnode/x/ethbridge/types" + "github.com/stretchr/testify/assert" +) + +func TestSetPause(t *testing.T) { + ctx, app := test.CreateSimulatorApp(false) + + // test the default value before any setting + paused := app.EthbridgeKeeper.IsPaused(ctx) + assert.False(t, paused) + + // pause + app.EthbridgeKeeper.SetPause(ctx, &types.Pause{ + IsPaused: true, + }) + + paused = app.EthbridgeKeeper.IsPaused(ctx) + assert.True(t, paused) + + // unpause + app.EthbridgeKeeper.SetPause(ctx, &types.Pause{ + IsPaused: false, + }) + + paused = app.EthbridgeKeeper.IsPaused(ctx) + assert.False(t, paused) +} diff --git a/x/ethbridge/keeper/querier.go b/x/ethbridge/keeper/querier.go index 81965a8549..3cee9303e6 100644 --- a/x/ethbridge/keeper/querier.go +++ b/x/ethbridge/keeper/querier.go @@ -24,6 +24,8 @@ func NewLegacyQuerier(keeper Keeper, cdc *codec.LegacyAmino) sdk.Querier { return legacyQueryCrosschainFeeConfig(ctx, cdc, req, keeper) case types.QueryBlacklist: return legacyQueryBlacklist(ctx, cdc, req, keeper) + case types.QueryPause: + return legacyQueryPause(ctx, cdc, req, keeper) default: return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown ethbridge query endpoint") } @@ -80,3 +82,16 @@ func legacyQueryBlacklist(ctx sdk.Context, cdc *codec.LegacyAmino, query abci.Re return cdc.MarshalJSONIndent(response, "", " ") } + +func legacyQueryPause(ctx sdk.Context, cdc *codec.LegacyAmino, query abci.RequestQuery, keeper Keeper) ([]byte, error) { //nolint + var req types.QueryPauseRequest + if err := cdc.UnmarshalJSON(query.Data, &req); err != nil { + return nil, sdkerrors.Wrap(types.ErrJSONMarshalling, fmt.Sprintf("failed to parse req: %s", err.Error())) + } + queryServer := NewQueryServer(keeper) + response, err := queryServer.GetPauseStatus(sdk.WrapSDKContext(ctx), &req) + if err != nil { + return nil, err + } + return cdc.MarshalJSONIndent(response, "", " ") +} diff --git a/x/ethbridge/test/test_helpers.go b/x/ethbridge/test/test_helpers.go index e1dc480f54..e5a61aaa4d 100644 --- a/x/ethbridge/test/test_helpers.go +++ b/x/ethbridge/test/test_helpers.go @@ -3,6 +3,7 @@ package test import ( "bytes" "encoding/hex" + tokenregistryTypes "github.com/Sifchain/sifnode/x/tokenregistry/types" "math/rand" "time" @@ -201,6 +202,15 @@ func CreateTestKeepers(t *testing.T, consensusNeeded float64, validatorAmounts [ return ctx, ethbridgeKeeper, bankKeeper, accountKeeper, oracleKeeper, encCfg, whitelist, valAddrsInOrder } +func CreateTestApp(isCheckTx bool) (*app.SifchainApp, sdk.Context) { + sifchainApp := app.Setup(isCheckTx) + ctx := sifchainApp.BaseApp.NewContext(isCheckTx, tmproto.Header{}) + sifchainApp.AccountKeeper.SetParams(ctx, authtypes.DefaultParams()) + initTokens := sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction) + _ = app.AddTestAddrs(sifchainApp, ctx, 6, initTokens) + return sifchainApp, ctx +} + // nolint: unparam func CreateTestAddrs(numAddrs int) ([]sdk.AccAddress, []sdk.ValAddress) { var addresses []sdk.AccAddress @@ -256,13 +266,21 @@ const ( ) // // returns context and app with params set on account keeper -func CreateTestApp(isCheckTx bool) (*app.SifchainApp, sdk.Context) { - sifapp := app.Setup(isCheckTx) - ctx := sifapp.BaseApp.NewContext(isCheckTx, tmproto.Header{}) - sifapp.AccountKeeper.SetParams(ctx, authtypes.DefaultParams()) - initTokens := sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction) - _ = app.AddTestAddrs(sifapp, ctx, 6, initTokens) - return sifapp, ctx +func CreateSimulatorApp(isCheckTx bool) (sdk.Context, *app.SifchainApp) { + sifchainApp := app.Setup(isCheckTx) + ctx := sifchainApp.BaseApp.NewContext(isCheckTx, tmproto.Header{}) + sifchainApp.TokenRegistryKeeper.SetRegistry(ctx, tokenregistryTypes.Registry{ + Entries: []*tokenregistryTypes.RegistryEntry{ + {Denom: "ceth", Decimals: 18, Permissions: []tokenregistryTypes.Permission{tokenregistryTypes.Permission_CLP}}, + {Denom: "cdash", Decimals: 18, Permissions: []tokenregistryTypes.Permission{tokenregistryTypes.Permission_CLP}}, + {Denom: "eth", Decimals: 18, Permissions: []tokenregistryTypes.Permission{tokenregistryTypes.Permission_CLP}}, + {Denom: "cacoin", Decimals: 18, Permissions: []tokenregistryTypes.Permission{tokenregistryTypes.Permission_CLP}}, + {Denom: "dash", Decimals: 18, Permissions: []tokenregistryTypes.Permission{tokenregistryTypes.Permission_CLP}}, + {Denom: "atom", Decimals: 18, Permissions: []tokenregistryTypes.Permission{tokenregistryTypes.Permission_CLP}}, + {Denom: "cusdc", Decimals: 18, Permissions: []tokenregistryTypes.Permission{tokenregistryTypes.Permission_CLP}}, + }, + }) + return ctx, sifchainApp } func CreateTestAppEthBridge(isCheckTx bool) (sdk.Context, ethbridgekeeper.Keeper) { diff --git a/x/ethbridge/types/errors.go b/x/ethbridge/types/errors.go index 59c19e3638..e2b97442a4 100644 --- a/x/ethbridge/types/errors.go +++ b/x/ethbridge/types/errors.go @@ -13,6 +13,8 @@ var ( ErrInvalidEthereumChainID = sdkerrors.Register(ModuleName, 6, "invalid ethereum chain id") ErrInvalidAmount = sdkerrors.Register(ModuleName, 7, "amount must be a valid integer > 0") ErrInvalidSymbol = sdkerrors.Register(ModuleName, 8, "symbol must be 1 character or more") - // Set as 11 because 9 and 10 are present on master branch - ErrBlacklistedAddress = sdkerrors.Register(ModuleName, 11, "Ethereum Address is in blocklist, cannot execute tx") + ErrCethAmount = sdkerrors.Register(ModuleName, 10, "not enough ceth provided") + ErrNotEnoughPermissions = sdkerrors.Register(ModuleName, 11, "account does not have enough permissions") + ErrPaused = sdkerrors.Register(ModuleName, 12, "transaction is paused") + ErrBlacklistedAddress = sdkerrors.Register(ModuleName, 13, "Ethereum Address is in blocklist, cannot execute tx") ) diff --git a/x/ethbridge/types/keys.go b/x/ethbridge/types/keys.go index a4954d56c6..7fdb58c74f 100644 --- a/x/ethbridge/types/keys.go +++ b/x/ethbridge/types/keys.go @@ -21,8 +21,9 @@ var ( PeggyTokenKeyPrefix = []byte{0x00} CrossChainFeeReceiverAccountPrefix = []byte{0x01} BlacklistPrefix = []byte{0x02} - GlobalNoncePrefix = []byte{0x03} - EthereumLockBurnSequencePrefix = []byte{0x04} - GlobalNonceToBlockNumberPrefix = []byte{0x05} - FirstLockDoublePegPrefix = []byte{0x06} + PausePrefix = []byte{0x03} + GlobalNoncePrefix = []byte{0x04} + EthereumLockBurnSequencePrefix = []byte{0x05} + GlobalNonceToBlockNumberPrefix = []byte{0x06} + FirstLockDoublePegPrefix = []byte{0x07} ) diff --git a/x/ethbridge/types/msgs.go b/x/ethbridge/types/msgs.go index f95aa803bd..4075bbb256 100644 --- a/x/ethbridge/types/msgs.go +++ b/x/ethbridge/types/msgs.go @@ -12,6 +12,33 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) +var _ sdk.Msg = &MsgPause{} + +// Route should return the name of the module +func (msg MsgPause) Route() string { return RouterKey } + +// Type should return the action +func (msg MsgPause) Type() string { return "pause" } + +// ValidateBasic runs stateless checks on the message +func (msg MsgPause) ValidateBasic() error { + if msg.GetSigner() == "" { + return sdkerrors.ErrInvalidAddress + } + return nil +} + +// GetSignBytes encodes the message for signing +func (msg MsgPause) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +// GetSigners defines whose signature is required +func (msg MsgPause) GetSigners() []sdk.AccAddress { + signer := sdk.MustAccAddressFromBech32(msg.Signer) + return []sdk.AccAddress{signer} +} + // NewMsgLock is a constructor function for MsgLock func NewMsgLock( networkDescriptor oracletypes.NetworkDescriptor, cosmosSender sdk.AccAddress, @@ -77,11 +104,7 @@ func (msg MsgLock) GetSignBytes() []byte { // GetSigners defines whose signature is required func (msg MsgLock) GetSigners() []sdk.AccAddress { - cosmosSender, err := sdk.AccAddressFromBech32(msg.CosmosSender) - if err != nil { - panic(err) - } - + cosmosSender := sdk.MustAccAddressFromBech32(msg.CosmosSender) return []sdk.AccAddress{cosmosSender} } @@ -179,11 +202,7 @@ func (msg MsgBurn) GetSignBytes() []byte { // GetSigners defines whose signature is required func (msg MsgBurn) GetSigners() []sdk.AccAddress { - cosmosSender, err := sdk.AccAddressFromBech32(msg.CosmosSender) - if err != nil { - panic(err) - } - + cosmosSender := sdk.MustAccAddressFromBech32(msg.CosmosSender) return []sdk.AccAddress{cosmosSender} } @@ -447,11 +466,7 @@ func (msg MsgRescueCrossChainFee) GetSignBytes() []byte { // GetSigners defines whose signature is required func (msg MsgRescueCrossChainFee) GetSigners() []sdk.AccAddress { - cosmosSender, err := sdk.AccAddressFromBech32(msg.CosmosSender) - if err != nil { - panic(err) - } - + cosmosSender := sdk.MustAccAddressFromBech32(msg.CosmosSender) return []sdk.AccAddress{cosmosSender} } @@ -497,11 +512,7 @@ func (msg MsgUpdateWhiteListValidator) GetSignBytes() []byte { // GetSigners defines whose signature is required func (msg MsgUpdateWhiteListValidator) GetSigners() []sdk.AccAddress { - cosmosSender, err := sdk.AccAddressFromBech32(msg.CosmosSender) - if err != nil { - panic(err) - } - + cosmosSender := sdk.MustAccAddressFromBech32(msg.CosmosSender) return []sdk.AccAddress{cosmosSender} } @@ -687,10 +698,6 @@ func (msg *MsgSetBlacklist) ValidateBasic() error { } func (msg *MsgSetBlacklist) GetSigners() []sdk.AccAddress { - from, err := sdk.AccAddressFromBech32(msg.From) - if err != nil { - panic(err) - } - + from := sdk.MustAccAddressFromBech32(msg.From) return []sdk.AccAddress{from} } diff --git a/x/ethbridge/types/msgs_test.go b/x/ethbridge/types/msgs_test.go index 5bee01f9d5..55bc9f9039 100644 --- a/x/ethbridge/types/msgs_test.go +++ b/x/ethbridge/types/msgs_test.go @@ -8,9 +8,11 @@ import ( cosmosbridge "github.com/Sifchain/sifnode/cmd/ebrelayer/contract/generated/artifacts/contracts/CosmosBridge.sol" oracletypes "github.com/Sifchain/sifnode/x/oracle/types" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" crypto "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/assert" ) @@ -157,3 +159,9 @@ func TestMsgSetBlacklistValidateBasic(t *testing.T) { assert.NoError(t, msgSetBlacklist.ValidateBasic()) } + +func TestPauseMsgEmptySignerReturnsError(t *testing.T) { + msgPause := MsgPause{Signer: "", IsPaused: true} + err := msgPause.ValidateBasic() + assert.Error(t, err, sdkerrors.ErrInvalidAddress) +} diff --git a/x/ethbridge/types/querier.go b/x/ethbridge/types/querier.go index b54e620f3f..fa329f5413 100644 --- a/x/ethbridge/types/querier.go +++ b/x/ethbridge/types/querier.go @@ -14,6 +14,7 @@ const ( QueryPropheciesCompleted = "propheciesCompleted" QueryBlacklist = "blacklist" + QueryPause = "pause" ) // NewQueryEthProphecyRequest creates a new QueryEthProphecyParams diff --git a/x/ethbridge/types/query.pb.go b/x/ethbridge/types/query.pb.go index 233360d3a0..3dd2ebe529 100644 --- a/x/ethbridge/types/query.pb.go +++ b/x/ethbridge/types/query.pb.go @@ -704,6 +704,86 @@ func (m *QueryBlacklistResponse) GetAddresses() []string { return nil } +type QueryPauseRequest struct { +} + +func (m *QueryPauseRequest) Reset() { *m = QueryPauseRequest{} } +func (m *QueryPauseRequest) String() string { return proto.CompactTextString(m) } +func (*QueryPauseRequest) ProtoMessage() {} +func (*QueryPauseRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_7077edcf9f792b78, []int{14} +} +func (m *QueryPauseRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPauseRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPauseRequest.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 *QueryPauseRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPauseRequest.Merge(m, src) +} +func (m *QueryPauseRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryPauseRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPauseRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPauseRequest proto.InternalMessageInfo + +type QueryPauseResponse struct { + IsPaused bool `protobuf:"varint,1,opt,name=is_paused,json=isPaused,proto3" json:"is_paused,omitempty"` +} + +func (m *QueryPauseResponse) Reset() { *m = QueryPauseResponse{} } +func (m *QueryPauseResponse) String() string { return proto.CompactTextString(m) } +func (*QueryPauseResponse) ProtoMessage() {} +func (*QueryPauseResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_7077edcf9f792b78, []int{15} +} +func (m *QueryPauseResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPauseResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPauseResponse.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 *QueryPauseResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPauseResponse.Merge(m, src) +} +func (m *QueryPauseResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryPauseResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPauseResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPauseResponse proto.InternalMessageInfo + +func (m *QueryPauseResponse) GetIsPaused() bool { + if m != nil { + return m.IsPaused + } + return false +} + func init() { proto.RegisterType((*QueryEthProphecyRequest)(nil), "sifnode.ethbridge.v1.QueryEthProphecyRequest") proto.RegisterType((*QueryEthProphecyResponse)(nil), "sifnode.ethbridge.v1.QueryEthProphecyResponse") @@ -719,65 +799,71 @@ func init() { proto.RegisterType((*QueryGlobalSequenceBlockNumberResponse)(nil), "sifnode.ethbridge.v1.QueryGlobalSequenceBlockNumberResponse") proto.RegisterType((*QueryBlacklistRequest)(nil), "sifnode.ethbridge.v1.QueryBlacklistRequest") proto.RegisterType((*QueryBlacklistResponse)(nil), "sifnode.ethbridge.v1.QueryBlacklistResponse") + proto.RegisterType((*QueryPauseRequest)(nil), "sifnode.ethbridge.v1.QueryPauseRequest") + proto.RegisterType((*QueryPauseResponse)(nil), "sifnode.ethbridge.v1.QueryPauseResponse") } func init() { proto.RegisterFile("sifnode/ethbridge/v1/query.proto", fileDescriptor_7077edcf9f792b78) } var fileDescriptor_7077edcf9f792b78 = []byte{ - // 846 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x56, 0xcf, 0x6f, 0xe3, 0x44, - 0x14, 0xce, 0xec, 0x66, 0xb7, 0x9b, 0x49, 0xe8, 0x76, 0xdd, 0x5d, 0x62, 0x0c, 0x9b, 0x06, 0xb3, - 0x2c, 0xe1, 0xc7, 0x3a, 0x6a, 0xd0, 0xae, 0xc4, 0x16, 0x24, 0x48, 0x0a, 0x55, 0x0b, 0xaa, 0xc0, - 0x41, 0x45, 0xea, 0xc5, 0xf2, 0x8f, 0x49, 0x62, 0xc5, 0xf1, 0xb8, 0x33, 0xe3, 0xb4, 0xb9, 0x70, - 0xe4, 0x8c, 0x90, 0x90, 0xb8, 0x71, 0x40, 0x5c, 0xe0, 0x7f, 0xe0, 0xcc, 0xb1, 0x47, 0x8e, 0xa8, - 0xfd, 0x47, 0x50, 0xc6, 0x63, 0xa7, 0x4d, 0x6c, 0xb7, 0x54, 0xaa, 0xd4, 0x5b, 0xf2, 0xe6, 0x9b, - 0xef, 0x7d, 0xef, 0xf3, 0x9b, 0x79, 0x03, 0xeb, 0xd4, 0xed, 0xf9, 0xd8, 0x41, 0x4d, 0xc4, 0x06, - 0x16, 0x71, 0x9d, 0x3e, 0x6a, 0x8e, 0xd7, 0x9b, 0x07, 0x21, 0x22, 0x13, 0x2d, 0x20, 0x98, 0x61, - 0xe9, 0xa1, 0x40, 0x68, 0x09, 0x42, 0x1b, 0xaf, 0x2b, 0x0f, 0xfb, 0xb8, 0x8f, 0x39, 0xa0, 0x39, - 0xfd, 0x15, 0x61, 0x95, 0x74, 0x36, 0x36, 0x09, 0x10, 0x15, 0x88, 0xc7, 0x31, 0x02, 0x13, 0xd3, - 0xf6, 0x16, 0x96, 0xdf, 0x5b, 0x5c, 0xf6, 0x11, 0x3b, 0xc4, 0x64, 0x68, 0x38, 0x88, 0xda, 0xc4, - 0x0d, 0x18, 0x26, 0x11, 0x56, 0xfd, 0x14, 0x56, 0xbf, 0x99, 0xea, 0xfc, 0x9c, 0x0d, 0xbe, 0x26, - 0x38, 0x18, 0x20, 0x7b, 0xa2, 0xa3, 0x83, 0x10, 0x51, 0x26, 0xad, 0xc1, 0x72, 0x20, 0x42, 0x86, - 0xeb, 0xc8, 0x4b, 0x75, 0xd0, 0xa8, 0xe8, 0x30, 0x0e, 0x6d, 0x3b, 0x3b, 0xc5, 0x7b, 0x60, 0x65, - 0x49, 0xfd, 0x0d, 0x40, 0x79, 0x91, 0x82, 0x06, 0xd8, 0xa7, 0x68, 0x9e, 0x03, 0xcc, 0x73, 0x48, - 0xcf, 0xe1, 0x5d, 0xca, 0x4c, 0x16, 0x52, 0xb9, 0x58, 0x07, 0x8d, 0xe5, 0xd6, 0x63, 0x2d, 0x76, - 0x2a, 0x12, 0xaf, 0x8d, 0xd7, 0xb5, 0x2e, 0x07, 0x7c, 0x8b, 0x8e, 0x98, 0x2e, 0xc0, 0xd2, 0xbb, - 0x70, 0xc5, 0xf6, 0x4c, 0x77, 0x64, 0x8c, 0x4d, 0xcf, 0x75, 0x4c, 0x86, 0x09, 0x95, 0xef, 0xd4, - 0x6f, 0x37, 0x4a, 0xfa, 0x7d, 0x1e, 0xdf, 0x4b, 0xc2, 0x3b, 0xc5, 0x7b, 0xb7, 0x56, 0x8a, 0xea, - 0x18, 0xae, 0x71, 0x91, 0x1d, 0x82, 0x29, 0xb5, 0x07, 0xa6, 0xeb, 0x7f, 0x81, 0x50, 0x07, 0xfb, - 0x3d, 0xb7, 0x1f, 0xd7, 0xdb, 0x85, 0xd2, 0xa2, 0x4d, 0x5c, 0xf2, 0x72, 0xeb, 0x49, 0x8a, 0xac, - 0xdd, 0x08, 0xbc, 0x99, 0x60, 0xf5, 0x07, 0xfe, 0x7c, 0x48, 0xfd, 0x1e, 0xd6, 0xb3, 0xf3, 0x0a, - 0x93, 0xf6, 0xe1, 0x23, 0x3b, 0x59, 0x36, 0x7a, 0x08, 0x19, 0x36, 0x07, 0xf0, 0xdc, 0xe5, 0xd6, - 0xd3, 0x94, 0xdc, 0x9c, 0xae, 0x73, 0x9e, 0x6e, 0xd5, 0x5e, 0xcc, 0xa1, 0xfe, 0x0a, 0x44, 0xe1, - 0xe2, 0xd3, 0xb8, 0x88, 0x76, 0xf0, 0x28, 0xf0, 0x10, 0x43, 0xce, 0x75, 0x16, 0x2e, 0xbd, 0x03, - 0xef, 0xf7, 0x3d, 0x6c, 0x99, 0x9e, 0x41, 0xa7, 0x69, 0x7c, 0x1b, 0xc9, 0xb7, 0xea, 0xa0, 0x51, - 0xd4, 0x97, 0xa3, 0x70, 0x57, 0x44, 0xd5, 0x81, 0x70, 0x28, 0x55, 0xa0, 0x70, 0x68, 0x13, 0xbe, - 0x32, 0x6b, 0x23, 0xbf, 0x87, 0x65, 0x50, 0xbf, 0xdd, 0x28, 0xb7, 0xd6, 0x52, 0xc4, 0xc5, 0x2d, - 0xb8, 0xed, 0xf7, 0xb0, 0x5e, 0x09, 0xce, 0xfc, 0x53, 0xff, 0x04, 0xf0, 0x49, 0xdc, 0xa9, 0x88, - 0xa0, 0x70, 0xf4, 0x15, 0xb6, 0x87, 0xed, 0x90, 0xf8, 0xb1, 0x96, 0x6b, 0x35, 0x44, 0x83, 0xab, - 0x04, 0x79, 0xe6, 0x04, 0x91, 0x69, 0xd3, 0x1a, 0xa6, 0xe3, 0x10, 0x44, 0x29, 0x37, 0xa5, 0xa4, - 0x3f, 0x10, 0x4b, 0x7b, 0xa6, 0xf7, 0x59, 0xb4, 0xa0, 0xf6, 0xe0, 0xdb, 0x17, 0x88, 0x15, 0xe6, - 0x7c, 0x02, 0x5f, 0x47, 0x02, 0x63, 0x78, 0xd8, 0x1e, 0x1a, 0x56, 0x48, 0xfc, 0x99, 0xeb, 0x80, - 0xbb, 0x2e, 0xa3, 0x0c, 0x1a, 0xf5, 0x0f, 0x00, 0xdf, 0xe2, 0x89, 0xbe, 0x73, 0x99, 0x8f, 0x28, - 0xbd, 0xd1, 0xa6, 0xd8, 0xe2, 0x0b, 0x66, 0x6a, 0x15, 0x9e, 0x6c, 0x40, 0xe5, 0x30, 0x82, 0x64, - 0x5b, 0x52, 0x3d, 0x4c, 0x27, 0x51, 0x7f, 0x07, 0xc2, 0xfa, 0xad, 0x73, 0x9d, 0xda, 0x9e, 0x32, - 0xed, 0x86, 0x23, 0x0b, 0x91, 0x9b, 0x71, 0x72, 0xbe, 0x84, 0x4f, 0x2f, 0x92, 0x29, 0xec, 0x78, - 0x13, 0x56, 0x2c, 0xee, 0x83, 0xcf, 0xe3, 0xc2, 0x80, 0xb2, 0x35, 0x83, 0xaa, 0x55, 0xf8, 0x88, - 0x93, 0xb5, 0x3d, 0xd3, 0x1e, 0x7a, 0x2e, 0x65, 0xa2, 0x46, 0xf5, 0x05, 0x7c, 0x75, 0x7e, 0x41, - 0xb0, 0xbe, 0x01, 0x4b, 0xe2, 0x83, 0x21, 0xca, 0x4f, 0x64, 0x49, 0x9f, 0x05, 0x5a, 0x7f, 0x2d, - 0xc1, 0x3b, 0x7c, 0xa3, 0xe4, 0xc3, 0xf2, 0x99, 0xd9, 0x20, 0x3d, 0xd3, 0xd2, 0x86, 0xa1, 0x96, - 0x31, 0x86, 0x14, 0xed, 0xb2, 0xf0, 0x48, 0x95, 0x5a, 0x90, 0x86, 0xb0, 0xb2, 0x85, 0x58, 0xa2, - 0x57, 0x7a, 0x3f, 0x87, 0x61, 0xbe, 0x5c, 0xe5, 0x83, 0xcb, 0x81, 0x93, 0x64, 0x3f, 0x00, 0xb8, - 0x9a, 0x72, 0xb9, 0x4b, 0xcf, 0x73, 0x78, 0xb2, 0x87, 0x90, 0xf2, 0xe2, 0xff, 0x6e, 0x4b, 0x84, - 0xfc, 0x0c, 0xa0, 0x9c, 0x75, 0x57, 0x48, 0x2f, 0xf3, 0x4d, 0xcc, 0xbb, 0x0d, 0x95, 0x8d, 0x2b, - 0xed, 0x4d, 0x74, 0xfd, 0x04, 0x60, 0x35, 0xe3, 0xb8, 0x4a, 0x1f, 0xe5, 0x50, 0xe7, 0x5f, 0x47, - 0xca, 0xcb, 0xab, 0x6c, 0x4d, 0x44, 0xfd, 0x02, 0xe0, 0x6b, 0x99, 0xc7, 0x46, 0xca, 0xab, 0xf8, - 0xa2, 0x3b, 0x41, 0xf9, 0xf8, 0x6a, 0x9b, 0xcf, 0x35, 0x54, 0xca, 0x2c, 0xcc, 0x6d, 0xa8, 0xec, - 0xe1, 0x9e, 0xdb, 0x50, 0x39, 0x23, 0x57, 0x2d, 0xb4, 0xb7, 0xfe, 0x3e, 0xa9, 0x81, 0xe3, 0x93, - 0x1a, 0xf8, 0xf7, 0xa4, 0x06, 0x7e, 0x3c, 0xad, 0x15, 0x8e, 0x4f, 0x6b, 0x85, 0x7f, 0x4e, 0x6b, - 0x85, 0xfd, 0x67, 0x7d, 0x97, 0x0d, 0x42, 0x4b, 0xb3, 0xf1, 0xa8, 0xd9, 0x75, 0x7b, 0xbc, 0x25, - 0x9b, 0xf1, 0xa3, 0xf3, 0xe8, 0xcc, 0xbb, 0x95, 0xbf, 0x4a, 0xad, 0xbb, 0xfc, 0xa9, 0xf9, 0xe1, - 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x4c, 0xcd, 0x7e, 0x9a, 0x27, 0x0b, 0x00, 0x00, + // 900 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x56, 0xcf, 0x6f, 0x1b, 0x45, + 0x14, 0xf6, 0xb4, 0x6e, 0x6b, 0x3f, 0x87, 0x34, 0x99, 0xb4, 0xc4, 0x6c, 0xa9, 0x63, 0x96, 0xd2, + 0x9a, 0x1f, 0x5d, 0x2b, 0x46, 0xad, 0x44, 0x0b, 0x12, 0x24, 0x85, 0xa8, 0x05, 0x55, 0xb0, 0x41, + 0x45, 0xea, 0x65, 0xb5, 0x3f, 0xc6, 0xf6, 0xca, 0xeb, 0x9d, 0xed, 0xcc, 0xac, 0xd3, 0x5c, 0x38, + 0x72, 0x46, 0x48, 0x48, 0xdc, 0x38, 0x54, 0x5c, 0xe0, 0x1f, 0xe1, 0xd8, 0x23, 0x47, 0x94, 0xfc, + 0x23, 0xc8, 0xb3, 0xb3, 0x1b, 0xc7, 0xde, 0xdd, 0x94, 0x48, 0x95, 0x72, 0xb3, 0xdf, 0x7c, 0xf3, + 0xbd, 0x6f, 0xbe, 0x7d, 0x33, 0xef, 0x41, 0x9b, 0xfb, 0xfd, 0x90, 0x7a, 0xa4, 0x4b, 0xc4, 0xd0, + 0x61, 0xbe, 0x37, 0x20, 0xdd, 0xc9, 0x66, 0xf7, 0x59, 0x4c, 0xd8, 0xbe, 0x11, 0x31, 0x2a, 0x28, + 0xbe, 0xa2, 0x10, 0x46, 0x86, 0x30, 0x26, 0x9b, 0xda, 0x95, 0x01, 0x1d, 0x50, 0x09, 0xe8, 0x4e, + 0x7f, 0x25, 0x58, 0x2d, 0x9f, 0x4d, 0xec, 0x47, 0x84, 0x2b, 0xc4, 0xf5, 0x14, 0x41, 0x99, 0xed, + 0x06, 0x0b, 0xcb, 0x1f, 0x2c, 0x2e, 0x87, 0x44, 0xec, 0x51, 0x36, 0xb2, 0x3c, 0xc2, 0x5d, 0xe6, + 0x47, 0x82, 0xb2, 0x04, 0xab, 0x7f, 0x0e, 0xeb, 0xdf, 0x4d, 0x75, 0x7e, 0x29, 0x86, 0xdf, 0x32, + 0x1a, 0x0d, 0x89, 0xbb, 0x6f, 0x92, 0x67, 0x31, 0xe1, 0x02, 0x6f, 0x40, 0x23, 0x52, 0x21, 0xcb, + 0xf7, 0x9a, 0x97, 0xda, 0xa8, 0xb3, 0x64, 0x42, 0x1a, 0x7a, 0xe8, 0x3d, 0xaa, 0xd6, 0xd0, 0xca, + 0x25, 0xfd, 0x05, 0x82, 0xe6, 0x22, 0x05, 0x8f, 0x68, 0xc8, 0xc9, 0x3c, 0x07, 0x9a, 0xe7, 0xc0, + 0x77, 0xe0, 0x22, 0x17, 0xb6, 0x88, 0x79, 0xb3, 0xda, 0x46, 0x9d, 0xe5, 0xde, 0x75, 0x23, 0x75, + 0x2a, 0x11, 0x6f, 0x4c, 0x36, 0x8d, 0x5d, 0x09, 0xf8, 0x9e, 0x3c, 0x17, 0xa6, 0x02, 0xe3, 0xf7, + 0x61, 0xc5, 0x0d, 0x6c, 0x7f, 0x6c, 0x4d, 0xec, 0xc0, 0xf7, 0x6c, 0x41, 0x19, 0x6f, 0x5e, 0x68, + 0x9f, 0xef, 0xd4, 0xcd, 0xcb, 0x32, 0xfe, 0x24, 0x0b, 0x3f, 0xaa, 0xd6, 0xce, 0xad, 0x54, 0xf5, + 0x09, 0x6c, 0x48, 0x91, 0xdb, 0x8c, 0x72, 0xee, 0x0e, 0x6d, 0x3f, 0xfc, 0x8a, 0x90, 0x6d, 0x1a, + 0xf6, 0xfd, 0x41, 0x7a, 0xde, 0x5d, 0xc0, 0x8b, 0x36, 0x49, 0xc9, 0xcb, 0xbd, 0x1b, 0x39, 0xb2, + 0x1e, 0x27, 0xe0, 0x07, 0x19, 0xd6, 0x5c, 0x0d, 0xe7, 0x43, 0xfa, 0x8f, 0xd0, 0x2e, 0xce, 0xab, + 0x4c, 0x7a, 0x0a, 0x57, 0xdd, 0x6c, 0xd9, 0xea, 0x13, 0x62, 0xb9, 0x12, 0x20, 0x73, 0x37, 0x7a, + 0x37, 0x73, 0x72, 0x4b, 0xba, 0xed, 0xe3, 0x74, 0x6b, 0xee, 0x62, 0x0e, 0xfd, 0x77, 0xa4, 0x0e, + 0xae, 0x3e, 0x8d, 0x4f, 0xf8, 0x36, 0x1d, 0x47, 0x01, 0x11, 0xc4, 0x7b, 0x9d, 0x07, 0xc7, 0xb7, + 0xe0, 0xf2, 0x20, 0xa0, 0x8e, 0x1d, 0x58, 0x7c, 0x9a, 0x26, 0x74, 0x49, 0xf3, 0x5c, 0x1b, 0x75, + 0xaa, 0xe6, 0x72, 0x12, 0xde, 0x55, 0x51, 0x7d, 0xa8, 0x1c, 0xca, 0x15, 0xa8, 0x1c, 0x7a, 0x00, + 0x6f, 0x1c, 0x95, 0x51, 0xd8, 0xa7, 0x4d, 0xd4, 0x3e, 0xdf, 0x69, 0xf4, 0x36, 0x72, 0xc4, 0xa5, + 0x25, 0xf8, 0x30, 0xec, 0x53, 0x73, 0x29, 0x9a, 0xf9, 0xa7, 0xff, 0x85, 0xe0, 0x46, 0x5a, 0xa9, + 0x84, 0x91, 0x78, 0xfc, 0x0d, 0x75, 0x47, 0x5b, 0x31, 0x0b, 0x53, 0x2d, 0xaf, 0xd5, 0x10, 0x03, + 0xd6, 0x18, 0x09, 0xec, 0x7d, 0xc2, 0xa6, 0x45, 0x6b, 0xd9, 0x9e, 0xc7, 0x08, 0xe7, 0xd2, 0x94, + 0xba, 0xb9, 0xaa, 0x96, 0x9e, 0xd8, 0xc1, 0x17, 0xc9, 0x82, 0xde, 0x87, 0xf7, 0x4e, 0x10, 0xab, + 0xcc, 0xf9, 0x0c, 0xae, 0x11, 0x85, 0xb1, 0x02, 0xea, 0x8e, 0x2c, 0x27, 0x66, 0xe1, 0x91, 0xeb, + 0x48, 0xba, 0xde, 0x24, 0x05, 0x34, 0xfa, 0x9f, 0x08, 0xde, 0x95, 0x89, 0x7e, 0xf0, 0x45, 0x48, + 0x38, 0x3f, 0xd3, 0xa6, 0xb8, 0xea, 0x0b, 0x16, 0x6a, 0x55, 0x9e, 0xdc, 0x07, 0x6d, 0x2f, 0x81, + 0x14, 0x5b, 0xb2, 0xbe, 0x97, 0x4f, 0xa2, 0xff, 0x81, 0x94, 0xf5, 0x3b, 0xc7, 0x2a, 0x75, 0x6b, + 0xca, 0xf4, 0x38, 0x1e, 0x3b, 0x84, 0x9d, 0x8d, 0x9b, 0xf3, 0x35, 0xdc, 0x3c, 0x49, 0xa6, 0xb2, + 0xe3, 0x1d, 0x58, 0x72, 0xa4, 0x0f, 0xa1, 0x8c, 0x2b, 0x03, 0x1a, 0xce, 0x11, 0x54, 0x5f, 0x87, + 0xab, 0x92, 0x6c, 0x2b, 0xb0, 0xdd, 0x51, 0xe0, 0x73, 0xa1, 0xce, 0xa8, 0xdf, 0x85, 0x37, 0xe7, + 0x17, 0x14, 0xeb, 0xdb, 0x50, 0x57, 0x1f, 0x8c, 0x70, 0x79, 0x23, 0xeb, 0xe6, 0x51, 0x40, 0x5f, + 0x83, 0xd5, 0xe4, 0x5e, 0xdb, 0x31, 0x4f, 0x8b, 0x48, 0xdf, 0x04, 0x3c, 0x1b, 0x54, 0x44, 0xd7, + 0xa0, 0xee, 0x73, 0x2b, 0x9a, 0xc6, 0x92, 0x1e, 0x51, 0x33, 0x6b, 0x3e, 0x97, 0x18, 0xaf, 0xf7, + 0xa2, 0x06, 0x17, 0xe4, 0x1e, 0x1c, 0x42, 0x63, 0xa6, 0xc7, 0xe0, 0xdb, 0x46, 0x5e, 0x53, 0x35, + 0x0a, 0xda, 0x99, 0x66, 0xbc, 0x2a, 0x3c, 0x11, 0xa5, 0x57, 0xf0, 0x08, 0x96, 0x76, 0x88, 0xc8, + 0xce, 0x8d, 0x3f, 0x2c, 0x61, 0x98, 0xb7, 0x4d, 0xfb, 0xe8, 0xd5, 0xc0, 0x59, 0xb2, 0x9f, 0x10, + 0xac, 0xe5, 0x34, 0x09, 0x7c, 0xa7, 0x84, 0xa7, 0xb8, 0x99, 0x69, 0x77, 0xff, 0xef, 0xb6, 0x4c, + 0xc8, 0xaf, 0x08, 0x9a, 0x45, 0x6f, 0x0e, 0xbe, 0x57, 0x6e, 0x62, 0xd9, 0xab, 0xaa, 0xdd, 0x3f, + 0xd5, 0xde, 0x4c, 0xd7, 0x2f, 0x08, 0xd6, 0x0b, 0xae, 0x3d, 0xfe, 0xa4, 0x84, 0xba, 0xfc, 0x59, + 0xd3, 0xee, 0x9d, 0x66, 0x6b, 0x26, 0xea, 0x37, 0x04, 0x6f, 0x15, 0x5e, 0x3f, 0x5c, 0x76, 0xe2, + 0x93, 0xde, 0x16, 0xed, 0xd3, 0xd3, 0x6d, 0x3e, 0x56, 0x50, 0x39, 0x3d, 0xb5, 0xb4, 0xa0, 0x8a, + 0x87, 0x84, 0xd2, 0x82, 0x2a, 0x69, 0xdd, 0x7a, 0x05, 0xbb, 0xb0, 0xbc, 0x43, 0x84, 0xbc, 0xcd, + 0xc9, 0x24, 0x87, 0x6f, 0x95, 0x71, 0xcd, 0x3c, 0x17, 0x5a, 0xe7, 0x64, 0x60, 0x92, 0x66, 0x6b, + 0xe7, 0xef, 0x83, 0x16, 0x7a, 0x79, 0xd0, 0x42, 0xff, 0x1e, 0xb4, 0xd0, 0xcf, 0x87, 0xad, 0xca, + 0xcb, 0xc3, 0x56, 0xe5, 0x9f, 0xc3, 0x56, 0xe5, 0xe9, 0xed, 0x81, 0x2f, 0x86, 0xb1, 0x63, 0xb8, + 0x74, 0xdc, 0xdd, 0xf5, 0xfb, 0xb2, 0xee, 0xbb, 0xe9, 0x84, 0xfc, 0x7c, 0x66, 0xc8, 0x96, 0x23, + 0xb4, 0x73, 0x51, 0xce, 0xc5, 0x1f, 0xff, 0x17, 0x00, 0x00, 0xff, 0xff, 0xf8, 0xb4, 0xc7, 0x10, + 0xd4, 0x0b, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -808,6 +894,7 @@ type QueryClient interface { // Prophecies Completed Query Service to fetch prophecy info from global // sequence PropheciesCompleted(ctx context.Context, in *QueryPropheciesCompletedRequest, opts ...grpc.CallOption) (*QueryPropheciesCompletedResponse, error) + GetPauseStatus(ctx context.Context, in *QueryPauseRequest, opts ...grpc.CallOption) (*QueryPauseResponse, error) } type queryClient struct { @@ -881,6 +968,15 @@ func (c *queryClient) PropheciesCompleted(ctx context.Context, in *QueryPropheci return out, nil } +func (c *queryClient) GetPauseStatus(ctx context.Context, in *QueryPauseRequest, opts ...grpc.CallOption) (*QueryPauseResponse, error) { + out := new(QueryPauseResponse) + err := c.cc.Invoke(ctx, "/sifnode.ethbridge.v1.Query/GetPauseStatus", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // EthProphecy queries an EthProphecy @@ -899,6 +995,7 @@ type QueryServer interface { // Prophecies Completed Query Service to fetch prophecy info from global // sequence PropheciesCompleted(context.Context, *QueryPropheciesCompletedRequest) (*QueryPropheciesCompletedResponse, error) + GetPauseStatus(context.Context, *QueryPauseRequest) (*QueryPauseResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -926,6 +1023,9 @@ func (*UnimplementedQueryServer) GlobalSequenceBlockNumber(ctx context.Context, func (*UnimplementedQueryServer) PropheciesCompleted(ctx context.Context, req *QueryPropheciesCompletedRequest) (*QueryPropheciesCompletedResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method PropheciesCompleted not implemented") } +func (*UnimplementedQueryServer) GetPauseStatus(ctx context.Context, req *QueryPauseRequest) (*QueryPauseResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPauseStatus not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -1057,6 +1157,24 @@ func _Query_PropheciesCompleted_Handler(srv interface{}, ctx context.Context, de return interceptor(ctx, in, info, handler) } +func _Query_GetPauseStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryPauseRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).GetPauseStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/sifnode.ethbridge.v1.Query/GetPauseStatus", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).GetPauseStatus(ctx, req.(*QueryPauseRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "sifnode.ethbridge.v1.Query", HandlerType: (*QueryServer)(nil), @@ -1089,6 +1207,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "PropheciesCompleted", Handler: _Query_PropheciesCompleted_Handler, }, + { + MethodName: "GetPauseStatus", + Handler: _Query_GetPauseStatus_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "sifnode/ethbridge/v1/query.proto", @@ -1543,6 +1665,62 @@ func (m *QueryBlacklistResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *QueryPauseRequest) 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 *QueryPauseRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPauseRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryPauseResponse) 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 *QueryPauseResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPauseResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.IsPaused { + i-- + if m.IsPaused { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -1751,6 +1929,27 @@ func (m *QueryBlacklistResponse) Size() (n int) { return n } +func (m *QueryPauseRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryPauseResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.IsPaused { + n += 2 + } + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -2932,6 +3131,126 @@ func (m *QueryBlacklistResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryPauseRequest) 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 ErrIntOverflowQuery + } + 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: QueryPauseRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPauseRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryPauseResponse) 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 ErrIntOverflowQuery + } + 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: QueryPauseResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPauseResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsPaused", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsPaused = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/ethbridge/types/tx.pb.go b/x/ethbridge/types/tx.pb.go index 2ef2785037..301fd18025 100644 --- a/x/ethbridge/types/tx.pb.go +++ b/x/ethbridge/types/tx.pb.go @@ -30,6 +30,94 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +type MsgPause struct { + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty" yaml:"signer"` + IsPaused bool `protobuf:"varint,2,opt,name=is_paused,json=isPaused,proto3" json:"is_paused,omitempty"` +} + +func (m *MsgPause) Reset() { *m = MsgPause{} } +func (m *MsgPause) String() string { return proto.CompactTextString(m) } +func (*MsgPause) ProtoMessage() {} +func (*MsgPause) Descriptor() ([]byte, []int) { + return fileDescriptor_44d60f3dabe1980f, []int{0} +} +func (m *MsgPause) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPause) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPause.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 *MsgPause) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPause.Merge(m, src) +} +func (m *MsgPause) XXX_Size() int { + return m.Size() +} +func (m *MsgPause) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPause.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPause proto.InternalMessageInfo + +func (m *MsgPause) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +func (m *MsgPause) GetIsPaused() bool { + if m != nil { + return m.IsPaused + } + return false +} + +type MsgPauseResponse struct { +} + +func (m *MsgPauseResponse) Reset() { *m = MsgPauseResponse{} } +func (m *MsgPauseResponse) String() string { return proto.CompactTextString(m) } +func (*MsgPauseResponse) ProtoMessage() {} +func (*MsgPauseResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_44d60f3dabe1980f, []int{1} +} +func (m *MsgPauseResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPauseResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPauseResponse.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 *MsgPauseResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPauseResponse.Merge(m, src) +} +func (m *MsgPauseResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgPauseResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPauseResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPauseResponse proto.InternalMessageInfo + // MsgLock defines a message for locking coins and triggering a related event type MsgLock struct { CosmosSender string `protobuf:"bytes,1,opt,name=cosmos_sender,json=cosmosSender,proto3" json:"cosmos_sender,omitempty"` @@ -44,7 +132,7 @@ func (m *MsgLock) Reset() { *m = MsgLock{} } func (m *MsgLock) String() string { return proto.CompactTextString(m) } func (*MsgLock) ProtoMessage() {} func (*MsgLock) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{0} + return fileDescriptor_44d60f3dabe1980f, []int{2} } func (m *MsgLock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -108,7 +196,7 @@ func (m *MsgLockResponse) Reset() { *m = MsgLockResponse{} } func (m *MsgLockResponse) String() string { return proto.CompactTextString(m) } func (*MsgLockResponse) ProtoMessage() {} func (*MsgLockResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{1} + return fileDescriptor_44d60f3dabe1980f, []int{3} } func (m *MsgLockResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -151,7 +239,7 @@ func (m *MsgBurn) Reset() { *m = MsgBurn{} } func (m *MsgBurn) String() string { return proto.CompactTextString(m) } func (*MsgBurn) ProtoMessage() {} func (*MsgBurn) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{2} + return fileDescriptor_44d60f3dabe1980f, []int{4} } func (m *MsgBurn) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -215,7 +303,7 @@ func (m *MsgBurnResponse) Reset() { *m = MsgBurnResponse{} } func (m *MsgBurnResponse) String() string { return proto.CompactTextString(m) } func (*MsgBurnResponse) ProtoMessage() {} func (*MsgBurnResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{3} + return fileDescriptor_44d60f3dabe1980f, []int{5} } func (m *MsgBurnResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -252,7 +340,7 @@ func (m *MsgCreateEthBridgeClaim) Reset() { *m = MsgCreateEthBridgeClaim func (m *MsgCreateEthBridgeClaim) String() string { return proto.CompactTextString(m) } func (*MsgCreateEthBridgeClaim) ProtoMessage() {} func (*MsgCreateEthBridgeClaim) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{4} + return fileDescriptor_44d60f3dabe1980f, []int{6} } func (m *MsgCreateEthBridgeClaim) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -295,7 +383,7 @@ func (m *MsgCreateEthBridgeClaimResponse) Reset() { *m = MsgCreateEthBri func (m *MsgCreateEthBridgeClaimResponse) String() string { return proto.CompactTextString(m) } func (*MsgCreateEthBridgeClaimResponse) ProtoMessage() {} func (*MsgCreateEthBridgeClaimResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{5} + return fileDescriptor_44d60f3dabe1980f, []int{7} } func (m *MsgCreateEthBridgeClaimResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -336,7 +424,7 @@ func (m *MsgUpdateWhiteListValidator) Reset() { *m = MsgUpdateWhiteListV func (m *MsgUpdateWhiteListValidator) String() string { return proto.CompactTextString(m) } func (*MsgUpdateWhiteListValidator) ProtoMessage() {} func (*MsgUpdateWhiteListValidator) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{6} + return fileDescriptor_44d60f3dabe1980f, []int{8} } func (m *MsgUpdateWhiteListValidator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -400,7 +488,7 @@ func (m *MsgUpdateWhiteListValidatorResponse) Reset() { *m = MsgUpdateWh func (m *MsgUpdateWhiteListValidatorResponse) String() string { return proto.CompactTextString(m) } func (*MsgUpdateWhiteListValidatorResponse) ProtoMessage() {} func (*MsgUpdateWhiteListValidatorResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{7} + return fileDescriptor_44d60f3dabe1980f, []int{9} } func (m *MsgUpdateWhiteListValidatorResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -438,7 +526,7 @@ func (m *MsgUpdateCrossChainFeeReceiverAccount) Reset() { *m = MsgUpdate func (m *MsgUpdateCrossChainFeeReceiverAccount) String() string { return proto.CompactTextString(m) } func (*MsgUpdateCrossChainFeeReceiverAccount) ProtoMessage() {} func (*MsgUpdateCrossChainFeeReceiverAccount) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{8} + return fileDescriptor_44d60f3dabe1980f, []int{10} } func (m *MsgUpdateCrossChainFeeReceiverAccount) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -492,7 +580,7 @@ func (m *MsgUpdateCrossChainFeeReceiverAccountResponse) String() string { } func (*MsgUpdateCrossChainFeeReceiverAccountResponse) ProtoMessage() {} func (*MsgUpdateCrossChainFeeReceiverAccountResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{9} + return fileDescriptor_44d60f3dabe1980f, []int{11} } func (m *MsgUpdateCrossChainFeeReceiverAccountResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -532,7 +620,7 @@ func (m *MsgRescueCrossChainFee) Reset() { *m = MsgRescueCrossChainFee{} func (m *MsgRescueCrossChainFee) String() string { return proto.CompactTextString(m) } func (*MsgRescueCrossChainFee) ProtoMessage() {} func (*MsgRescueCrossChainFee) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{10} + return fileDescriptor_44d60f3dabe1980f, []int{12} } func (m *MsgRescueCrossChainFee) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -591,7 +679,7 @@ func (m *MsgSetBlacklist) Reset() { *m = MsgSetBlacklist{} } func (m *MsgSetBlacklist) String() string { return proto.CompactTextString(m) } func (*MsgSetBlacklist) ProtoMessage() {} func (*MsgSetBlacklist) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{11} + return fileDescriptor_44d60f3dabe1980f, []int{13} } func (m *MsgSetBlacklist) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -641,7 +729,7 @@ func (m *MsgSetBlacklistResponse) Reset() { *m = MsgSetBlacklistResponse func (m *MsgSetBlacklistResponse) String() string { return proto.CompactTextString(m) } func (*MsgSetBlacklistResponse) ProtoMessage() {} func (*MsgSetBlacklistResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{12} + return fileDescriptor_44d60f3dabe1980f, []int{14} } func (m *MsgSetBlacklistResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -677,7 +765,7 @@ func (m *MsgRescueCrossChainFeeResponse) Reset() { *m = MsgRescueCrossCh func (m *MsgRescueCrossChainFeeResponse) String() string { return proto.CompactTextString(m) } func (*MsgRescueCrossChainFeeResponse) ProtoMessage() {} func (*MsgRescueCrossChainFeeResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{13} + return fileDescriptor_44d60f3dabe1980f, []int{15} } func (m *MsgRescueCrossChainFeeResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -720,7 +808,7 @@ func (m *MsgSetFeeInfo) Reset() { *m = MsgSetFeeInfo{} } func (m *MsgSetFeeInfo) String() string { return proto.CompactTextString(m) } func (*MsgSetFeeInfo) ProtoMessage() {} func (*MsgSetFeeInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{14} + return fileDescriptor_44d60f3dabe1980f, []int{16} } func (m *MsgSetFeeInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -777,7 +865,7 @@ func (m *MsgSetFeeInfoResponse) Reset() { *m = MsgSetFeeInfoResponse{} } func (m *MsgSetFeeInfoResponse) String() string { return proto.CompactTextString(m) } func (*MsgSetFeeInfoResponse) ProtoMessage() {} func (*MsgSetFeeInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{15} + return fileDescriptor_44d60f3dabe1980f, []int{17} } func (m *MsgSetFeeInfoResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -819,7 +907,7 @@ func (m *MsgSignProphecy) Reset() { *m = MsgSignProphecy{} } func (m *MsgSignProphecy) String() string { return proto.CompactTextString(m) } func (*MsgSignProphecy) ProtoMessage() {} func (*MsgSignProphecy) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{16} + return fileDescriptor_44d60f3dabe1980f, []int{18} } func (m *MsgSignProphecy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -890,7 +978,7 @@ func (m *MsgSignProphecyResponse) Reset() { *m = MsgSignProphecyResponse func (m *MsgSignProphecyResponse) String() string { return proto.CompactTextString(m) } func (*MsgSignProphecyResponse) ProtoMessage() {} func (*MsgSignProphecyResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{17} + return fileDescriptor_44d60f3dabe1980f, []int{19} } func (m *MsgSignProphecyResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -929,7 +1017,7 @@ func (m *MsgUpdateConsensusNeeded) Reset() { *m = MsgUpdateConsensusNeed func (m *MsgUpdateConsensusNeeded) String() string { return proto.CompactTextString(m) } func (*MsgUpdateConsensusNeeded) ProtoMessage() {} func (*MsgUpdateConsensusNeeded) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{18} + return fileDescriptor_44d60f3dabe1980f, []int{20} } func (m *MsgUpdateConsensusNeeded) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -986,7 +1074,7 @@ func (m *MsgUpdateConsensusNeededResponse) Reset() { *m = MsgUpdateConse func (m *MsgUpdateConsensusNeededResponse) String() string { return proto.CompactTextString(m) } func (*MsgUpdateConsensusNeededResponse) ProtoMessage() {} func (*MsgUpdateConsensusNeededResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_44d60f3dabe1980f, []int{19} + return fileDescriptor_44d60f3dabe1980f, []int{21} } func (m *MsgUpdateConsensusNeededResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1016,6 +1104,8 @@ func (m *MsgUpdateConsensusNeededResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgUpdateConsensusNeededResponse proto.InternalMessageInfo func init() { + proto.RegisterType((*MsgPause)(nil), "sifnode.ethbridge.v1.MsgPause") + proto.RegisterType((*MsgPauseResponse)(nil), "sifnode.ethbridge.v1.MsgPauseResponse") proto.RegisterType((*MsgLock)(nil), "sifnode.ethbridge.v1.MsgLock") proto.RegisterType((*MsgLockResponse)(nil), "sifnode.ethbridge.v1.MsgLockResponse") proto.RegisterType((*MsgBurn)(nil), "sifnode.ethbridge.v1.MsgBurn") @@ -1041,87 +1131,91 @@ func init() { func init() { proto.RegisterFile("sifnode/ethbridge/v1/tx.proto", fileDescriptor_44d60f3dabe1980f) } var fileDescriptor_44d60f3dabe1980f = []byte{ - // 1269 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x58, 0xcf, 0x6f, 0x1b, 0xc5, - 0x17, 0xcf, 0x26, 0x4e, 0xda, 0xbc, 0xe6, 0xe7, 0x36, 0xfe, 0x66, 0xeb, 0xb6, 0xb6, 0x3b, 0xfd, - 0x06, 0x5a, 0x20, 0xb6, 0x12, 0x4a, 0x25, 0x40, 0x08, 0xd5, 0x2e, 0x2d, 0xa9, 0x9a, 0xaa, 0xda, - 0xa8, 0x54, 0xe2, 0xc0, 0x6a, 0xbd, 0x3b, 0x5e, 0xaf, 0xec, 0xdd, 0xb1, 0x76, 0xd6, 0x6e, 0x2d, - 0x21, 0x71, 0x41, 0x88, 0x03, 0x07, 0x0e, 0x70, 0xe7, 0xc4, 0xdf, 0xd2, 0x63, 0x8f, 0x88, 0x83, - 0x85, 0x9a, 0xbf, 0x00, 0x5f, 0xb9, 0xa0, 0x9d, 0x99, 0x1d, 0xef, 0x26, 0xb6, 0x6b, 0xab, 0x54, - 0xaa, 0x10, 0xa7, 0xd8, 0xef, 0x7d, 0xde, 0x8f, 0x79, 0xf3, 0x79, 0x6f, 0x5e, 0x0c, 0x97, 0xa9, - 0x5b, 0xf7, 0x89, 0x8d, 0xcb, 0x38, 0x6c, 0xd4, 0x02, 0xd7, 0x76, 0x70, 0xb9, 0xbb, 0x57, 0x0e, - 0x9f, 0x96, 0xda, 0x01, 0x09, 0x89, 0xba, 0x25, 0xd4, 0x25, 0xa9, 0x2e, 0x75, 0xf7, 0x72, 0x5b, - 0x0e, 0x71, 0x08, 0x03, 0x94, 0xa3, 0x4f, 0x1c, 0x9b, 0x2b, 0x8e, 0x76, 0xd5, 0x6b, 0x63, 0x2a, - 0x10, 0xef, 0xc4, 0x08, 0x12, 0x98, 0x56, 0x8b, 0xa9, 0x7d, 0x1c, 0x3e, 0x21, 0x41, 0xd3, 0xb0, - 0x31, 0xb5, 0x02, 0xb7, 0x1d, 0x92, 0x80, 0x63, 0xd1, 0x60, 0x01, 0xce, 0x1c, 0x52, 0xe7, 0x3e, - 0xb1, 0x9a, 0xea, 0x55, 0x58, 0xb5, 0x08, 0xf5, 0x08, 0x35, 0x28, 0xf6, 0x6d, 0x1c, 0x68, 0x4a, - 0x51, 0xb9, 0xb6, 0xac, 0xaf, 0x70, 0xe1, 0x11, 0x93, 0xa9, 0x8f, 0x61, 0xc9, 0xf4, 0x48, 0xc7, - 0x0f, 0xb5, 0xf9, 0x48, 0x5b, 0xf9, 0xf4, 0x59, 0xbf, 0x30, 0xf7, 0x7b, 0xbf, 0xf0, 0x96, 0xe3, - 0x86, 0x8d, 0x4e, 0xad, 0x64, 0x11, 0xaf, 0xcc, 0x0d, 0xc4, 0x9f, 0x5d, 0x6a, 0x37, 0x45, 0x7a, - 0x07, 0x7e, 0x38, 0xe8, 0x17, 0x56, 0x7b, 0xa6, 0xd7, 0xfa, 0x08, 0x71, 0x2f, 0x48, 0x17, 0xee, - 0xd4, 0x1b, 0x00, 0x36, 0xf6, 0x89, 0x67, 0x34, 0x4c, 0xda, 0xd0, 0x16, 0x98, 0xf3, 0xec, 0xa0, - 0x5f, 0xd8, 0xe4, 0xf0, 0xa1, 0x0e, 0xe9, 0xcb, 0xec, 0xcb, 0xe7, 0x26, 0x6d, 0xa8, 0x07, 0xb0, - 0x89, 0xc3, 0x06, 0x0e, 0x70, 0xc7, 0x33, 0x02, 0x6c, 0x61, 0xb7, 0x8b, 0x03, 0x6d, 0x91, 0x19, - 0x5f, 0x1a, 0xf4, 0x0b, 0x1a, 0x37, 0x3e, 0x05, 0x41, 0xfa, 0x46, 0x2c, 0xd3, 0x85, 0x48, 0xf5, - 0x61, 0xcd, 0x0a, 0x08, 0xa5, 0x56, 0xc3, 0x74, 0x7d, 0xa3, 0x8e, 0xb1, 0xb6, 0xc4, 0xfc, 0xdc, - 0x9d, 0xf9, 0x84, 0x59, 0x1e, 0x35, 0xed, 0x0d, 0xe9, 0xab, 0x43, 0xc1, 0x1d, 0x8c, 0xd5, 0x00, - 0xd4, 0xd3, 0xd7, 0xa2, 0x9d, 0x29, 0x2a, 0xd7, 0xd6, 0xf6, 0xff, 0x5f, 0x8a, 0x19, 0xc1, 0xef, - 0xb0, 0xd4, 0xdd, 0x2b, 0x3d, 0xe0, 0xe0, 0xdb, 0x12, 0x5b, 0xb9, 0x3c, 0xe8, 0x17, 0x2e, 0xf0, - 0x58, 0xa7, 0x3d, 0x21, 0x7d, 0xd3, 0x3f, 0x69, 0x71, 0x2f, 0x73, 0x36, 0xb3, 0xb1, 0x88, 0x36, - 0x61, 0x5d, 0xdc, 0xb9, 0x8e, 0x69, 0x9b, 0xf8, 0x14, 0xa3, 0x1f, 0x32, 0x8c, 0x07, 0x95, 0x4e, - 0xe0, 0xab, 0x9f, 0x8c, 0xe4, 0x41, 0x45, 0x1b, 0xf4, 0x0b, 0x5b, 0xe2, 0x64, 0x49, 0x35, 0xfa, - 0x8f, 0x21, 0xff, 0x52, 0x86, 0x44, 0x6c, 0x90, 0x0c, 0xf9, 0x4e, 0x81, 0xed, 0x43, 0xea, 0x54, - 0x03, 0x6c, 0x86, 0xf8, 0xb3, 0xb0, 0x51, 0x61, 0xb3, 0xa7, 0xda, 0x32, 0x5d, 0x4f, 0x6d, 0x42, - 0x54, 0x2c, 0x83, 0x8f, 0x23, 0xc3, 0x8a, 0x64, 0x8c, 0x34, 0xe7, 0x12, 0x69, 0x26, 0x47, 0x5b, - 0x29, 0x6d, 0x5f, 0xb9, 0x38, 0xe8, 0x17, 0xb6, 0xe5, 0x45, 0xa4, 0xfc, 0x20, 0x7d, 0x0d, 0xa7, - 0xc0, 0xe8, 0x0a, 0x14, 0xc6, 0xe4, 0x21, 0x73, 0xfd, 0x65, 0x1e, 0x2e, 0x1e, 0x52, 0xe7, 0x51, - 0xdb, 0x36, 0x43, 0xfc, 0xb8, 0xe1, 0x86, 0xf8, 0xbe, 0x4b, 0xc3, 0x2f, 0xcc, 0x96, 0x6b, 0x9b, - 0x21, 0x09, 0x5e, 0x95, 0xe1, 0xfb, 0xb0, 0xdc, 0x8d, 0x7d, 0x09, 0x92, 0x6f, 0x0d, 0xfa, 0x85, - 0x0d, 0x6e, 0x2a, 0x55, 0x48, 0x1f, 0xc2, 0xc6, 0xdc, 0x65, 0xe6, 0x75, 0xde, 0xa5, 0xba, 0x05, - 0x8b, 0x6d, 0xf2, 0x44, 0xd0, 0x7d, 0x55, 0xe7, 0x5f, 0xee, 0x65, 0xce, 0x2e, 0x6c, 0x64, 0xd0, - 0x0e, 0x5c, 0x9d, 0x50, 0x21, 0x59, 0xc9, 0x6f, 0x15, 0xd8, 0x91, 0xb8, 0x6a, 0xc4, 0xce, 0xaa, - 0x60, 0x67, 0xdc, 0x15, 0xb7, 0x2c, 0x8b, 0x75, 0xe7, 0x54, 0xaf, 0xc7, 0x4d, 0xd8, 0x4e, 0x73, - 0x7e, 0xd8, 0x92, 0xac, 0x8e, 0x7a, 0x36, 0xd5, 0x01, 0x71, 0x0c, 0x54, 0x86, 0xdd, 0xa9, 0xb2, - 0x90, 0x79, 0xff, 0xa9, 0xc0, 0xff, 0x0e, 0xa9, 0xa3, 0x63, 0x6a, 0x75, 0xd2, 0x16, 0xd3, 0x25, - 0xfa, 0x36, 0xac, 0x0b, 0xd0, 0x89, 0x04, 0xd7, 0xb8, 0x58, 0xce, 0x84, 0x7d, 0xc8, 0x9e, 0x38, - 0x11, 0xed, 0x79, 0x35, 0xd2, 0xe2, 0xf3, 0x49, 0x3f, 0x9f, 0x3a, 0xcf, 0x11, 0x53, 0xa9, 0x8f, - 0x4e, 0xcd, 0x91, 0x0c, 0x23, 0x51, 0x69, 0xb6, 0x39, 0x72, 0x62, 0x5c, 0xa0, 0x2a, 0x6b, 0xda, - 0x23, 0x1c, 0x56, 0x5a, 0xa6, 0xd5, 0x6c, 0xb9, 0x34, 0x54, 0x55, 0xc8, 0xd4, 0x03, 0xe2, 0x89, - 0x23, 0xb2, 0xcf, 0xea, 0x25, 0x58, 0x36, 0x6d, 0x3b, 0xc0, 0x94, 0x62, 0xaa, 0xcd, 0x17, 0x17, - 0xae, 0x2d, 0xeb, 0x43, 0x01, 0xba, 0xc0, 0xba, 0x3c, 0xe9, 0x44, 0xd6, 0xb4, 0x08, 0xf9, 0xd1, - 0x25, 0x95, 0x88, 0x67, 0x8b, 0xb0, 0xca, 0xad, 0xef, 0x60, 0x7c, 0xe0, 0xd7, 0xc9, 0x74, 0xc5, - 0x1e, 0xdd, 0x1b, 0xf3, 0xaf, 0xb5, 0x37, 0xae, 0xc0, 0x4a, 0x74, 0x59, 0x56, 0x27, 0x08, 0xb0, - 0x6f, 0xf5, 0xc4, 0x75, 0x9d, 0xab, 0x63, 0x5c, 0x15, 0x22, 0x95, 0xc2, 0x46, 0x12, 0x62, 0x38, - 0x26, 0x15, 0x17, 0x75, 0x30, 0xf3, 0xc0, 0x17, 0xd3, 0xed, 0xa4, 0x3f, 0xa4, 0xaf, 0x25, 0x22, - 0xde, 0x35, 0xa9, 0xda, 0x85, 0x4d, 0xcf, 0xf5, 0x5d, 0xaf, 0xe3, 0x19, 0x2d, 0x62, 0x35, 0x0d, - 0x8b, 0xd0, 0x50, 0x3c, 0x57, 0xf7, 0x66, 0x8e, 0x2a, 0x1e, 0xb7, 0x53, 0x0e, 0x91, 0xbe, 0x2e, - 0x64, 0xd1, 0x12, 0x50, 0x25, 0x34, 0x4c, 0xc6, 0xad, 0x75, 0x02, 0x9f, 0xc7, 0x5d, 0xfa, 0x67, - 0xe2, 0x4a, 0x87, 0xc3, 0xb8, 0xd1, 0xd3, 0xc2, 0xe2, 0xfe, 0xac, 0xc0, 0xa5, 0xba, 0x1b, 0xd0, - 0x90, 0xa3, 0x6c, 0xd2, 0xa9, 0xb5, 0xb0, 0xd1, 0xc6, 0x8e, 0xd3, 0xe3, 0x39, 0x9c, 0x61, 0x39, - 0x3c, 0x9a, 0x39, 0x87, 0xab, 0xa2, 0xe2, 0x13, 0x7c, 0x23, 0x5d, 0x63, 0xea, 0x28, 0x99, 0xdb, - 0x4c, 0xf9, 0x30, 0xd2, 0x45, 0x79, 0xa1, 0x6d, 0xc8, 0xa6, 0x98, 0x2c, 0x39, 0xfe, 0xd3, 0x3c, - 0x6f, 0x33, 0xd7, 0xf1, 0x1f, 0x06, 0xa4, 0xdd, 0xc0, 0x56, 0xef, 0xcd, 0x65, 0x79, 0x01, 0xce, - 0xb5, 0x45, 0x92, 0x86, 0x6b, 0x33, 0x92, 0xaf, 0xe8, 0x10, 0x8b, 0x0e, 0x6c, 0xf5, 0x3a, 0xc8, - 0x35, 0xc7, 0x10, 0x43, 0x80, 0x73, 0x5c, 0x5f, 0x8f, 0xe5, 0xb7, 0xb8, 0x38, 0x9a, 0x1b, 0xd4, - 0x75, 0x7c, 0x33, 0xec, 0x04, 0x98, 0x33, 0x52, 0x1f, 0x0a, 0xe2, 0xb9, 0x91, 0xa8, 0x8a, 0xac, - 0x58, 0x5f, 0x01, 0x6d, 0x38, 0xbd, 0x23, 0x91, 0x4f, 0x3b, 0xf4, 0x01, 0xc6, 0x36, 0xb6, 0xdf, - 0xdc, 0xd2, 0x5d, 0x87, 0x0d, 0x2b, 0xce, 0xd5, 0xf0, 0x59, 0xb2, 0xac, 0x7e, 0xab, 0xfa, 0xba, - 0x95, 0x3e, 0x03, 0x42, 0x50, 0x1c, 0x77, 0xbe, 0xb8, 0x08, 0xfb, 0x7f, 0x9d, 0x85, 0x85, 0x43, - 0xea, 0xa8, 0xf7, 0x21, 0xc3, 0xfe, 0xd9, 0xba, 0x3c, 0x7a, 0x31, 0x12, 0x7b, 0x79, 0x6e, 0x67, - 0xa2, 0x3a, 0xf6, 0x1a, 0x79, 0x63, 0x2b, 0xfb, 0x78, 0x6f, 0x91, 0x7a, 0x82, 0xb7, 0xe4, 0x8a, - 0xa7, 0x7e, 0x0d, 0x5b, 0x23, 0xd7, 0xbb, 0xdd, 0xb1, 0xe6, 0xa3, 0xe0, 0xb9, 0x0f, 0x66, 0x82, - 0xcb, 0xe8, 0xdf, 0x2b, 0xa0, 0x8d, 0xdd, 0xd8, 0xf6, 0xc6, 0xfa, 0x1c, 0x67, 0x92, 0xfb, 0x70, - 0x66, 0x13, 0x99, 0x8a, 0x0d, 0x2b, 0xa9, 0xfe, 0x1e, 0x5f, 0xbf, 0x24, 0x2c, 0xb7, 0x3b, 0x15, - 0x4c, 0x46, 0xf9, 0x55, 0x01, 0x34, 0xc5, 0x62, 0xf5, 0xf1, 0x4b, 0xce, 0x31, 0xc9, 0x38, 0x57, - 0x7d, 0x05, 0x63, 0x99, 0x68, 0x0f, 0xce, 0x8f, 0x5a, 0xa4, 0xde, 0x1b, 0xeb, 0x7b, 0x04, 0x3a, - 0x77, 0x63, 0x16, 0xb4, 0x0c, 0xfd, 0x15, 0x40, 0x72, 0x9b, 0x18, 0x5f, 0x60, 0x09, 0xca, 0xbd, - 0x3b, 0x05, 0x48, 0xfa, 0xff, 0x06, 0xb2, 0xa3, 0xe7, 0x52, 0xe9, 0x65, 0x85, 0x4b, 0xe3, 0x73, - 0x37, 0x67, 0xc3, 0xa7, 0xa8, 0x96, 0xdc, 0xd8, 0x76, 0x26, 0x65, 0x2f, 0x61, 0x93, 0xa8, 0x36, - 0x62, 0x75, 0xab, 0xdc, 0x7d, 0xf6, 0x22, 0xaf, 0x3c, 0x7f, 0x91, 0x57, 0xfe, 0x78, 0x91, 0x57, - 0x7e, 0x3c, 0xce, 0xcf, 0x3d, 0x3f, 0xce, 0xcf, 0xfd, 0x76, 0x9c, 0x9f, 0xfb, 0x72, 0x37, 0xf1, - 0xa0, 0x1e, 0xb9, 0x75, 0xb6, 0x4c, 0x96, 0xe3, 0x1f, 0x90, 0x9e, 0x26, 0x7e, 0x64, 0x62, 0x6f, - 0x6b, 0x6d, 0x89, 0xfd, 0x6c, 0xf4, 0xfe, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x0a, 0xc9, 0xed, - 0x41, 0xd1, 0x12, 0x00, 0x00, + // 1338 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x58, 0x4f, 0x6f, 0x1b, 0x45, + 0x14, 0xcf, 0x26, 0x4e, 0x1a, 0xbf, 0xe6, 0x8f, 0xb3, 0x4d, 0x88, 0xeb, 0xb6, 0xb6, 0x3b, 0x25, + 0x25, 0x05, 0x62, 0x2b, 0xa1, 0x54, 0x02, 0x84, 0x50, 0xed, 0xd2, 0x92, 0xaa, 0xa9, 0xaa, 0x89, + 0x4a, 0x25, 0x0e, 0xac, 0x36, 0xbb, 0xe3, 0xf5, 0x2a, 0xf6, 0x8e, 0xb5, 0xb3, 0x76, 0x6b, 0x09, + 0x89, 0x0b, 0x42, 0x1c, 0x38, 0x70, 0x80, 0x3b, 0x27, 0x3e, 0x4b, 0x6f, 0xf4, 0x88, 0x38, 0x58, + 0xa8, 0xfd, 0x04, 0xf8, 0x13, 0xa0, 0x9d, 0x99, 0x1d, 0xef, 0x26, 0xb6, 0x6b, 0xab, 0x54, 0xaa, + 0x10, 0x27, 0xdb, 0x6f, 0x7e, 0xef, 0xf7, 0xde, 0xbc, 0xf9, 0xcd, 0xdb, 0xe7, 0x85, 0x4b, 0xcc, + 0xad, 0x79, 0xd4, 0x26, 0x65, 0x12, 0xd4, 0x8f, 0x7c, 0xd7, 0x76, 0x48, 0xb9, 0xb3, 0x5b, 0x0e, + 0x9e, 0x94, 0x5a, 0x3e, 0x0d, 0xa8, 0xbe, 0x2e, 0x97, 0x4b, 0x6a, 0xb9, 0xd4, 0xd9, 0xcd, 0xad, + 0x3b, 0xd4, 0xa1, 0x1c, 0x50, 0x0e, 0xbf, 0x09, 0x6c, 0xae, 0x38, 0x9c, 0xaa, 0xdb, 0x22, 0x4c, + 0x22, 0xde, 0x8d, 0x10, 0xd4, 0x37, 0xad, 0x06, 0x5f, 0xf6, 0x48, 0xf0, 0x98, 0xfa, 0xc7, 0x86, + 0x4d, 0x98, 0xe5, 0xbb, 0xad, 0x80, 0xfa, 0x02, 0x8b, 0x30, 0x2c, 0x1e, 0x30, 0xe7, 0x81, 0xd9, + 0x66, 0x44, 0xbf, 0x06, 0x0b, 0xcc, 0x75, 0x3c, 0xe2, 0x67, 0xb5, 0xa2, 0xb6, 0x9d, 0xae, 0xac, + 0xf5, 0x7b, 0x85, 0xe5, 0xae, 0xd9, 0x6c, 0x7c, 0x8c, 0x84, 0x1d, 0x61, 0x09, 0xd0, 0x2f, 0x40, + 0xda, 0x65, 0x46, 0x2b, 0x74, 0xb3, 0xb3, 0xb3, 0x45, 0x6d, 0x7b, 0x11, 0x2f, 0xba, 0x8c, 0xd3, + 0xd8, 0x48, 0x87, 0x4c, 0xc4, 0x89, 0x09, 0x6b, 0x51, 0x8f, 0x11, 0xd4, 0x9f, 0x83, 0x33, 0x07, + 0xcc, 0xb9, 0x47, 0xad, 0x63, 0xfd, 0x0a, 0x2c, 0x5b, 0x94, 0x35, 0x29, 0x33, 0x18, 0xf1, 0xec, + 0x28, 0x1c, 0x5e, 0x12, 0xc6, 0x43, 0x6e, 0xd3, 0x1f, 0xc1, 0x82, 0xd9, 0xa4, 0x6d, 0x2f, 0xe0, + 0xf4, 0xe9, 0xca, 0x67, 0x4f, 0x7b, 0x85, 0x99, 0x3f, 0x7b, 0x85, 0xab, 0x8e, 0x1b, 0xd4, 0xdb, + 0x47, 0x25, 0x8b, 0x36, 0xcb, 0xc2, 0x41, 0x7e, 0xec, 0x30, 0xfb, 0x58, 0x96, 0x61, 0xdf, 0x0b, + 0x06, 0xa9, 0x0b, 0x16, 0x84, 0x25, 0x9d, 0x7e, 0x1d, 0xc0, 0x26, 0x1e, 0x6d, 0x1a, 0x75, 0x93, + 0xd5, 0xb3, 0x73, 0x9c, 0x7c, 0xa3, 0xdf, 0x2b, 0xac, 0x09, 0xf8, 0x60, 0x0d, 0xe1, 0x34, 0xff, + 0xf1, 0x85, 0xc9, 0xea, 0xfa, 0x3e, 0xac, 0x91, 0xa0, 0x4e, 0x7c, 0xd2, 0x6e, 0x1a, 0x3e, 0xb1, + 0x88, 0xdb, 0x21, 0x7e, 0x76, 0x9e, 0x3b, 0x5f, 0xec, 0xf7, 0x0a, 0x59, 0xe1, 0x7c, 0x0a, 0x82, + 0x70, 0x26, 0xb2, 0x61, 0x69, 0xd2, 0x3d, 0x58, 0xb1, 0x7c, 0xca, 0x98, 0x55, 0x37, 0x5d, 0xcf, + 0xa8, 0x11, 0x92, 0x5d, 0xe0, 0x3c, 0x77, 0xa6, 0xde, 0xe1, 0x86, 0x88, 0x9a, 0x64, 0x43, 0x78, + 0x79, 0x60, 0xb8, 0x4d, 0x88, 0xee, 0x83, 0x7e, 0xfa, 0xf8, 0xb3, 0x67, 0x8a, 0xda, 0xf6, 0xca, + 0xde, 0xdb, 0xa5, 0x48, 0x79, 0x42, 0x2b, 0xa5, 0xce, 0x6e, 0xe9, 0xbe, 0x00, 0xdf, 0x52, 0xd8, + 0xca, 0xa5, 0x7e, 0xaf, 0x70, 0x5e, 0xc4, 0x3a, 0xcd, 0x84, 0xf0, 0x9a, 0x77, 0xd2, 0xe3, 0x6e, + 0x6a, 0x31, 0x95, 0x99, 0x47, 0x6b, 0xb0, 0x2a, 0xcf, 0x5c, 0xe9, 0xe0, 0xc7, 0x14, 0xd7, 0x41, + 0xa5, 0xed, 0x7b, 0xfa, 0xa7, 0x43, 0x75, 0x50, 0xc9, 0xf6, 0x7b, 0x85, 0x75, 0xb9, 0xb3, 0xf8, + 0x32, 0xfa, 0x5f, 0x21, 0xff, 0x51, 0x85, 0x84, 0x6a, 0x50, 0x0a, 0xf9, 0x5e, 0x83, 0xcd, 0x03, + 0xe6, 0x54, 0x7d, 0x62, 0x06, 0xe4, 0xf3, 0xa0, 0x5e, 0xe1, 0x3d, 0xae, 0xda, 0x30, 0xdd, 0xa6, + 0x7e, 0x0c, 0x61, 0xb1, 0x0c, 0xd1, 0xf6, 0x0c, 0x2b, 0xb4, 0x71, 0xd1, 0x9c, 0x8d, 0xa5, 0x19, + 0x6f, 0xa1, 0xa5, 0xa4, 0x7f, 0xe5, 0x42, 0xbf, 0x57, 0xd8, 0x54, 0x07, 0x91, 0xe0, 0x41, 0x78, + 0x85, 0x24, 0xc0, 0xe8, 0x32, 0x14, 0x46, 0xe4, 0xa1, 0x72, 0xfd, 0x75, 0x16, 0x2e, 0x1c, 0x30, + 0xe7, 0x61, 0xcb, 0x36, 0x03, 0xf2, 0xa8, 0xee, 0x06, 0xe4, 0x9e, 0xcb, 0x82, 0x2f, 0xcd, 0x86, + 0x6b, 0x9b, 0x01, 0xf5, 0x5f, 0x55, 0xe1, 0x7b, 0x90, 0xee, 0x44, 0x5c, 0x52, 0xe4, 0xeb, 0xfd, + 0x5e, 0x21, 0x23, 0x5c, 0xd5, 0x12, 0xc2, 0x03, 0xd8, 0x88, 0xb3, 0x4c, 0xbd, 0xce, 0xb3, 0xd4, + 0xd7, 0x61, 0xbe, 0x45, 0x1f, 0x4b, 0xb9, 0x2f, 0x63, 0xf1, 0xe3, 0x6e, 0x6a, 0x71, 0x2e, 0x93, + 0x42, 0x5b, 0x70, 0x65, 0x4c, 0x85, 0x54, 0x25, 0xbf, 0xd3, 0x60, 0x4b, 0xe1, 0xaa, 0xa1, 0x3a, + 0xab, 0x52, 0x9d, 0xd1, 0xad, 0xb8, 0x69, 0x59, 0xfc, 0x76, 0x4e, 0xf4, 0xf4, 0xb8, 0x01, 0x9b, + 0x49, 0xcd, 0x0f, 0xae, 0x24, 0xaf, 0x23, 0xde, 0x48, 0xdc, 0x80, 0x28, 0x06, 0x2a, 0xc3, 0xce, + 0x44, 0x59, 0xa8, 0xbc, 0xff, 0xd6, 0xe0, 0xad, 0x03, 0xe6, 0x60, 0xc2, 0xac, 0x76, 0xd2, 0x63, + 0xb2, 0x44, 0xdf, 0x81, 0x55, 0x09, 0x3a, 0x91, 0xe0, 0x8a, 0x30, 0xab, 0x9e, 0xb0, 0x07, 0x1b, + 0x27, 0x76, 0xc4, 0xba, 0xcd, 0x23, 0xda, 0x10, 0xfd, 0x09, 0x9f, 0x4b, 0xec, 0xe7, 0x90, 0x2f, + 0xe9, 0x0f, 0x4f, 0xf5, 0x91, 0x14, 0x17, 0x51, 0x69, 0xba, 0x3e, 0x72, 0xa2, 0x5d, 0xa0, 0x2a, + 0xbf, 0xb4, 0x87, 0x24, 0xa8, 0x34, 0x4c, 0xeb, 0xb8, 0xe1, 0xb2, 0x40, 0xd7, 0x21, 0x55, 0xf3, + 0x69, 0x53, 0x6e, 0x91, 0x7f, 0xd7, 0x2f, 0x42, 0xda, 0xb4, 0x6d, 0x9f, 0x30, 0x46, 0x58, 0x76, + 0xb6, 0x38, 0xb7, 0x9d, 0xc6, 0x03, 0x03, 0x3a, 0xcf, 0x6f, 0x79, 0x9c, 0x44, 0xd5, 0xb4, 0x08, + 0xf9, 0xe1, 0x25, 0x55, 0x88, 0xa7, 0xf3, 0xb0, 0x2c, 0xbc, 0x6f, 0x13, 0xb2, 0xef, 0xd5, 0xe8, + 0x64, 0xc5, 0x1e, 0x7e, 0x37, 0x66, 0x5f, 0xeb, 0xdd, 0xb8, 0x0c, 0x4b, 0xe1, 0x61, 0x59, 0x6d, + 0xdf, 0x27, 0x9e, 0xd5, 0x95, 0xc7, 0x75, 0xb6, 0x46, 0x48, 0x55, 0x9a, 0x74, 0x06, 0x99, 0x38, + 0xc4, 0x70, 0x4c, 0x26, 0x0f, 0x6a, 0x7f, 0xea, 0x86, 0x2f, 0xbb, 0xdb, 0x49, 0x3e, 0x84, 0x57, + 0x62, 0x11, 0xef, 0x98, 0x4c, 0xef, 0xc0, 0x5a, 0xd3, 0xf5, 0xdc, 0x66, 0xbb, 0x69, 0x34, 0xa8, + 0x75, 0x6c, 0x58, 0x94, 0x05, 0xf2, 0x71, 0x75, 0x77, 0xea, 0xa8, 0xf2, 0xe1, 0x76, 0x8a, 0x10, + 0xe1, 0x55, 0x69, 0x0b, 0x87, 0x80, 0x2a, 0x65, 0x41, 0x3c, 0xee, 0x51, 0xdb, 0xf7, 0x44, 0xdc, + 0x85, 0x7f, 0x27, 0xae, 0x22, 0x1c, 0xc4, 0x0d, 0x1f, 0x2d, 0x3c, 0xee, 0x2f, 0x1a, 0x5c, 0xac, + 0xb9, 0x3e, 0x0b, 0x04, 0xca, 0xa6, 0xed, 0xa3, 0x06, 0x31, 0x5a, 0xc4, 0x71, 0xba, 0x22, 0x87, + 0x33, 0x3c, 0x87, 0x87, 0x53, 0xe7, 0x70, 0x45, 0x56, 0x7c, 0x0c, 0x37, 0xc2, 0x59, 0xbe, 0x1c, + 0x26, 0x73, 0x8b, 0x2f, 0x3e, 0x08, 0xd7, 0xc2, 0xbc, 0xd0, 0x26, 0x6c, 0x24, 0x94, 0xac, 0x34, + 0xfe, 0xf3, 0xac, 0xb8, 0x66, 0xae, 0xe3, 0x3d, 0xf0, 0x69, 0xab, 0x4e, 0xac, 0xee, 0x9b, 0xab, + 0xf2, 0x02, 0x9c, 0x6d, 0xc9, 0x24, 0x0d, 0xd7, 0xe6, 0x22, 0x5f, 0xc2, 0x10, 0x99, 0xf6, 0x6d, + 0xfd, 0x1a, 0xa8, 0x31, 0xc7, 0x90, 0x4d, 0x40, 0x68, 0x1c, 0xaf, 0x46, 0xf6, 0x9b, 0xc2, 0x1c, + 0xf6, 0x8d, 0xf0, 0x5f, 0x86, 0x19, 0xb4, 0x7d, 0x22, 0x14, 0x89, 0x07, 0x86, 0xa8, 0x6f, 0xc4, + 0xaa, 0xa2, 0x2a, 0xd6, 0xd3, 0x20, 0x3b, 0xe8, 0xde, 0xa1, 0xc9, 0x63, 0x6d, 0x76, 0x9f, 0x10, + 0x9b, 0xd8, 0x6f, 0x6e, 0xe9, 0xae, 0x41, 0xc6, 0x8a, 0x72, 0x35, 0x3c, 0x9e, 0x2c, 0xaf, 0xdf, + 0x32, 0x5e, 0xb5, 0x92, 0x7b, 0x40, 0x08, 0x8a, 0xa3, 0xf6, 0x17, 0x15, 0x61, 0xef, 0xf7, 0x34, + 0xcc, 0x1d, 0x30, 0x47, 0xbf, 0x07, 0x29, 0xfe, 0x67, 0xeb, 0xd2, 0xf0, 0xc1, 0x48, 0xce, 0xe5, + 0xb9, 0xad, 0xb1, 0xcb, 0x11, 0x6b, 0xc8, 0xc6, 0x47, 0xf6, 0xd1, 0x6c, 0xe1, 0xf2, 0x18, 0xb6, + 0xf8, 0x88, 0xa7, 0x7f, 0x03, 0xeb, 0x43, 0xc7, 0xbb, 0x9d, 0x91, 0xee, 0xc3, 0xe0, 0xb9, 0x0f, + 0xa7, 0x82, 0xab, 0xe8, 0x3f, 0x68, 0x90, 0x1d, 0x39, 0xb1, 0xed, 0x8e, 0xe4, 0x1c, 0xe5, 0x92, + 0xfb, 0x68, 0x6a, 0x17, 0x95, 0x8a, 0x0d, 0x4b, 0x89, 0xfb, 0x3d, 0xba, 0x7e, 0x71, 0x58, 0x6e, + 0x67, 0x22, 0x98, 0x8a, 0xf2, 0x9b, 0x06, 0x68, 0x82, 0xc1, 0xea, 0x93, 0x97, 0xec, 0x63, 0x9c, + 0x73, 0xae, 0xfa, 0x0a, 0xce, 0x2a, 0xd1, 0x2e, 0x9c, 0x1b, 0x36, 0x48, 0xbd, 0x3f, 0x92, 0x7b, + 0x08, 0x3a, 0x77, 0x7d, 0x1a, 0xb4, 0x0a, 0xfd, 0x35, 0x40, 0x7c, 0x9a, 0x18, 0x5d, 0x60, 0x05, + 0xca, 0xbd, 0x37, 0x01, 0x48, 0xf1, 0x7f, 0x0b, 0x1b, 0xc3, 0xfb, 0x52, 0xe9, 0x65, 0x85, 0x4b, + 0xe2, 0x73, 0x37, 0xa6, 0xc3, 0x27, 0xa4, 0x16, 0x9f, 0xd8, 0xb6, 0xc6, 0x65, 0xaf, 0x60, 0xe3, + 0xa4, 0x36, 0x64, 0x74, 0xd3, 0x31, 0x2c, 0x1e, 0x92, 0x40, 0xbc, 0x4e, 0xca, 0x8f, 0x74, 0xe5, + 0xeb, 0xb9, 0xab, 0xe3, 0xd7, 0x23, 0xce, 0xca, 0x9d, 0xa7, 0xcf, 0xf3, 0xda, 0xb3, 0xe7, 0x79, + 0xed, 0xaf, 0xe7, 0x79, 0xed, 0xa7, 0x17, 0xf9, 0x99, 0x67, 0x2f, 0xf2, 0x33, 0x7f, 0xbc, 0xc8, + 0xcf, 0x7c, 0xb5, 0x13, 0x7b, 0x48, 0x1f, 0xba, 0x35, 0x3e, 0xa0, 0x96, 0xa3, 0x97, 0x5f, 0x4f, + 0x62, 0x2f, 0xc8, 0xf8, 0xf3, 0xfa, 0x68, 0x81, 0xbf, 0xf2, 0xfa, 0xe0, 0x9f, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x28, 0x53, 0xc6, 0x8a, 0x8d, 0x13, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1146,6 +1240,7 @@ type MsgClient interface { SetFeeInfo(ctx context.Context, in *MsgSetFeeInfo, opts ...grpc.CallOption) (*MsgSetFeeInfoResponse, error) UpdateConsensusNeeded(ctx context.Context, in *MsgUpdateConsensusNeeded, opts ...grpc.CallOption) (*MsgUpdateConsensusNeededResponse, error) SetBlacklist(ctx context.Context, in *MsgSetBlacklist, opts ...grpc.CallOption) (*MsgSetBlacklistResponse, error) + SetPause(ctx context.Context, in *MsgPause, opts ...grpc.CallOption) (*MsgPauseResponse, error) } type msgClient struct { @@ -1246,6 +1341,15 @@ func (c *msgClient) SetBlacklist(ctx context.Context, in *MsgSetBlacklist, opts return out, nil } +func (c *msgClient) SetPause(ctx context.Context, in *MsgPause, opts ...grpc.CallOption) (*MsgPauseResponse, error) { + out := new(MsgPauseResponse) + err := c.cc.Invoke(ctx, "/sifnode.ethbridge.v1.Msg/SetPause", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { Lock(context.Context, *MsgLock) (*MsgLockResponse, error) @@ -1258,6 +1362,7 @@ type MsgServer interface { SetFeeInfo(context.Context, *MsgSetFeeInfo) (*MsgSetFeeInfoResponse, error) UpdateConsensusNeeded(context.Context, *MsgUpdateConsensusNeeded) (*MsgUpdateConsensusNeededResponse, error) SetBlacklist(context.Context, *MsgSetBlacklist) (*MsgSetBlacklistResponse, error) + SetPause(context.Context, *MsgPause) (*MsgPauseResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -1294,6 +1399,9 @@ func (*UnimplementedMsgServer) UpdateConsensusNeeded(ctx context.Context, req *M func (*UnimplementedMsgServer) SetBlacklist(ctx context.Context, req *MsgSetBlacklist) (*MsgSetBlacklistResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SetBlacklist not implemented") } +func (*UnimplementedMsgServer) SetPause(ctx context.Context, req *MsgPause) (*MsgPauseResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetPause not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -1479,6 +1587,24 @@ func _Msg_SetBlacklist_Handler(srv interface{}, ctx context.Context, dec func(in return interceptor(ctx, in, info, handler) } +func _Msg_SetPause_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgPause) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SetPause(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/sifnode.ethbridge.v1.Msg/SetPause", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SetPause(ctx, req.(*MsgPause)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "sifnode.ethbridge.v1.Msg", HandlerType: (*MsgServer)(nil), @@ -1523,11 +1649,78 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "SetBlacklist", Handler: _Msg_SetBlacklist_Handler, }, + { + MethodName: "SetPause", + Handler: _Msg_SetPause_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "sifnode/ethbridge/v1/tx.proto", } +func (m *MsgPause) 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 *MsgPause) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPause) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.IsPaused { + i-- + if m.IsPaused { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgPauseResponse) 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 *MsgPauseResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPauseResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func (m *MsgLock) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -2297,6 +2490,31 @@ func encodeVarintTx(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } +func (m *MsgPause) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.IsPaused { + n += 2 + } + return n +} + +func (m *MsgPauseResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func (m *MsgLock) Size() (n int) { if m == nil { return 0 @@ -2619,6 +2837,158 @@ func sovTx(x uint64) (n int) { func sozTx(x uint64) (n int) { return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } +func (m *MsgPause) 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: MsgPause: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPause: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsPaused", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsPaused = bool(v != 0) + 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 *MsgPauseResponse) 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: MsgPauseResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPauseResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + 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 *MsgLock) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/ethbridge/types/types.pb.go b/x/ethbridge/types/types.pb.go index 1bcc8da79f..69189d4c2a 100644 --- a/x/ethbridge/types/types.pb.go +++ b/x/ethbridge/types/types.pb.go @@ -318,69 +318,115 @@ func (m *GenesisState) GetCrosschainFeeReceiveAccount() string { return "" } +type Pause struct { + IsPaused bool `protobuf:"varint,1,opt,name=is_paused,json=isPaused,proto3" json:"is_paused,omitempty"` +} + +func (m *Pause) Reset() { *m = Pause{} } +func (m *Pause) String() string { return proto.CompactTextString(m) } +func (*Pause) ProtoMessage() {} +func (*Pause) Descriptor() ([]byte, []int) { + return fileDescriptor_4cb34f678c9ed59f, []int{3} +} +func (m *Pause) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Pause) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Pause.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 *Pause) XXX_Merge(src proto.Message) { + xxx_messageInfo_Pause.Merge(m, src) +} +func (m *Pause) XXX_Size() int { + return m.Size() +} +func (m *Pause) XXX_DiscardUnknown() { + xxx_messageInfo_Pause.DiscardUnknown(m) +} + +var xxx_messageInfo_Pause proto.InternalMessageInfo + +func (m *Pause) GetIsPaused() bool { + if m != nil { + return m.IsPaused + } + return false +} + func init() { proto.RegisterEnum("sifnode.ethbridge.v1.ClaimType", ClaimType_name, ClaimType_value) proto.RegisterType((*EthBridgeClaim)(nil), "sifnode.ethbridge.v1.EthBridgeClaim") proto.RegisterType((*PeggyTokens)(nil), "sifnode.ethbridge.v1.PeggyTokens") proto.RegisterType((*GenesisState)(nil), "sifnode.ethbridge.v1.GenesisState") + proto.RegisterType((*Pause)(nil), "sifnode.ethbridge.v1.Pause") } func init() { proto.RegisterFile("sifnode/ethbridge/v1/types.proto", fileDescriptor_4cb34f678c9ed59f) } var fileDescriptor_4cb34f678c9ed59f = []byte{ - // 823 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x54, 0xcd, 0x72, 0x1a, 0x47, - 0x10, 0x66, 0x25, 0x4c, 0x60, 0x24, 0x23, 0x34, 0x96, 0xe4, 0xb5, 0x1c, 0xb3, 0x78, 0x2a, 0x71, - 0x11, 0xa7, 0x0c, 0x71, 0x7e, 0x2e, 0x3e, 0x24, 0x25, 0x90, 0xec, 0xc2, 0x51, 0x14, 0x65, 0x90, - 0xcb, 0x15, 0x5f, 0xb6, 0x96, 0xd9, 0x16, 0x6c, 0xc1, 0xee, 0x90, 0x99, 0x81, 0x84, 0xb7, 0xc8, - 0x3d, 0x2f, 0x90, 0x47, 0xf1, 0xd1, 0xc7, 0x54, 0x0e, 0x5b, 0x29, 0xe9, 0x0d, 0xf6, 0x09, 0x52, - 0x3b, 0xb3, 0x60, 0x0c, 0xf2, 0x09, 0xa6, 0xbf, 0xaf, 0xbf, 0x9e, 0xed, 0xfe, 0x7a, 0x50, 0x4d, - 0x06, 0x97, 0x11, 0xf7, 0xa1, 0x09, 0x6a, 0xd0, 0x13, 0x81, 0xdf, 0x87, 0xe6, 0xf4, 0x69, 0x53, - 0xcd, 0xc6, 0x20, 0x1b, 0x63, 0xc1, 0x15, 0xc7, 0x7b, 0x19, 0xa3, 0xb1, 0x60, 0x34, 0xa6, 0x4f, - 0x0f, 0xf7, 0xfa, 0xbc, 0xcf, 0x35, 0xa1, 0x99, 0xfe, 0x33, 0xdc, 0xc3, 0xc7, 0x73, 0x35, 0x2e, - 0x3c, 0x36, 0xd2, 0x52, 0x11, 0xa8, 0xdf, 0xb9, 0x18, 0xba, 0x3e, 0x48, 0x26, 0x82, 0xb1, 0xe2, - 0xc2, 0x70, 0xc9, 0x5f, 0x45, 0x54, 0x3e, 0x51, 0x83, 0x96, 0x96, 0x6c, 0x8f, 0xbc, 0x20, 0xc4, - 0x6f, 0xd0, 0x5d, 0x53, 0xc1, 0x65, 0x3c, 0x52, 0xc2, 0x63, 0xca, 0xf5, 0x7c, 0x5f, 0x80, 0x94, - 0xf6, 0x46, 0xcd, 0xaa, 0x97, 0x5a, 0x24, 0x89, 0x9d, 0xea, 0xcc, 0x0b, 0x47, 0xcf, 0xc8, 0x47, - 0x88, 0x84, 0xee, 0x1b, 0xa4, 0x9d, 0x01, 0x47, 0x26, 0x8e, 0xbf, 0x40, 0x05, 0x39, 0x0b, 0x7b, - 0x7c, 0x64, 0xe7, 0xb5, 0xd4, 0x6e, 0x12, 0x3b, 0xb7, 0x8d, 0x94, 0x89, 0x13, 0x9a, 0x11, 0xf0, - 0x6b, 0x74, 0xa0, 0xf8, 0x10, 0xa2, 0xf5, 0x5b, 0xdc, 0xd2, 0xa9, 0x0f, 0x93, 0xd8, 0x79, 0x60, - 0x52, 0x6f, 0xe6, 0x11, 0xba, 0xa7, 0x81, 0xd5, 0x3b, 0xb4, 0xd1, 0x0e, 0xa8, 0x01, 0x08, 0x98, - 0x84, 0xae, 0x84, 0xc8, 0x07, 0x61, 0x17, 0xb4, 0xe2, 0x61, 0x12, 0x3b, 0x07, 0x46, 0x71, 0x85, - 0x40, 0x68, 0x79, 0x1e, 0xe9, 0xea, 0x40, 0x2a, 0xc2, 0xb8, 0x0c, 0xb9, 0x74, 0x05, 0x30, 0x08, - 0xa6, 0x20, 0xec, 0x4f, 0x56, 0x45, 0x56, 0x08, 0x84, 0x96, 0x4d, 0x84, 0x66, 0x01, 0xdc, 0x41, - 0xbb, 0x53, 0x6f, 0x14, 0xf8, 0x9e, 0xe2, 0x62, 0xf1, 0x75, 0x45, 0x2d, 0xf3, 0x69, 0x12, 0x3b, - 0xb6, 0x91, 0x59, 0xa3, 0x10, 0x5a, 0x59, 0xc4, 0xe6, 0x1f, 0xf5, 0x1a, 0x15, 0xbc, 0x90, 0x4f, - 0x22, 0x65, 0x97, 0x74, 0xfe, 0x0f, 0x6f, 0x63, 0x27, 0xf7, 0x6f, 0xec, 0x3c, 0xea, 0x07, 0x6a, - 0x30, 0xe9, 0x35, 0x18, 0x0f, 0x9b, 0xa6, 0x7a, 0xf6, 0xf3, 0x44, 0xfa, 0xc3, 0xcc, 0x61, 0x9d, - 0x48, 0xbd, 0x1f, 0x83, 0x51, 0x21, 0x34, 0x93, 0xc3, 0xdf, 0x23, 0xc4, 0x52, 0x5b, 0xb8, 0x29, - 0xd7, 0x46, 0x35, 0xab, 0x5e, 0xfe, 0xda, 0x69, 0xdc, 0xe4, 0xc6, 0x86, 0xb6, 0xcf, 0xc5, 0x6c, - 0x0c, 0xb4, 0xc4, 0xe6, 0x7f, 0xf1, 0xb7, 0x08, 0x99, 0xf1, 0x44, 0x5e, 0x08, 0xf6, 0x96, 0xbe, - 0xdc, 0x7e, 0x12, 0x3b, 0xbb, 0xcb, 0xa3, 0x4b, 0x31, 0x42, 0x4b, 0xfa, 0x70, 0xe6, 0x85, 0x80, - 0xbf, 0x43, 0x45, 0x1f, 0x58, 0x10, 0x7a, 0x23, 0x69, 0x6f, 0xd7, 0xac, 0xfa, 0x66, 0xeb, 0x5e, - 0x12, 0x3b, 0xfb, 0xcb, 0x39, 0x73, 0x9c, 0xd0, 0x05, 0x15, 0x7f, 0x89, 0x6e, 0xf9, 0x10, 0xf1, - 0xd0, 0xbe, 0xbd, 0x5a, 0x47, 0x87, 0xdd, 0x81, 0x27, 0x07, 0x84, 0x1a, 0x0e, 0x7e, 0x86, 0xb6, - 0xb3, 0x09, 0x99, 0x9c, 0xb2, 0xce, 0xb9, 0x9b, 0xc4, 0xce, 0x9d, 0x0f, 0xe6, 0xa7, 0x51, 0x42, - 0xb7, 0xcc, 0xf1, 0x58, 0xe7, 0x0a, 0x84, 0xd7, 0x57, 0xca, 0xde, 0xd1, 0xdd, 0xf9, 0x6c, 0xd1, - 0x1d, 0xb3, 0x7f, 0x69, 0x6b, 0xce, 0x0c, 0xf9, 0x78, 0xc1, 0x6d, 0x3d, 0x48, 0x62, 0xe7, 0x9e, - 0xa9, 0xb3, 0xae, 0x44, 0xe8, 0x6e, 0xb4, 0x9a, 0x81, 0x01, 0xdd, 0x5f, 0xd8, 0x72, 0xc4, 0xd9, - 0xd0, 0xed, 0x4d, 0x44, 0xe4, 0x4a, 0xf8, 0x6d, 0x02, 0x11, 0x03, 0xbb, 0x52, 0xb3, 0xea, 0xf9, - 0xd6, 0xa3, 0x24, 0x76, 0xc8, 0x8a, 0x87, 0xd7, 0xc9, 0x84, 0xda, 0x73, 0xf4, 0x94, 0xb3, 0x61, - 0x6b, 0x22, 0xa2, 0x6e, 0x06, 0xbd, 0xcc, 0x17, 0xad, 0xca, 0xc6, 0xcb, 0x7c, 0x71, 0xb3, 0x92, - 0x27, 0x9f, 0xa3, 0xad, 0x73, 0xe8, 0xf7, 0x67, 0x17, 0x69, 0xc7, 0x25, 0x3e, 0x40, 0x05, 0xdd, - 0x7b, 0x69, 0x5b, 0xb5, 0xcd, 0x7a, 0x89, 0x66, 0x27, 0xf2, 0xb7, 0x85, 0xb6, 0x5f, 0x40, 0x04, - 0x32, 0x90, 0x5d, 0xe5, 0x29, 0xc0, 0x5f, 0xa1, 0x3d, 0x06, 0x6a, 0x30, 0xb7, 0xbe, 0xeb, 0x31, - 0xa6, 0xbd, 0x69, 0xa5, 0x2d, 0xa6, 0x38, 0xc5, 0xb2, 0x25, 0x38, 0x32, 0x08, 0x7e, 0x88, 0xb6, - 0xc7, 0x69, 0x25, 0x37, 0x2b, 0xb0, 0xa1, 0x0b, 0x6c, 0x8d, 0x97, 0xaa, 0xb7, 0x51, 0x95, 0x09, - 0x2e, 0x25, 0x1b, 0x78, 0x41, 0xe4, 0x5e, 0x02, 0xac, 0xc9, 0x6f, 0x6a, 0xf9, 0xfb, 0xef, 0x59, - 0xcf, 0x01, 0x3e, 0xac, 0xf3, 0xf8, 0x17, 0x54, 0x5a, 0xd8, 0x14, 0x1f, 0xa2, 0x83, 0xf6, 0xe9, - 0x51, 0xe7, 0x27, 0xf7, 0xe2, 0xd7, 0xf3, 0x13, 0xf7, 0xd5, 0x59, 0xf7, 0xfc, 0xa4, 0xdd, 0x79, - 0xde, 0x39, 0x39, 0xae, 0xe4, 0xf0, 0x1d, 0xb4, 0xb3, 0x84, 0xb5, 0x5e, 0xd1, 0xb3, 0x8a, 0xb5, - 0x12, 0x3c, 0xfd, 0xb9, 0xfd, 0x63, 0x65, 0xa3, 0xf5, 0xe2, 0xed, 0x55, 0xd5, 0x7a, 0x77, 0x55, - 0xb5, 0xfe, 0xbb, 0xaa, 0x5a, 0x7f, 0x5e, 0x57, 0x73, 0xef, 0xae, 0xab, 0xb9, 0x7f, 0xae, 0xab, - 0xb9, 0x37, 0x4f, 0x96, 0x96, 0xaf, 0x1b, 0x5c, 0xea, 0x2b, 0x35, 0xe7, 0x8f, 0xf3, 0x1f, 0x4b, - 0x8f, 0xbd, 0xde, 0xc3, 0x5e, 0x41, 0x3f, 0xc9, 0xdf, 0xfc, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xb5, - 0x80, 0xd5, 0xf3, 0x0e, 0x06, 0x00, 0x00, + // 847 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x55, 0xdd, 0x72, 0xda, 0x46, + 0x14, 0x46, 0x36, 0xa6, 0xb0, 0x76, 0x30, 0xde, 0xd8, 0x8e, 0x62, 0x37, 0x88, 0xec, 0xa4, 0x19, + 0x9a, 0x4e, 0xa0, 0xe9, 0xcf, 0x4d, 0x2e, 0xda, 0x31, 0xd8, 0xc9, 0x90, 0xba, 0xae, 0xbb, 0x38, + 0x93, 0x69, 0x6e, 0x34, 0x62, 0x75, 0x0c, 0x1a, 0x90, 0x96, 0xee, 0x2e, 0x6e, 0x79, 0x8b, 0xde, + 0xf7, 0x05, 0xfa, 0x28, 0xb9, 0xcc, 0x65, 0xa7, 0x17, 0x9a, 0x8e, 0xfd, 0x06, 0x7a, 0x82, 0x8e, + 0x76, 0x05, 0x21, 0xe0, 0x5e, 0xb1, 0x7b, 0xbe, 0xef, 0x7c, 0xe7, 0xe8, 0xfc, 0x2c, 0xa8, 0x26, + 0x83, 0xcb, 0x88, 0xfb, 0xd0, 0x04, 0x35, 0xe8, 0x89, 0xc0, 0xef, 0x43, 0xf3, 0xea, 0x59, 0x53, + 0x4d, 0xc7, 0x20, 0x1b, 0x63, 0xc1, 0x15, 0xc7, 0xbb, 0x19, 0xa3, 0x31, 0x67, 0x34, 0xae, 0x9e, + 0x1d, 0xec, 0xf6, 0x79, 0x9f, 0x6b, 0x42, 0x33, 0x3d, 0x19, 0xee, 0xc1, 0x93, 0x99, 0x1a, 0x17, + 0x1e, 0x1b, 0x69, 0xa9, 0x08, 0xd4, 0x6f, 0x5c, 0x0c, 0x5d, 0x1f, 0x24, 0x13, 0xc1, 0x58, 0x71, + 0x61, 0xb8, 0xe4, 0xcf, 0x22, 0x2a, 0x9f, 0xa8, 0x41, 0x4b, 0x4b, 0xb6, 0x47, 0x5e, 0x10, 0xe2, + 0xb7, 0xe8, 0x9e, 0x89, 0xe0, 0x32, 0x1e, 0x29, 0xe1, 0x31, 0xe5, 0x7a, 0xbe, 0x2f, 0x40, 0x4a, + 0x7b, 0xad, 0x66, 0xd5, 0x4b, 0x2d, 0x92, 0xc4, 0x4e, 0x75, 0xea, 0x85, 0xa3, 0xe7, 0xe4, 0x7f, + 0x88, 0x84, 0xee, 0x19, 0xa4, 0x9d, 0x01, 0x47, 0xc6, 0x8e, 0x3f, 0x47, 0x05, 0x39, 0x0d, 0x7b, + 0x7c, 0x64, 0xe7, 0xb5, 0xd4, 0x4e, 0x12, 0x3b, 0x77, 0x8c, 0x94, 0xb1, 0x13, 0x9a, 0x11, 0xf0, + 0x1b, 0xb4, 0xaf, 0xf8, 0x10, 0xa2, 0xd5, 0x2c, 0x36, 0xb4, 0xeb, 0xc3, 0x24, 0x76, 0x1e, 0x18, + 0xd7, 0xdb, 0x79, 0x84, 0xee, 0x6a, 0x60, 0x39, 0x87, 0x36, 0xda, 0x06, 0x35, 0x00, 0x01, 0x93, + 0xd0, 0x95, 0x10, 0xf9, 0x20, 0xec, 0x82, 0x56, 0x3c, 0x48, 0x62, 0x67, 0xdf, 0x28, 0x2e, 0x11, + 0x08, 0x2d, 0xcf, 0x2c, 0x5d, 0x6d, 0x48, 0x45, 0x18, 0x97, 0x21, 0x97, 0xae, 0x00, 0x06, 0xc1, + 0x15, 0x08, 0xfb, 0x93, 0x65, 0x91, 0x25, 0x02, 0xa1, 0x65, 0x63, 0xa1, 0x99, 0x01, 0x77, 0xd0, + 0xce, 0x95, 0x37, 0x0a, 0x7c, 0x4f, 0x71, 0x31, 0xff, 0xba, 0xa2, 0x96, 0xf9, 0x34, 0x89, 0x1d, + 0xdb, 0xc8, 0xac, 0x50, 0x08, 0xad, 0xcc, 0x6d, 0xb3, 0x8f, 0x7a, 0x83, 0x0a, 0x5e, 0xc8, 0x27, + 0x91, 0xb2, 0x4b, 0xda, 0xff, 0xfb, 0x77, 0xb1, 0x93, 0xfb, 0x27, 0x76, 0x1e, 0xf7, 0x03, 0x35, + 0x98, 0xf4, 0x1a, 0x8c, 0x87, 0x4d, 0x13, 0x3d, 0xfb, 0x79, 0x2a, 0xfd, 0x61, 0x36, 0x61, 0x9d, + 0x48, 0x7d, 0x68, 0x83, 0x51, 0x21, 0x34, 0x93, 0xc3, 0xdf, 0x21, 0xc4, 0xd2, 0xb1, 0x70, 0x53, + 0xae, 0x8d, 0x6a, 0x56, 0xbd, 0xfc, 0x95, 0xd3, 0xb8, 0x6d, 0x1a, 0x1b, 0x7a, 0x7c, 0x2e, 0xa6, + 0x63, 0xa0, 0x25, 0x36, 0x3b, 0xe2, 0x6f, 0x10, 0x32, 0xed, 0x89, 0xbc, 0x10, 0xec, 0x4d, 0x9d, + 0xdc, 0x5e, 0x12, 0x3b, 0x3b, 0x8b, 0xad, 0x4b, 0x31, 0x42, 0x4b, 0xfa, 0x72, 0xe6, 0x85, 0x80, + 0xbf, 0x45, 0x45, 0x1f, 0x58, 0x10, 0x7a, 0x23, 0x69, 0x6f, 0xd5, 0xac, 0xfa, 0x7a, 0xeb, 0x7e, + 0x12, 0x3b, 0x7b, 0x8b, 0x3e, 0x33, 0x9c, 0xd0, 0x39, 0x15, 0x7f, 0x81, 0x36, 0x7c, 0x88, 0x78, + 0x68, 0xdf, 0x59, 0x8e, 0xa3, 0xcd, 0xee, 0xc0, 0x93, 0x03, 0x42, 0x0d, 0x07, 0x3f, 0x47, 0x5b, + 0x59, 0x87, 0x8c, 0x4f, 0x59, 0xfb, 0xdc, 0x4b, 0x62, 0xe7, 0xee, 0x47, 0xfd, 0xd3, 0x28, 0xa1, + 0x9b, 0xe6, 0x7a, 0xac, 0x7d, 0x05, 0xc2, 0xab, 0x2b, 0x65, 0x6f, 0xeb, 0xea, 0x3c, 0x9a, 0x57, + 0xc7, 0xec, 0x5f, 0x5a, 0x9a, 0x33, 0x43, 0x3e, 0x9e, 0x73, 0x5b, 0x0f, 0x92, 0xd8, 0xb9, 0x6f, + 0xe2, 0xac, 0x2a, 0x11, 0xba, 0x13, 0x2d, 0x7b, 0x60, 0x40, 0x87, 0xf3, 0xb1, 0x1c, 0x71, 0x36, + 0x74, 0x7b, 0x13, 0x11, 0xb9, 0x12, 0x7e, 0x9d, 0x40, 0xc4, 0xc0, 0xae, 0xd4, 0xac, 0x7a, 0xbe, + 0xf5, 0x38, 0x89, 0x1d, 0xb2, 0x34, 0xc3, 0xab, 0x64, 0x42, 0xed, 0x19, 0x7a, 0xca, 0xd9, 0xb0, + 0x35, 0x11, 0x51, 0x37, 0x83, 0x5e, 0xe5, 0x8b, 0x56, 0x65, 0xed, 0x55, 0xbe, 0xb8, 0x5e, 0xc9, + 0x93, 0xcf, 0xd0, 0xe6, 0x39, 0xf4, 0xfb, 0xd3, 0x8b, 0xb4, 0xe2, 0x12, 0xef, 0xa3, 0x82, 0xae, + 0xbd, 0xb4, 0xad, 0xda, 0x7a, 0xbd, 0x44, 0xb3, 0x1b, 0xf9, 0xcb, 0x42, 0x5b, 0x2f, 0x21, 0x02, + 0x19, 0xc8, 0xae, 0xf2, 0x14, 0xe0, 0x2f, 0xd1, 0x2e, 0x03, 0x35, 0x98, 0x8d, 0xbe, 0xeb, 0x31, + 0xa6, 0x67, 0xd3, 0x4a, 0x4b, 0x4c, 0x71, 0x8a, 0x65, 0x4b, 0x70, 0x64, 0x10, 0xfc, 0x10, 0x6d, + 0x8d, 0xd3, 0x48, 0x6e, 0x16, 0x60, 0x4d, 0x07, 0xd8, 0x1c, 0x2f, 0x44, 0x6f, 0xa3, 0x2a, 0x13, + 0x5c, 0x4a, 0x36, 0xf0, 0x82, 0xc8, 0xbd, 0x04, 0x58, 0x91, 0x5f, 0xd7, 0xf2, 0x87, 0x1f, 0x58, + 0x2f, 0x00, 0x3e, 0x8e, 0x43, 0x1e, 0xa1, 0x8d, 0x73, 0x6f, 0x22, 0x01, 0x1f, 0xa2, 0x52, 0x20, + 0xdd, 0x71, 0x7a, 0xf6, 0x75, 0x5e, 0x45, 0x5a, 0x0c, 0xa4, 0xc6, 0xfc, 0x27, 0x3f, 0xa3, 0xd2, + 0x7c, 0x98, 0xf1, 0x01, 0xda, 0x6f, 0x9f, 0x1e, 0x75, 0x7e, 0x74, 0x2f, 0x7e, 0x39, 0x3f, 0x71, + 0x5f, 0x9f, 0x75, 0xcf, 0x4f, 0xda, 0x9d, 0x17, 0x9d, 0x93, 0xe3, 0x4a, 0x0e, 0xdf, 0x45, 0xdb, + 0x0b, 0x58, 0xeb, 0x35, 0x3d, 0xab, 0x58, 0x4b, 0xc6, 0xd3, 0x9f, 0xda, 0x3f, 0x54, 0xd6, 0x5a, + 0x2f, 0xdf, 0x5d, 0x57, 0xad, 0xf7, 0xd7, 0x55, 0xeb, 0xdf, 0xeb, 0xaa, 0xf5, 0xc7, 0x4d, 0x35, + 0xf7, 0xfe, 0xa6, 0x9a, 0xfb, 0xfb, 0xa6, 0x9a, 0x7b, 0xfb, 0x74, 0x61, 0x45, 0xbb, 0xc1, 0xa5, + 0x4e, 0xbc, 0x39, 0x7b, 0xc2, 0x7f, 0x5f, 0xf8, 0x4b, 0xd0, 0xdb, 0xda, 0x2b, 0xe8, 0x87, 0xfb, + 0xeb, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x52, 0xe5, 0x8e, 0xa1, 0x34, 0x06, 0x00, 0x00, } func (m *EthBridgeClaim) Marshal() (dAtA []byte, err error) { @@ -579,6 +625,39 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *Pause) 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 *Pause) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Pause) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.IsPaused { + i-- + if m.IsPaused { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { offset -= sovTypes(v) base := offset @@ -687,6 +766,18 @@ func (m *GenesisState) Size() (n int) { return n } +func (m *Pause) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.IsPaused { + n += 2 + } + return n +} + func sovTypes(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1369,6 +1460,76 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { } return nil } +func (m *Pause) 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 ErrIntOverflowTypes + } + 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: Pause: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Pause: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsPaused", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsPaused = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTypes(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/ethbridge/utils/utils.go b/x/ethbridge/utils/utils.go new file mode 100644 index 0000000000..01b2afb3eb --- /dev/null +++ b/x/ethbridge/utils/utils.go @@ -0,0 +1,13 @@ +package utils + +import "github.com/pkg/errors" + +func ParseStringToBool(s string) (bool, error) { + if s == "true" || s == "True" || s == "TRUE" { + return true, nil + } + if s == "false" || s == "False" || s == "FALSE" { + return false, nil + } + return false, errors.New("Can only accept true or false") +} diff --git a/x/ethbridge/utils/utils_test.go b/x/ethbridge/utils/utils_test.go new file mode 100644 index 0000000000..37fcd3364a --- /dev/null +++ b/x/ethbridge/utils/utils_test.go @@ -0,0 +1,69 @@ +package utils_test + +import ( + "github.com/Sifchain/sifnode/x/ethbridge/utils" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestParseStringToBool(t *testing.T) { + tt := []struct { + name string + input string + expected bool + isError bool + }{ + { + name: "TC_1", + input: "true", + expected: true, + isError: false, + }, + { + name: "TC_2", + input: "true", + expected: true, + isError: false, + }, + { + name: "TC_3", + input: "false", + expected: false, + isError: false, + }, + { + name: "TC_4", + input: "True", + expected: true, + isError: false, + }, + { + name: "TC_5", + input: "False", + expected: false, + isError: false, + }, + { + name: "TC_6", + input: "No", + expected: false, + isError: true, + }, + { + name: "TC_7", + input: "Pause", + expected: false, + isError: true, + }, + } + for _, test := range tt { + tc := test + t.Run(tc.name, func(t *testing.T) { + toBool, err := utils.ParseStringToBool(tc.input) + assert.Equal(t, tc.expected, toBool) + if tc.isError { + assert.Error(t, err) + } + }) + } +}