Skip to content

Commit 92c62b1

Browse files
committed
test liquid params
1 parent 9012cfa commit 92c62b1

File tree

1 file changed

+273
-31
lines changed

1 file changed

+273
-31
lines changed

tests/interchain/delegator/liquid_test.go

Lines changed: 273 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package delegator_test
22

33
import (
4+
"encoding/json"
45
"fmt"
6+
"strconv"
57
"testing"
68
"time"
79

@@ -11,7 +13,8 @@ import (
1113
"github.com/strangelove-ventures/interchaintest/v8/ibc"
1214
"github.com/strangelove-ventures/interchaintest/v8/testutil"
1315
"github.com/stretchr/testify/suite"
14-
"golang.org/x/mod/semver"
16+
"github.com/tidwall/gjson"
17+
"github.com/tidwall/sjson"
1518
"golang.org/x/sync/errgroup"
1619
)
1720

@@ -25,12 +28,19 @@ const (
2528

2629
type LSMSuite struct {
2730
*chainsuite.Suite
28-
Stride *chainsuite.Chain
29-
ICAAddr string
31+
LinkedChain *chainsuite.Chain
3032
LSMWallets map[string]ibc.Wallet
3133
ShareFactor sdkmath.Int
3234
}
3335

36+
type ProposalJSON struct {
37+
Messages []json.RawMessage `json:"messages"`
38+
InitialDeposit string `json:"deposit"`
39+
Title string `json:"title"`
40+
Summary string `json:"summary"`
41+
Metadata string `json:"metadata"`
42+
}
43+
3444
func (s *LSMSuite) checkAMinusBEqualsX(a, b string, x sdkmath.Int) {
3545
intA, err := chainsuite.StrToSDKInt(a)
3646
s.Require().NoError(err)
@@ -48,8 +58,7 @@ func (s *LSMSuite) TestLSMHappyPath() {
4858
liquid1Redeem = 20000000
4959
)
5060
providerWallet := s.Chain.ValidatorWallets[0]
51-
52-
strideWallet := s.Stride.ValidatorWallets[0]
61+
ibcWallet := s.LinkedChain.ValidatorWallets[0]
5362

5463
var tokenizedDenom string
5564
s.Run("Tokenize", func() {
@@ -121,9 +130,9 @@ func (s *LSMSuite) TestLSMHappyPath() {
121130
var happyLiquid1Delegations1 string
122131
var ibcDenom string
123132

124-
ibcChannelProvider, err := s.Relayer.GetTransferChannel(s.GetContext(), s.Chain, s.Stride)
133+
ibcChannelProvider, err := s.Relayer.GetTransferChannel(s.GetContext(), s.Chain, s.LinkedChain)
125134
s.Require().NoError(err)
126-
ibcChannelStride, err := s.Relayer.GetTransferChannel(s.GetContext(), s.Stride, s.Chain)
135+
ibcChannel, err := s.Relayer.GetTransferChannel(s.GetContext(), s.LinkedChain, s.Chain)
127136
s.Require().NoError(err)
128137

129138
s.Run("Transfer Tokens", func() {
@@ -141,11 +150,11 @@ func (s *LSMSuite) TestLSMHappyPath() {
141150
_, err = s.Chain.SendIBCTransfer(s.GetContext(), ibcChannelProvider.ChannelID, s.LSMWallets[lsmLiquid1Moniker].FormattedAddress(), ibc.WalletAmount{
142151
Amount: sdkmath.NewInt(ibcTransfer),
143152
Denom: tokenizedDenom,
144-
Address: strideWallet.Address,
153+
Address: ibcWallet.Address,
145154
}, ibc.TransferOptions{})
146155
s.Require().NoError(err)
147-
s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 5, s.Stride))
148-
balances, err := s.Stride.BankQueryAllBalances(s.GetContext(), strideWallet.Address)
156+
s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 5, s.LinkedChain))
157+
balances, err := s.LinkedChain.BankQueryAllBalances(s.GetContext(), ibcWallet.Address)
149158
s.Require().NoError(err)
150159
for _, balance := range balances {
151160
if balance.Amount.Int64() == ibcTransfer {
@@ -167,7 +176,7 @@ func (s *LSMSuite) TestLSMHappyPath() {
167176
"--gas", "auto")
168177
s.Require().NoError(err)
169178

170-
_, err = s.Stride.SendIBCTransfer(s.GetContext(), ibcChannelStride.ChannelID, strideWallet.Address, ibc.WalletAmount{
179+
_, err = s.LinkedChain.SendIBCTransfer(s.GetContext(), ibcChannel.ChannelID, ibcWallet.Address, ibc.WalletAmount{
171180
Amount: sdkmath.NewInt(ibcTransfer),
172181
Denom: ibcDenom,
173182
Address: s.LSMWallets[lsmLiquid3Moniker].FormattedAddress(),
@@ -272,6 +281,252 @@ func (s *LSMSuite) TestTokenizeVested() {
272281
s.checkAMinusBEqualsX(sharesPostTokenize, sharesPreTokenize, sdkmath.NewInt(tokenizeAmount).Mul(s.ShareFactor))
273282
}
274283

284+
func (s *LSMSuite) TestLSMParams() {
285+
const (
286+
delegation = 100000000
287+
globalCap = 0.1
288+
validatorCap = 0.05
289+
)
290+
291+
providerWallet := s.Chain.ValidatorWallets[0]
292+
293+
liquidParams, err := s.Chain.QueryJSON(s.GetContext(), "params", "liquid", "params")
294+
s.Require().NoError(err)
295+
296+
startingValidatorCap := gjson.Get(liquidParams.String(), "validator_liquid_staking_cap").String()
297+
startingGlobalCap := gjson.Get(liquidParams.String(), "global_liquid_staking_cap").String()
298+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("Starting validator liquid staking cap: %s", startingValidatorCap)
299+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("Starting global liquid staking cap: %s", startingGlobalCap)
300+
301+
s.Run("Update params", func() {
302+
303+
authority, err := s.Chain.GetGovernanceAddress(s.GetContext())
304+
s.Require().NoError(err)
305+
306+
updatedParams, err := sjson.Set(liquidParams.String(), "global_liquid_staking_cap", fmt.Sprintf("%f", globalCap))
307+
s.Require().NoError(err)
308+
updatedParams, err = sjson.Set(updatedParams, "validator_liquid_staking_cap", fmt.Sprintf("%f", validatorCap))
309+
s.Require().NoError(err)
310+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("Updated params: %s", updatedParams)
311+
312+
paramChangeMessage := fmt.Sprintf(`{
313+
"@type": "/gaia.liquid.v1beta1.MsgUpdateParams",
314+
"authority": "%s",
315+
"params": %s
316+
}`, authority, updatedParams)
317+
318+
proposal := ProposalJSON{
319+
Messages: []json.RawMessage{json.RawMessage(paramChangeMessage)},
320+
InitialDeposit: "5000000uatom",
321+
Title: "Liquid Param Change Proposal",
322+
Summary: "Test Proposal",
323+
Metadata: "ipfs://CID",
324+
}
325+
326+
// Marshal to JSON
327+
proposalBytes, err := json.MarshalIndent(proposal, "", " ")
328+
s.Require().NoError(err)
329+
330+
// Get the home directory of the node
331+
homeDir := s.Chain.GetNode().HomeDir()
332+
proposalPath := homeDir + "/params-proposal.json"
333+
// Write to file
334+
err = s.Chain.GetNode().WriteFile(s.GetContext(), proposalBytes, "params-proposal.json")
335+
s.Require().NoError(err)
336+
337+
// out, _, _ := s.Chain.GetNode().Exec(s.GetContext(), []string{"ls", "-l", string(homeDir)}, []string{homeDir})
338+
// fmt.Println("Files in home dir:", string(out))
339+
_, err = s.Chain.GetNode().ExecTx(s.GetContext(), providerWallet.Address,
340+
"gov", "submit-proposal", proposalPath)
341+
s.Require().NoError(err)
342+
343+
lastProposalId, err := s.Chain.QueryJSON(s.GetContext(), "proposals.@reverse.0.id", "gov", "proposals")
344+
s.Require().NoError(err)
345+
346+
// Pass proposal
347+
_, err = s.Chain.GetNode().ExecTx(s.GetContext(), providerWallet.Address,
348+
"gov", "vote", lastProposalId.String(), "yes")
349+
s.Require().NoError(err)
350+
time.Sleep(80 * time.Second)
351+
352+
// Check proposal
353+
proposalStatus, err := s.Chain.QueryJSON(s.GetContext(), "proposal", "gov", "proposal", lastProposalId.String())
354+
s.Require().NoError(err)
355+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("Proposal status: %s", proposalStatus)
356+
// Check params
357+
liquidParams, err = s.Chain.QueryJSON(s.GetContext(), "params", "liquid", "params")
358+
s.Require().NoError(err)
359+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("Current params: %s", liquidParams)
360+
validatorLiquidCapParam := gjson.Get(liquidParams.String(), "validator_liquid_staking_cap")
361+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("Validator liquid cap: %s", validatorLiquidCapParam)
362+
globalLiquidCapParam := gjson.Get(liquidParams.String(), "global_liquid_staking_cap")
363+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("Global liquid cap: %s", globalLiquidCapParam)
364+
validatorLiquidCapFloat := validatorLiquidCapParam.Float()
365+
globalLiquidCapFloat := globalLiquidCapParam.Float()
366+
s.Require().Equal(validatorCap, validatorLiquidCapFloat)
367+
s.Require().Equal(globalCap, globalLiquidCapFloat)
368+
369+
})
370+
371+
s.Run("Test liquid caps", func() {
372+
_, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Address, "staking", "delegate", s.Chain.ValidatorWallets[1].ValoperAddress, fmt.Sprintf("%d%s", delegation, chainsuite.Uatom))
373+
s.Require().NoError(err)
374+
_, err = s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Address, "staking", "delegate", s.Chain.ValidatorWallets[2].ValoperAddress, fmt.Sprintf("%d%s", delegation, chainsuite.Uatom))
375+
s.Require().NoError(err)
376+
377+
liquidParams, err := s.Chain.QueryJSON(s.GetContext(), "params", "liquid", "params")
378+
s.Require().NoError(err)
379+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("Starting params: %s", liquidParams)
380+
validatorLiquidCapParam := gjson.Get(liquidParams.String(), "validator_liquid_staking_cap")
381+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("Validator liquid cap: %s", validatorLiquidCapParam)
382+
globalLiquidCapParam := gjson.Get(liquidParams.String(), "global_liquid_staking_cap")
383+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("Global liquid cap: %s", globalLiquidCapParam)
384+
385+
val0Tokens, err := s.Chain.QueryJSON(s.GetContext(), "validator.tokens", "staking", "validator", s.Chain.ValidatorWallets[0].ValoperAddress)
386+
s.Require().NoError(err)
387+
val1Tokens, err := s.Chain.QueryJSON(s.GetContext(), "validator.tokens", "staking", "validator", s.Chain.ValidatorWallets[1].ValoperAddress)
388+
s.Require().NoError(err)
389+
val2Tokens, err := s.Chain.QueryJSON(s.GetContext(), "validator.tokens", "staking", "validator", s.Chain.ValidatorWallets[2].ValoperAddress)
390+
s.Require().NoError(err)
391+
totalBondedTokens, err := s.Chain.QueryJSON(s.GetContext(), "pool.bonded_tokens", "staking", "pool")
392+
s.Require().NoError(err)
393+
totalLiquidStaked, err := s.Chain.QueryJSON(s.GetContext(), "tokens", "liquid", "total-liquid-staked")
394+
s.Require().NoError(err)
395+
val0TokensFloat := val0Tokens.Float()
396+
val1TokensFloat := val1Tokens.Float()
397+
val2TokensFloat := val2Tokens.Float()
398+
totalBondedTokensFloat := totalBondedTokens.Float()
399+
totalLiquidStakedFloat := totalLiquidStaked.Float()
400+
401+
validatorCap := validatorLiquidCapParam.Float()
402+
globalCap := globalLiquidCapParam.Float()
403+
404+
val0Cap := validatorCap * val0TokensFloat
405+
val1Cap := validatorCap * val1TokensFloat
406+
val2Cap := validatorCap * val2TokensFloat
407+
globalCapShares := globalCap * totalBondedTokensFloat
408+
409+
val0LiquidShares, err := s.Chain.QueryJSON(s.GetContext(), "liquid_validator.liquid_shares", "liquid", "liquid-validator", s.Chain.ValidatorWallets[0].ValoperAddress)
410+
s.Require().NoError(err)
411+
val1LiquidShares, err := s.Chain.QueryJSON(s.GetContext(), "liquid_validator.liquid_shares", "liquid", "liquid-validator", s.Chain.ValidatorWallets[1].ValoperAddress)
412+
s.Require().NoError(err)
413+
val2LiquidShares, err := s.Chain.QueryJSON(s.GetContext(), "liquid_validator.liquid_shares", "liquid", "liquid-validator", s.Chain.ValidatorWallets[2].ValoperAddress)
414+
s.Require().NoError(err)
415+
val0LiquidSharesFloat := val0LiquidShares.Float()
416+
val1LiquidSharesFloat := val1LiquidShares.Float()
417+
val2LiquidSharesFloat := val2LiquidShares.Float()
418+
419+
val0CapAvailable := val0Cap - val0LiquidSharesFloat
420+
val1CapAvailable := val1Cap - val1LiquidSharesFloat
421+
val2CapAvailable := val2Cap - val2LiquidSharesFloat
422+
globalCapSharesAvailable := globalCapShares - totalLiquidStakedFloat
423+
424+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("val0 cap: %f, available shares: %f", val0Cap, val0CapAvailable)
425+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("val1 cap: %f, available shares: %f", val1Cap, val1CapAvailable)
426+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("Val2 cap: %f, available shares: %f", val2Cap, val2CapAvailable)
427+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("Global cap: %f, available shares: %f", globalCapShares, globalCapSharesAvailable)
428+
429+
// Validator 1 must have a lower cap than the global amount
430+
s.Require().Less(val1CapAvailable, globalCapSharesAvailable)
431+
432+
// Try to tokenize more than the global cap
433+
globalFailAmount := int(globalCapSharesAvailable + 1000000)
434+
_, err = s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Address, "liquid", "tokenize-share",
435+
s.Chain.ValidatorWallets[1].ValoperAddress, fmt.Sprintf("%d%s", globalFailAmount, s.Chain.Config().Denom), s.Chain.ValidatorWallets[0].Address)
436+
s.Require().Error(err)
437+
438+
// Try to tokenize more than the validator cap
439+
validatorFailAmount := int(val1CapAvailable + 1000000)
440+
_, err = s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Address, "liquid", "tokenize-share",
441+
s.Chain.ValidatorWallets[1].ValoperAddress, fmt.Sprintf("%d%s", validatorFailAmount, s.Chain.Config().Denom), s.Chain.ValidatorWallets[0].Address)
442+
s.Require().Error(err)
443+
444+
// Tokenize less than the validator cap
445+
validatorSuccessAmount := int(val1CapAvailable - 1000000)
446+
_, err = s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Address, "liquid", "tokenize-share",
447+
s.Chain.ValidatorWallets[1].ValoperAddress, fmt.Sprintf("%d%s", validatorSuccessAmount, s.Chain.Config().Denom), s.Chain.ValidatorWallets[0].Address)
448+
s.Require().NoError(err)
449+
450+
tokenizedDenom, err := s.Chain.QueryJSON(s.GetContext(), "balances.@reverse.1.denom", "bank", "balances", s.Chain.ValidatorWallets[0].Address)
451+
s.Require().NoError(err)
452+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("Tokenized denom: %s", tokenizedDenom)
453+
// liquid_denom= $(./gaiad-v24 q bank balances $WALLET_1 --home $HOME_1 -o json | jq -r '.balances[-2].denom')
454+
// Redeem tokenized amount
455+
_, err = s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Address, "liquid", "redeem-tokens",
456+
fmt.Sprintf("%d%s", validatorSuccessAmount, tokenizedDenom))
457+
s.Require().NoError(err)
458+
459+
// Unbond
460+
_, err = s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Address, "staking", "unbond", s.Chain.ValidatorWallets[1].ValoperAddress, fmt.Sprintf("%d%s", delegation, chainsuite.Uatom))
461+
s.Require().NoError(err)
462+
_, err = s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Address, "staking", "unbond", s.Chain.ValidatorWallets[2].ValoperAddress, fmt.Sprintf("%d%s", delegation, chainsuite.Uatom))
463+
s.Require().NoError(err)
464+
465+
})
466+
467+
s.Run("Restore params", func() {
468+
authority, err := s.Chain.GetGovernanceAddress(s.GetContext())
469+
s.Require().NoError(err)
470+
471+
updatedParams, err := sjson.Set(liquidParams.String(), "global_liquid_staking_cap", startingGlobalCap)
472+
s.Require().NoError(err)
473+
updatedParams, err = sjson.Set(updatedParams, "validator_liquid_staking_cap", startingValidatorCap)
474+
s.Require().NoError(err)
475+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("Updated params: %s", updatedParams)
476+
477+
paramChangeMessage := fmt.Sprintf(`{
478+
"@type": "/gaia.liquid.v1beta1.MsgUpdateParams",
479+
"authority": "%s",
480+
"params": %s
481+
}`, authority, updatedParams)
482+
483+
proposal := ProposalJSON{
484+
Messages: []json.RawMessage{json.RawMessage(paramChangeMessage)},
485+
InitialDeposit: "5000000uatom",
486+
Title: "Liquid Param Restore Proposal",
487+
Summary: "Test Proposal",
488+
Metadata: "ipfs://CID",
489+
}
490+
491+
// Marshal to JSON
492+
proposalBytes, err := json.MarshalIndent(proposal, "", " ")
493+
s.Require().NoError(err)
494+
495+
// Get the home directory of the node
496+
homeDir := s.Chain.GetNode().HomeDir()
497+
proposalPath := homeDir + "/restore-params-proposal.json"
498+
// Write to file
499+
err = s.Chain.GetNode().WriteFile(s.GetContext(), proposalBytes, "restore-params-proposal.json")
500+
s.Require().NoError(err)
501+
502+
_, err = s.Chain.GetNode().ExecTx(s.GetContext(), providerWallet.Address,
503+
"gov", "submit-proposal", proposalPath)
504+
s.Require().NoError(err)
505+
506+
lastProposalId, err := s.Chain.QueryJSON(s.GetContext(), "proposals.@reverse.0.id", "gov", "proposals")
507+
s.Require().NoError(err)
508+
chainsuite.GetLogger(s.GetContext()).Sugar().Infof("Last Proposal ID: %s", lastProposalId)
509+
510+
// Pass proposal
511+
_, err = s.Chain.GetNode().ExecTx(s.GetContext(), providerWallet.Address,
512+
"gov", "vote", lastProposalId.String(), "yes")
513+
s.Require().NoError(err)
514+
time.Sleep(80 * time.Second)
515+
516+
// Check params
517+
liquidParams, err = s.Chain.QueryJSON(s.GetContext(), "params", "liquid", "params")
518+
s.Require().NoError(err)
519+
validatorLiquidCapParam := gjson.Get(liquidParams.String(), "validator_liquid_staking_cap").Float()
520+
globalLiquidCapParam := gjson.Get(liquidParams.String(), "global_liquid_staking_cap").Float()
521+
startingValidatorCapFloat, err := strconv.ParseFloat(startingValidatorCap, 64)
522+
s.Require().NoError(err)
523+
startingGlobalCapFloat, err := strconv.ParseFloat(startingGlobalCap, 64)
524+
s.Require().NoError(err)
525+
s.Require().Equal(startingValidatorCapFloat, validatorLiquidCapParam)
526+
s.Require().Equal(startingGlobalCapFloat, globalLiquidCapParam)
527+
})
528+
}
529+
275530
func (s *LSMSuite) setupLSMWallets() {
276531
names := []string{lsmBondingMoniker, lsmLiquid1Moniker, lsmLiquid2Moniker, lsmLiquid3Moniker, lsmOwnerMoniker}
277532
wallets := make(map[string]ibc.Wallet)
@@ -299,38 +554,25 @@ func (s *LSMSuite) setupLSMWallets() {
299554

300555
func (s *LSMSuite) SetupSuite() {
301556
s.Suite.SetupSuite()
302-
// This is slightly broken while stride is still in the process of being upgraded, so skip if
303-
// going from v21 -> v21
304-
if semver.Major(s.Env.OldGaiaImageVersion) == s.Env.UpgradeName && s.Env.UpgradeName == "v21" {
305-
s.T().Skip("Skipping LSM when going from v21 -> v21")
306-
}
307-
stride, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, chainsuite.ConsumerConfig{
308-
ChainName: "stride",
309-
Version: chainsuite.StrideVersion,
310-
Denom: chainsuite.StrideDenom,
311-
TopN: 100,
312-
ShouldCopyProviderKey: []bool{true},
313-
})
314-
s.Require().NoError(err)
315-
s.Stride = stride
316-
err = s.Chain.CheckCCV(s.GetContext(), stride, s.Relayer, 1_000_000, 0, 1)
557+
secondChain, err := s.Chain.AddLinkedChain(s.GetContext(), s.T(), s.Relayer, chainsuite.DefaultChainSpec(s.Env))
317558
s.Require().NoError(err)
559+
s.LinkedChain = secondChain
318560

319-
icaAddr, err := stride.SetupICAAccount(s.GetContext(), s.Chain, s.Relayer, stride.ValidatorWallets[0].Address, 0, 1_000_000_000)
320-
s.Require().NoError(err)
321-
s.ICAAddr = icaAddr
322561
shareFactor, ok := sdkmath.NewIntFromString("1000000000000000000")
323562
s.Require().True(ok)
324563
s.ShareFactor = shareFactor
325564

326565
s.setupLSMWallets()
327-
s.UpgradeChain()
328566
}
329567

330568
func TestLSM(t *testing.T) {
331569
s := &LSMSuite{
332570
Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{
333-
CreateRelayer: true,
571+
CreateRelayer: true,
572+
UpgradeOnSetup: true,
573+
ChainSpec: &interchaintest.ChainSpec{
574+
NumValidators: &chainsuite.SixValidators,
575+
},
334576
}),
335577
}
336578
suite.Run(t, s)

0 commit comments

Comments
 (0)