|
| 1 | +package gov_test |
| 2 | + |
| 3 | +import ( |
| 4 | + "testing" |
| 5 | + |
| 6 | + "github.com/cometbft/cometbft/types" |
| 7 | + sdk "github.com/cosmos/cosmos-sdk/types" |
| 8 | + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" |
| 9 | + "github.com/dydxprotocol/v4-chain/protocol/lib" |
| 10 | + testapp "github.com/dydxprotocol/v4-chain/protocol/testutil/app" |
| 11 | + "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" |
| 12 | + pricestest "github.com/dydxprotocol/v4-chain/protocol/testutil/prices" |
| 13 | + clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" |
| 14 | + perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" |
| 15 | + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" |
| 16 | + "github.com/stretchr/testify/require" |
| 17 | +) |
| 18 | + |
| 19 | +var ( |
| 20 | + GENESIS_MARKET_PARAM = pricestypes.MarketParam{ |
| 21 | + Id: 0, |
| 22 | + Pair: "btc-adv4tnt", |
| 23 | + Exponent: -8, |
| 24 | + MinExchanges: 2, |
| 25 | + MinPriceChangePpm: 1_000, |
| 26 | + ExchangeConfigJson: "{}", |
| 27 | + } |
| 28 | + |
| 29 | + MODIFIED_MARKET_PARAM = pricestypes.MarketParam{ |
| 30 | + Id: GENESIS_MARKET_PARAM.Id, |
| 31 | + Pair: "eth-adv4tnt", |
| 32 | + Exponent: GENESIS_MARKET_PARAM.Exponent, // exponent cannot be updated |
| 33 | + MinExchanges: 3, |
| 34 | + MinPriceChangePpm: 2_002, |
| 35 | + ExchangeConfigJson: `{"exchanges":[{"exchangeName":"Bitfinex","ticker":"tBTCUSD"}]}`, |
| 36 | + } |
| 37 | +) |
| 38 | + |
| 39 | +// This tests `MsgUpdateMarketParam` in `x/prices`. |
| 40 | +func TestUpdateMarketParam(t *testing.T) { |
| 41 | + tests := map[string]struct { |
| 42 | + msg *pricestypes.MsgUpdateMarketParam |
| 43 | + expectCheckTxFails bool |
| 44 | + expectSubmitProposalFails bool |
| 45 | + expectedProposalStatus govtypesv1.ProposalStatus |
| 46 | + }{ |
| 47 | + "Success": { |
| 48 | + msg: &pricestypes.MsgUpdateMarketParam{ |
| 49 | + Authority: lib.GovModuleAddress.String(), |
| 50 | + MarketParam: MODIFIED_MARKET_PARAM, |
| 51 | + }, |
| 52 | + expectedProposalStatus: govtypesv1.ProposalStatus_PROPOSAL_STATUS_PASSED, |
| 53 | + }, |
| 54 | + "Failure: market param does not exist": { |
| 55 | + msg: &pricestypes.MsgUpdateMarketParam{ |
| 56 | + Authority: lib.GovModuleAddress.String(), |
| 57 | + MarketParam: pricestypes.MarketParam{ |
| 58 | + Id: MODIFIED_MARKET_PARAM.Id + 1, // id does not exist |
| 59 | + Pair: MODIFIED_MARKET_PARAM.Pair, |
| 60 | + Exponent: MODIFIED_MARKET_PARAM.Exponent, |
| 61 | + MinExchanges: MODIFIED_MARKET_PARAM.MinExchanges, |
| 62 | + MinPriceChangePpm: MODIFIED_MARKET_PARAM.MinPriceChangePpm, |
| 63 | + ExchangeConfigJson: MODIFIED_MARKET_PARAM.ExchangeConfigJson, |
| 64 | + }, |
| 65 | + }, |
| 66 | + expectedProposalStatus: govtypesv1.ProposalStatus_PROPOSAL_STATUS_FAILED, |
| 67 | + }, |
| 68 | + "Failure: exponent is updated": { |
| 69 | + msg: &pricestypes.MsgUpdateMarketParam{ |
| 70 | + Authority: lib.GovModuleAddress.String(), |
| 71 | + MarketParam: pricestypes.MarketParam{ |
| 72 | + Id: MODIFIED_MARKET_PARAM.Id, |
| 73 | + Pair: MODIFIED_MARKET_PARAM.Pair, |
| 74 | + Exponent: MODIFIED_MARKET_PARAM.Exponent + 1, // update to exponent is not permitted. |
| 75 | + MinExchanges: MODIFIED_MARKET_PARAM.MinExchanges, |
| 76 | + MinPriceChangePpm: MODIFIED_MARKET_PARAM.MinPriceChangePpm, |
| 77 | + ExchangeConfigJson: MODIFIED_MARKET_PARAM.ExchangeConfigJson, |
| 78 | + }, |
| 79 | + }, |
| 80 | + expectedProposalStatus: govtypesv1.ProposalStatus_PROPOSAL_STATUS_FAILED, |
| 81 | + }, |
| 82 | + "Failure: empty pair": { |
| 83 | + msg: &pricestypes.MsgUpdateMarketParam{ |
| 84 | + Authority: lib.GovModuleAddress.String(), |
| 85 | + MarketParam: pricestypes.MarketParam{ |
| 86 | + Id: MODIFIED_MARKET_PARAM.Id, |
| 87 | + Pair: "", // invalid |
| 88 | + Exponent: MODIFIED_MARKET_PARAM.Exponent, |
| 89 | + MinExchanges: MODIFIED_MARKET_PARAM.MinExchanges, |
| 90 | + MinPriceChangePpm: MODIFIED_MARKET_PARAM.MinPriceChangePpm, |
| 91 | + ExchangeConfigJson: MODIFIED_MARKET_PARAM.ExchangeConfigJson, |
| 92 | + }, |
| 93 | + }, |
| 94 | + expectCheckTxFails: true, |
| 95 | + }, |
| 96 | + "Failure: min exchanges is 0": { |
| 97 | + msg: &pricestypes.MsgUpdateMarketParam{ |
| 98 | + Authority: lib.GovModuleAddress.String(), |
| 99 | + MarketParam: pricestypes.MarketParam{ |
| 100 | + Id: MODIFIED_MARKET_PARAM.Id, |
| 101 | + Pair: MODIFIED_MARKET_PARAM.Pair, |
| 102 | + Exponent: MODIFIED_MARKET_PARAM.Exponent, |
| 103 | + MinExchanges: 0, // invalid |
| 104 | + MinPriceChangePpm: MODIFIED_MARKET_PARAM.MinPriceChangePpm, |
| 105 | + ExchangeConfigJson: MODIFIED_MARKET_PARAM.ExchangeConfigJson, |
| 106 | + }, |
| 107 | + }, |
| 108 | + expectCheckTxFails: true, |
| 109 | + }, |
| 110 | + "Failure: malformed exchange config json": { |
| 111 | + msg: &pricestypes.MsgUpdateMarketParam{ |
| 112 | + Authority: lib.GovModuleAddress.String(), |
| 113 | + MarketParam: pricestypes.MarketParam{ |
| 114 | + Id: MODIFIED_MARKET_PARAM.Id, |
| 115 | + Pair: MODIFIED_MARKET_PARAM.Pair, |
| 116 | + Exponent: MODIFIED_MARKET_PARAM.Exponent, |
| 117 | + MinExchanges: MODIFIED_MARKET_PARAM.MinExchanges, |
| 118 | + MinPriceChangePpm: MODIFIED_MARKET_PARAM.MinPriceChangePpm, |
| 119 | + ExchangeConfigJson: `{{"exchanges":[{"exchangeName":"Bitfinex","ticker":"tBTCUSD"}]}`, // invalid |
| 120 | + }, |
| 121 | + }, |
| 122 | + expectCheckTxFails: true, |
| 123 | + }, |
| 124 | + "Failure: invalid authority": { |
| 125 | + msg: &pricestypes.MsgUpdateMarketParam{ |
| 126 | + Authority: constants.AliceAccAddress.String(), |
| 127 | + MarketParam: MODIFIED_MARKET_PARAM, |
| 128 | + }, |
| 129 | + expectSubmitProposalFails: true, |
| 130 | + }, |
| 131 | + } |
| 132 | + |
| 133 | + for name, tc := range tests { |
| 134 | + t.Run(name, func(t *testing.T) { |
| 135 | + tApp := testapp.NewTestAppBuilder(t).WithGenesisDocFn(func() (genesis types.GenesisDoc) { |
| 136 | + genesis = testapp.DefaultGenesis() |
| 137 | + testapp.UpdateGenesisDocWithAppStateForModule( |
| 138 | + &genesis, |
| 139 | + func(genesisState *govtypesv1.GenesisState) { |
| 140 | + genesisState.Params.VotingPeriod = &testapp.TestVotingPeriod |
| 141 | + }, |
| 142 | + ) |
| 143 | + // Initialize prices module with genesis market param. |
| 144 | + testapp.UpdateGenesisDocWithAppStateForModule( |
| 145 | + &genesis, |
| 146 | + func(genesisState *pricestypes.GenesisState) { |
| 147 | + marketParamPrice := pricestest.GenerateMarketParamPrice( |
| 148 | + pricestest.WithId(GENESIS_MARKET_PARAM.Id), |
| 149 | + pricestest.WithPair(GENESIS_MARKET_PARAM.Pair), |
| 150 | + pricestest.WithExponent(GENESIS_MARKET_PARAM.Exponent), |
| 151 | + pricestest.WithMinExchanges(GENESIS_MARKET_PARAM.MinExchanges), |
| 152 | + pricestest.WithMinPriceChangePpm(GENESIS_MARKET_PARAM.MinPriceChangePpm), |
| 153 | + pricestest.WithExchangeConfigJson(GENESIS_MARKET_PARAM.ExchangeConfigJson), |
| 154 | + ) |
| 155 | + genesisState.MarketParams = []pricestypes.MarketParam{marketParamPrice.Param} |
| 156 | + genesisState.MarketPrices = []pricestypes.MarketPrice{marketParamPrice.Price} |
| 157 | + }, |
| 158 | + ) |
| 159 | + // Initialize perpetuals module with no perpetuals. |
| 160 | + testapp.UpdateGenesisDocWithAppStateForModule( |
| 161 | + &genesis, |
| 162 | + func(genesisState *perptypes.GenesisState) { |
| 163 | + genesisState.Perpetuals = []perptypes.Perpetual{} |
| 164 | + }, |
| 165 | + ) |
| 166 | + // Initialize clob module with no clob pairs. |
| 167 | + testapp.UpdateGenesisDocWithAppStateForModule( |
| 168 | + &genesis, |
| 169 | + func(genesisState *clobtypes.GenesisState) { |
| 170 | + genesisState.ClobPairs = []clobtypes.ClobPair{} |
| 171 | + }, |
| 172 | + ) |
| 173 | + return genesis |
| 174 | + }).Build() |
| 175 | + ctx := tApp.InitChain() |
| 176 | + initialMarketParams := tApp.App.PricesKeeper.GetAllMarketParams(ctx) |
| 177 | + |
| 178 | + // Submit and tally governance proposal that includes `MsgUpdateMarketParam`. |
| 179 | + ctx = testapp.SubmitAndTallyProposal( |
| 180 | + t, |
| 181 | + ctx, |
| 182 | + tApp, |
| 183 | + []sdk.Msg{tc.msg}, |
| 184 | + tc.expectCheckTxFails, |
| 185 | + tc.expectSubmitProposalFails, |
| 186 | + tc.expectedProposalStatus, |
| 187 | + ) |
| 188 | + |
| 189 | + if tc.expectedProposalStatus == govtypesv1.ProposalStatus_PROPOSAL_STATUS_PASSED { |
| 190 | + // If proposal is supposed to pass, verify that maret param is updated. |
| 191 | + updatedMarketParam, exists := tApp.App.PricesKeeper.GetMarketParam(ctx, tc.msg.MarketParam.Id) |
| 192 | + require.True(t, exists) |
| 193 | + require.Equal(t, tc.msg.MarketParam, updatedMarketParam) |
| 194 | + } else { |
| 195 | + // Otherwise, verify that market params are unchanged. |
| 196 | + require.Equal(t, initialMarketParams, tApp.App.PricesKeeper.GetAllMarketParams(ctx)) |
| 197 | + } |
| 198 | + }) |
| 199 | + } |
| 200 | +} |
0 commit comments